Linkedin has updated the CSS property for companyLogo. Current property returning the default logo for all the companies. I have updated it to correct logo.
198 lines
6.0 KiB
JavaScript
198 lines
6.0 KiB
JavaScript
const cheerio = require("cheerio");
|
|
const axios = require("axios");
|
|
|
|
module.exports.query = (queryObject) => {
|
|
const query = new Query(queryObject);
|
|
console.log(query.url(0));
|
|
return query.getJobs();
|
|
};
|
|
|
|
//transfers object values passed to our .query to an obj we can access
|
|
function Query(queryObj) {
|
|
//query vars
|
|
this.host = queryObj.host || "www.linkedin.com";
|
|
|
|
//api handles strings with spaces by replacing the values with %20
|
|
this.keyword = queryObj.keyword?.trim().replace(" ", "+") || "";
|
|
this.location = queryObj.location?.trim().replace(" ", "+") || "";
|
|
this.dateSincePosted = queryObj.dateSincePosted || "";
|
|
this.jobType = queryObj.jobType || "";
|
|
this.remoteFilter = queryObj.remoteFilter || "";
|
|
this.salary = queryObj.salary || "";
|
|
this.experienceLevel = queryObj.experienceLevel || "";
|
|
this.sortBy = queryObj.sortBy || "";
|
|
//internal variable
|
|
this.limit = Number(queryObj.limit) || 0;
|
|
}
|
|
|
|
/*
|
|
*
|
|
*
|
|
* Following get Functions act as object literals so the query can be constructed with the correct parameters
|
|
*
|
|
*
|
|
*/
|
|
Query.prototype.getDateSincePosted = function () {
|
|
const dateRange = {
|
|
"past month": "r2592000",
|
|
"past week": "r604800",
|
|
"24hr": "r86400",
|
|
};
|
|
return dateRange[this.dateSincePosted.toLowerCase()] ?? "";
|
|
};
|
|
|
|
Query.prototype.getExperienceLevel = function () {
|
|
const experienceRange = {
|
|
internship: "1",
|
|
"entry level": "2",
|
|
associate: "3",
|
|
senior: "4",
|
|
director: "5",
|
|
executive: "6",
|
|
};
|
|
return experienceRange[this.experienceLevel.toLowerCase()] ?? "";
|
|
};
|
|
Query.prototype.getJobType = function () {
|
|
const jobTypeRange = {
|
|
"full time": "F",
|
|
"full-time": "F",
|
|
"part time": "P",
|
|
"part-time": "P",
|
|
contract: "C",
|
|
temporary: "T",
|
|
volunteer: "V",
|
|
internship: "I",
|
|
};
|
|
return jobTypeRange[this.jobType.toLowerCase()] ?? "";
|
|
};
|
|
Query.prototype.getRemoteFilter = function () {
|
|
const remoteFilterRange = {
|
|
"on-site": "1",
|
|
"on site": "1",
|
|
remote: "2",
|
|
hybrid: "3",
|
|
};
|
|
return remoteFilterRange[this.remoteFilter.toLowerCase()] ?? "";
|
|
};
|
|
Query.prototype.getSalary = function () {
|
|
const salaryRange = {
|
|
40000: "1",
|
|
60000: "2",
|
|
80000: "3",
|
|
100000: "4",
|
|
120000: "5",
|
|
};
|
|
return salaryRange[this.salary.toLowerCase()] ?? "";
|
|
};
|
|
|
|
/*
|
|
* EXAMPLE OF A SAMPLE QUERY
|
|
* https://www.linkedin.com/jobs/search/?f_E=2%2C3&f_JT=F%2CP&f_SB2=1&f_TPR=r2592000&f_WT=2%2C1&geoId=90000049&keywords=programmer&location=Los%20Angeles%20Metropolitan%20Area
|
|
* Date Posted (Single Pick) f_TPR
|
|
* Job Type (Multiple Picks) f_JT
|
|
* Experience Level(Multiple Picks) f_E
|
|
* On-Site/Remote (Multiple Picks) f_WT
|
|
* Salary (Single Pick) f_SB2
|
|
*
|
|
*/
|
|
Query.prototype.url = function (start) {
|
|
let query = `https://${this.host}/jobs-guest/jobs/api/seeMoreJobPostings/search?`;
|
|
if (this.keyword !== "") query += `keywords=${this.keyword}`;
|
|
if (this.location !== "") query += `&location=${this.location}`;
|
|
if (this.getDateSincePosted() !== "")
|
|
query += `&f_TPR=${this.getDateSincePosted()}`;
|
|
if (this.getSalary() !== "") query += `&f_SB2=${this.getSalary()}`;
|
|
if (this.getExperienceLevel() !== "")
|
|
query += `&f_E=${this.getExperienceLevel()}`;
|
|
if (this.getRemoteFilter() !== "") query += `&f_WT=${this.getRemoteFilter()}`;
|
|
if (this.getJobType() !== "") query += `&f_JT=${this.getJobType()}`;
|
|
query += `&start=${start}`;
|
|
if (this.sortBy == "recent" || this.sortBy == "relevant") {
|
|
let sortMethod = "R";
|
|
if (this.sortBy == "recent") sortMethod = "DD";
|
|
query += `&sortBy=${sortMethod}`;
|
|
}
|
|
return encodeURI(query);
|
|
};
|
|
|
|
Query.prototype.getJobs = async function () {
|
|
try {
|
|
let parsedJobs,
|
|
resultCount = 1,
|
|
start = 0,
|
|
jobLimit = this.limit,
|
|
allJobs = [];
|
|
|
|
while (resultCount > 0) {
|
|
//fetch our data using our url generator with
|
|
//the page to start on
|
|
const { data } = await axios.get(this.url(start));
|
|
|
|
//select data so we can check the number of jobs returned
|
|
const $ = cheerio.load(data);
|
|
const jobs = $("li");
|
|
//if result count ends up being 0 we will stop getting more jobs
|
|
resultCount = jobs.length;
|
|
console.log("I got ", jobs.length, " jobs");
|
|
|
|
//to get the job data as objects with the desired details
|
|
parsedJobs = parseJobList(data);
|
|
allJobs.push(...parsedJobs);
|
|
|
|
//increment by 25 bc thats how many jobs the AJAX request fetches at a time
|
|
start += 25;
|
|
|
|
//in order to limit how many jobs are returned
|
|
//this if statment will return our function value after looping and removing excess jobs
|
|
if (jobLimit != 0 && allJobs.length > jobLimit) {
|
|
while (allJobs.length != jobLimit) allJobs.pop();
|
|
return allJobs;
|
|
}
|
|
}
|
|
//console.log(allJobs)
|
|
return allJobs;
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
};
|
|
function parseJobList(jobData) {
|
|
const $ = cheerio.load(jobData);
|
|
const jobs = $("li");
|
|
|
|
const jobObjects = jobs
|
|
.map((index, element) => {
|
|
const job = $(element);
|
|
const position = job.find(".base-search-card__title").text().trim() || "";
|
|
const company =
|
|
job.find(".base-search-card__subtitle").text().trim() || "";
|
|
const location =
|
|
job.find(".job-search-card__location").text().trim() || "";
|
|
const date = job.find("time").attr("datetime") || "";
|
|
const salary =
|
|
job
|
|
.find(".job-search-card__salary-info")
|
|
.text()
|
|
.trim()
|
|
.replace(/\n/g, "")
|
|
.replaceAll(" ", "") || "";
|
|
const jobUrl = job.find(".base-card__full-link").attr("href") || "";
|
|
const companyLogo =
|
|
job.find(".artdeco-entity-image").attr("data-delayed-url") || "";
|
|
const agoTime =
|
|
job.find(".job-search-card__listdate").text().trim() || "";
|
|
return {
|
|
position: position,
|
|
company: company,
|
|
companyLogo: companyLogo,
|
|
location: location,
|
|
date: date,
|
|
agoTime: agoTime,
|
|
salary: salary,
|
|
jobUrl: jobUrl,
|
|
};
|
|
})
|
|
.get();
|
|
|
|
return jobObjects;
|
|
}
|