爬取数据利用node也行,你知道吗?

386 阅读3分钟

以前老觉得爬虫就是phython的专利,我想做的事情都会因为phython太难,自己又不想学而畏首畏尾,今天就来个node的爬虫测试看看。(爬取boss数据)

狠人话少,上干货!

1️⃣ 搜索boss前端数据

安装node我就不说了,但凡有点基础都知道。建立文件夹,执行初始化。

npm init -y
npm install --save puppeteer exceljs

建立index.js文件

import puppeteer from 'puppeteer';
import ExcelJS from 'exceljs';

const browser = await puppeteer.launch({
  headless: false,
  defaultViewport: {
    width: 0,
    height: 0
  }
});

const page = await browser.newPage();

await page.goto('https://www.zhipin.com/web/geek/job?query=前端&city=100010000');

await page.waitForSelector('.job-list-box');

const totalPage = await page.$eval('.options-pages a:nth-last-child(2)', e => {
  return parseInt(e.textContent);
});

const allJobs = [];
for (let i = 1; i <= totalPage; i++) {
  await page.goto('https://www.zhipin.com/web/geek/job?query=前端&city=100010000&page=' + i);

  await page.waitForSelector('.job-list-box');

  const jobs = await page.$eval('.job-list-box', el => {
    return [...el.querySelectorAll('.job-card-wrapper')].map(item => {
      return {
        job: {
          name: item.querySelector('.job-name').textContent,
          area: item.querySelector('.job-area').textContent,
          salary: item.querySelector('.salary').textContent
        },
        link: item.querySelector('a').href,
        company: {
          name: item.querySelector('.company-name').textContent,
        }
      };
    });
  });
  allJobs.push(...jobs);
}

for (let i = 0; i < allJobs.length; i++) {
  await page.goto(allJobs[i].link);

  try {
    await page.waitForSelector('.job-sec-text');

    const jd = await page.$eval('.job-sec-text', el => {
      return el.textContent;
    });
    allJobs[i].desc = jd;
  } catch (e) { }
}

// 创建 Excel 文件
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('爬取数据');

// 设置表头
worksheet.columns = [
  { header: '工作名称', key: 'job', width: 20 },
  { header: '链接', key: 'link', width: 15 },
  { header: '公司名称', key: 'company', width: 15 },
  { header: '描述', key: 'desc', width: 15 }
];

// 写入数据
allJobs.forEach(item => worksheet.addRow(item));

// 保存文件
await workbook.xlsx.writeFile('output.xlsx');
await browser.close();

成果如下

image.png

如果想要你的excel更好,你就利用js更细致的解析数据就好了。

import puppeteer from 'puppeteer';
import ExcelJS from 'exceljs';

const browser = await puppeteer.launch({
  headless: false,
  defaultViewport: {
    width: 0,
    height: 0
  }
});

const page = await browser.newPage();

await page.goto('https://www.zhipin.com/web/geek/job?query=前端&city=100010000');

await page.waitForSelector('.job-list-box');

const totalPage = await page.$eval('.options-pages a:nth-last-child(2)', e => {
  return parseInt(e.textContent);
});

const allJobs = [];
for (let i = 1; i <= totalPage; i++) {
  await page.waitForTimeout(2000);
  await page.goto('https://www.zhipin.com/web/geek/job?query=前端&city=100010000&page=' + i);

  await page.waitForSelector('.job-list-box');

  const jobs = await page.$eval('.job-list-box', el => {
    return [...el.querySelectorAll('.job-card-wrapper')].map(item => {
      return {
        job: {
          name: item.querySelector('.job-name').textContent,
          area: item.querySelector('.job-area').textContent,
          salary: item.querySelector('.salary').textContent
        },
        link: item.querySelector('a').href,
        company: {
          name: item.querySelector('.company-name').textContent,
        }
      };
    });
  });
  allJobs.push(...jobs);
}

