From 0f42003ce5277ad52d1dbbfd5ed0f767abcdc20f Mon Sep 17 00:00:00 2001 From: Vishwa Gaurav <81325730+VishwaGauravIn@users.noreply.github.com> Date: Wed, 29 Jun 2022 15:46:32 +0530 Subject: [PATCH] initial codebase --- index.js | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/index.js b/index.js index e69de29..435c294 100644 --- a/index.js +++ b/index.js @@ -0,0 +1,169 @@ +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.replace(' ', '+') || '' + this.location = queryObj.location.replace(' ', '+') || '' + this.dateSincePosted = queryObj.dateSincePosted || '' + this.jobType = queryObj.jobType || '' + this.remoteFilter = queryObj.remoteFilter || '' + this.salary = queryObj.salary || '' + this.experienceLevel = queryObj.experienceLevel || '' + + //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', + 'part time': 'P', + 'contract': 'C', + 'temporary': 'T', + 'volunteer': 'V' + } + 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}` + 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') || '' + return { + position: position, + company: company, + location: location, + date: date, + salary: salary, + jobUrl: jobUrl + } + }).get() + + return jobObjects +} \ No newline at end of file