You need to enable JavaScript to run this app. 使用Puppeteer实现Glassdoor求职页面分页采集的问题咨询
优惠活动
大模型
产品
解决方案
定价
更多

使用Puppeteer实现Glassdoor求职页面分页采集的问题咨询

实现方案

你可以通过两种方式实现分页采集,以下是可直接复用的改造逻辑:

方案1:URL拼接分页(最简单稳定)

Glassdoor的列表页URL末尾_IPx.htm中的x就是当前页码,你只要循环修改这个参数即可,步骤如下:

  1. 先把登录逻辑从scrapeListings中抽离,避免每次爬新页码重复登录触发反爬:
// 新增单独的登录函数
async function loginToGlassdoor(page) {
  try {
    await page.goto("https://www.glassdoor.co.uk/index.htm");
    await page.click(
      "#SiteNav > nav > div.d-lg-none.d-flex.align-items-center.justify-content-between.px-std.py-xsm.px-md-lg.py-md-std.LockedHomeHeaderStyles__bottomBorder.LockedHomeHeaderStyles__fullWidth > div.d-flex.justify-content-center.order-1.order-md-2.LockedHomeHeaderStyles__flexibleContainer > button",
      { delay: 200 }
    );
    await page.type("#userEmail", "*******", { delay: 200 });
    await page.type("#userPassword", "*******", { delay: 200 });
    await page.click(".mt-std.d-flex.flex-column.align-items-center", { delay: 200 });
    await page.waitForNavigation();
  } catch (err) {
    console.error("登录失败:", err);
  }
}
  1. 修改scrapeListings,支持传入页码参数爬对应页的列表:
async function scrapeListings(page, pageNum) {
  try {
    // 拼接对应页码的URL
    await page.goto(`https://www.glassdoor.co.uk/Job/london-internship-jobs-SRCH_IL.0,6_IC2671300_KO7,17_IP${pageNum}.htm`);
    await page.waitForSelector("[data-test='jobListing']", { timeout: 5000 }); // 等待列表加载完成
    const html = await page.content();
    const $ = cheerio.load(html);
    const listings = $("[data-test='jobListing']")
      .map((index, element) => {
        const titleElement = $(element).find(".css-l2wjgv.e1n63ojh0.jobLink");
        const timeElement = $(element).find("[data-test='job-age']");
        const companyName = $(titleElement).text();
        const url = "https://www.glassdoor.co.uk" + $(titleElement).attr("href");
        const datePosted = $(timeElement).text();
        return { companyName, url, datePosted };
      })
      .get();
    return listings; 
  } catch (err) {
    console.error(`爬取第${pageNum}页失败:`, err);
    return [];
  }
}
  1. 修改main函数,加分页循环,你可以自定义要爬的总页数,也可以判断当返回的列表为空时终止循环:
async function main() {
  await connectToMongoDb();
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  // 先执行一次登录
  await loginToGlassdoor(page);
  // 定义要爬的总页数,比如爬10页就设为10,想爬所有结果就设个大数值,等列表返回空自动终止
  const totalPage = 10;
  for (let currentPage = 1; currentPage <= totalPage; currentPage++) {
    console.log(`正在爬取第${currentPage}页`);
    const listings = await scrapeListings(page, currentPage);
    if (listings.length === 0) {
      console.log("没有更多结果,爬取终止");
      break;
    }
    // 爬当前页的职位详情
    await scrapeJobDescriptions(listings, page);
    // 翻页间隔2秒,避免触发反爬
    await sleep(2000);
  }
  await browser.close();
}

方案2:模拟点击下一页按钮(不需要提前知道总页数)

如果不想拼接URL,也可以找到页面底部的下一页按钮,每次爬完一页点击跳转,直到按钮不可点击为止:
你只需要在main函数里修改循环逻辑即可,示例如下:

async function main() {
  await connectToMongoDb();
  const browser = await puppeteer.launch({ headless: false });
  const page = await browser.newPage();
  await loginToGlassdoor(page);
  // 先跳转到第一页
  await page.goto("https://www.glassdoor.co.uk/Job/london-internship-jobs-SRCH_IL.0,6_IC2671300_KO7,17_IP1.htm");
  while (true) {
    // 爬当前页列表
    await page.waitForSelector("[data-test='jobListing']", { timeout: 5000 });
    const html = await page.content();
    const $ = cheerio.load(html);
    const listings = $("[data-test='jobListing']")
      .map((index, element) => {
        const titleElement = $(element).find(".css-l2wjgv.e1n63ojh0.jobLink");
        const timeElement = $(element).find("[data-test='job-age']");
        const companyName = $(titleElement).text();
        const url = "https://www.glassdoor.co.uk" + $(titleElement).attr("href");
        const datePosted = $(timeElement).text();
        return { companyName, url, datePosted };
      })
      .get();
    if (listings.length === 0) break;
    await scrapeJobDescriptions(listings, page);
    // 判断下一页按钮是否可点击,选择器不匹配可以自己F12抓取更新
    const nextBtnDisabled = await page.$eval("button[aria-label='下一页']", btn => btn.disabled).catch(() => true);
    if (nextBtnDisabled) {
      console.log("已到最后一页,爬取结束");
      break;
    }
    // 点击下一页
    await page.click("button[aria-label='下一页']", { delay: 200 });
    await page.waitForNavigation({ waitUntil: 'networkidle0' });
    await sleep(2000);
  }
  await browser.close();
}

注意事项

  • 原代码里scrapeListings中return listings之后的await page.waitForNavigation是无效代码,执行不到,已经在修改版里删掉了。
  • 可以适当加大翻页的延迟时间,避免触发Glassdoor的反爬机制导致IP被封。
  • 如果遇到元素选择器失效的情况,重新抓取页面元素更新选择器即可。

内容的提问来源于stack exchange,提问作者Christopher

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.15 08:06:03