for (let i = 0; i < allJobs.length; i++) {
  const job = allJobs[i]?.job;
  allJobs.name = job?.name;
  allJobs.area = job?.area;
  allJobs.salary = job?.salary;
  allJobs.company = job?.company?.name;

  await page.goto(allJobs[i].link);

  try {
    await page.waitForSelector('.job-sec-text');

    const jd = await page.$eval('.job-sec-text', el => {
      return el.textContent;
    });
    allJobs[i].desc = jd;
  } catch (e) { }
}



// 创建 Excel 文件
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('爬取数据');

// 设置表头
worksheet.columns = [
  { header: '工作名称', key: 'name', width: 20 },
  { header: '工作地点', key: 'area', width: 20 },
  { header: '工资', key: 'salary', width: 20 },
  { header: '链接', key: 'link', width: 15 },
  { header: '公司名称', key: 'company', width: 15 },
  { header: '描述', key: 'desc', width: 15 }
];

// 写入数据
allJobs.forEach(item => worksheet.addRow(item));

// 保存文件
await workbook.xlsx.writeFile('output.xlsx');
await browser.close();


2️⃣ 搜索掘金首页数据

你以为有个虚拟滚动我就不会啦?小看我了吧,看我的:

image.png

上代码:

import puppeteer from 'puppeteer';
import ExcelJS from 'exceljs';

(async () => {
  // 启动浏览器实例
  const browser = await puppeteer.launch({
    headless: false, // 设置为 false 可以看到浏览器操作过程
    defaultViewport: {
      width: 1200,
      height: 800
    }
  });
  const page = await browser.newPage();

  // 导航到搜索页面
  await page.goto('https://juejin.cn/search?query=%E5%89%8D%E7%AB%AF&fromSeo=0&fromHistory=0&fromSuggest=0');

  // 等待目标元素加载完成
  await page.waitForSelector('.main-list');

  // 定义一个数组来存储抓取的数据
  let jobs = [];

  // 定义一个函数来抓取当前页面上的数据
  const scrapeJobs = async () => {
    const newJobs = await page.$eval('.main-list', el => {
      return [...el.querySelectorAll('.item')].map(item => {
        if (!item.querySelector('.title-row .title')?.innerText.trim()) {
          return;
        }

        return {
          title: item.querySelector('.title-row .title')?.innerText.trim() || '无标题',
          link: item.querySelector('a')?.href || '无链接',
          author: item.querySelector('.popover-box')?.innerText.trim() || '无作者',
          desc: item.querySelector('.abstract-row .abstract')?.innerText.trim() || '无描述',
          count: item.querySelector('.action-row .count')?.innerText.trim() || '0',
          // 可以根据需要添加更多字段
        };
      }).filter(_ => _);
    });
    jobs = jobs.concat(newJobs); // 将新抓取的数据添加到数组中
  };

  // 初次抓取数据
  await scrapeJobs();
  // 模拟滚动以加载更多数据
  let scrollCount = 0;
  while (jobs.length < 200) {
    // 滚动到页面底部
    await page.evaluate(() => {
      window.scrollBy(0, window.innerHeight);
    });

    // 等待新内容加载
    await new Promise(resolve => setTimeout(resolve, 2000)); // 等待 2 秒

    // 再次抓取数据
    await scrapeJobs();

    // 防止无限滚动,设置最大滚动次数
    scrollCount++;
    if (scrollCount > 20) { // 假设最多滚动 20 次
      break;
    }
  }

  // 输出抓取到的数据
  //console.log(jobs.slice(0, 200)); // 确保只输出 200 条数据
  // 创建 Excel 文件
  const workbook = new ExcelJS.Workbook();
  const worksheet = workbook.addWorksheet('爬取数据');

  // 设置表头
  worksheet.columns = [
    { header: '标题', key: 'title', width: 20 },
    { header: '链接', key: 'link', width: 15 },
    { header: '作者', key: 'author', width: 15 },
    { header: '描述', key: 'desc', width: 15 },
    { header: '点赞量', key: 'count', width: 15 }
  ];

  // 写入数据
  jobs.forEach(item => worksheet.addRow(item));

  // 保存文件
  await workbook.xlsx.writeFile('稀土掘金点赞.xlsx');

  // 关闭浏览器实例
  await browser.close();
})();

执行命令后得到

image.png