import { ElementHandle, Page } from "puppeteer-core"; import { randomDelay } from "../utils/browser"; import { Job } from "../types/job"; import { createDateFromRelativeTime } from "../utils/parse-time"; export class BrowserService { private page: Page; constructor(page: Page) { this.page = page; } async getJobLinks(): Promise { const jobLinks = await this.page.evaluate(() => { const links = document.querySelectorAll( "a[href*='/viewjob/']" ) as NodeListOf; return Array.from(links).map((link) => link.href); }); return jobLinks; } async getJobData(jobLink: string): Promise { let jobContainer: ElementHandle | null = null; await this.page.goto(jobLink, { waitUntil: "domcontentloaded", timeout: 60000, }); // await this.page.waitForNetworkIdle({ idleTime: 3000, timeout: 0 }); try { jobContainer = await this.page.waitForSelector( "::-p-xpath(/html/body/div[1]/div[3]/div/div/div/div[1])", { timeout: 10000, } ); } catch (error) { console.log("No job listings found on the page"); return {} as Job; } if (jobContainer === null) { console.log("Job container not found"); return {} as Job; } const jobTitle = await jobContainer.waitForSelector("h2"); const jobTitleText = await jobTitle?.evaluate((el) => el.textContent?.trim() ); const company = await jobContainer.waitForSelector( "span.text-xl.font-semibold.text-gray-700.flex-none" ); const companyText = await company?.evaluate((el) => el.textContent?.trim()); const location = await jobContainer.waitForSelector( "::-p-xpath(/html/body/div[1]/div[3]/div/div/div/div[1]/div/div[2]/div[3]/span)" ); const locationText = await location?.evaluate((el) => el.textContent?.trim() ); const jobPostTime = await jobContainer.waitForSelector( "::-p-xpath(/html/body/div[1]/div[3]/div/div/div/div[1]/div/div[2]/div[1]/div/span)" ); const jobPostTimeText = await jobPostTime?.evaluate((el) => el.textContent?.trim() ); const time = jobPostTimeText?.split(" ")[1] ?? ""; const parsedDateTime = createDateFromRelativeTime(time); const description = await this.page.waitForSelector("article"); const descriptionText = await description?.evaluate((el) => el.textContent?.trim() ); const job: Job = { company: companyText || "", title: jobTitleText || "", location: locationText || "", description: descriptionText || "", criteria: {}, jobLink, companyLink: "", provider: 7, jobPostTime: parsedDateTime.toISOString(), status: 0, }; console.log({ job }); return job; } }