nestjs爬取boss,并导出excel/json格式

1,513 阅读3分钟

一、创建nestjs项目

npm i -g @nestjs/cli
nest new project-name

1.学习nest命令,快速生成curd

nest --help

image.png

nest g res spider

1.选择风格 image.png

生成成功并自动注入 image.png

2.安装依赖

pnpm add puppeteer exceljs -S

了解Puppeteer

Puppeteer 是一个 Node.js 库,它提供了一个高级 API 来通过 开发工具协议 控制 Chrome/Chromium。Puppeteer 默认以 无头 模式运行,但可以配置为在完整 ("有头") Chrome/Chromium 中运行。

特性

你可以在浏览器中手动执行的大多数操作都可以使用 Puppeteer 完成!以下是一些帮助你入门的示例:

  • 自动化表单提交、UI 测试、键盘输入等。
  • 使用最新的 JavaScript 和浏览器功能创建自动化测试环境。
  • 捕获站点的 时间线痕迹 以帮助诊断性能问题。
  • 测试 Chrome 扩展程序
  • 生成页面的屏幕截图和 PDF。
  • 抓取 SPA(单页应用)并生成预渲染内容(即 "SSR"(服务器端渲染))。

二、开始写代码

import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { TestService } from './test.service';
import { CreateTestDto } from './dto/create-test.dto';
import { UpdateTestDto } from './dto/update-test.dto';

@Controller('test')
export class TestController {
  constructor(private readonly testService: TestService) {}

  @Post()
  create(@Body() createTestDto: CreateTestDto) {
    return this.testService.create(createTestDto);
  }
  // 新增 转json,excel
  @Get('toJson')
  toJson() {
    return this.testService.toJson();
  }
  @Get('toExcel')
  toExcel() {
    return this.testService.toExcel();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.testService.findOne(+id);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() updateTestDto: UpdateTestDto) {
    return this.testService.update(+id, updateTestDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.testService.remove(+id);
  }
}
// servece层
import { Injectable } from '@nestjs/common';
import { CreateSpiderDto } from './dto/create-spider.dto';
import { UpdateSpiderDto } from './dto/update-spider.dto';
import puppeteer from 'puppeteer'
import { writeFile } from 'fs';
import { join } from 'path';
import * as Excel from 'exceljs';

@Injectable()
export class SpiderService {
  create(createSpiderDto: CreateSpiderDto) {
    return 'This action adds a new spider';
  }
  async toJson() {

  }

  async toExcel() {

  }

  findAll() {
    return `This action returns all spider`;
  }

  findOne(id: number) {
    return `This action returns a #${id} spider`;
  }

  update(id: number, updateSpiderDto: UpdateSpiderDto) {
    return `This action updates a #${id} spider`;
  }

  remove(id: number) {
    return `This action removes a #${id} spider`;
  }
}

先打开boss地址 1.查看如何根据岗位-城市-页码进行搜索查询 2.确认要获取dom信息的class

如:www.zhipin.com/web/geek/jo…

image.png

 async toJson() {
    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: item.querySelector('.job-name').textContent,
                  companyArea: item.querySelector('.job-area').textContent,
                  salary: item.querySelector('.salary').textContent,
                  companyName: item.querySelector('.company-name').textContent,
              }
          })
      });
      allJobs.push(...jobs);
    }
 }

想要的信息保存在数组之后,然后要导出你想要的格式

    // 转成json
    const json = JSON.stringify(allJobs);
    let p = join(process.cwd(), 'job.json');
    // 写入本地文件
    writeFile(p, json, (err) => {
      if (err) throw err;
      console.log('The file has been saved!');
    });
    return `This action returns a toJson spider`;
     // 转成xlsx
     let p = join(process.cwd(), './data.xlsx');

    const workbook = new Excel.Workbook();
    const worksheet = workbook.addWorksheet('Sheet1');

    worksheet.columns = [
      { header: '岗位', key: 'job', width: 20 },
      { header: '薪资', key: 'salary', width: 20 },
      { header: '公司', key: 'companyName', width: 40 },
      { header: '地址', key: 'companyArea', width: 60 },
      { header: '是否在线', key: 'online', width: 20 },
    ];
    // 添加数据
    allJobs.forEach((rowData) => {
      worksheet.addRow(rowData);
    });
    workbook.xlsx.writeFile(p);
    return `This action returns a toExcel spider`;

然后输入启动命令即可尝试

pnpm start:dev

打开浏览器输入

localhost:3000/spider/toJson

localhost:3000/spider/toExcel

然后会在目录下生成这俩文件 image.png

所以操作完大家有没有发现boss的问题,最低要有10页的气氛组,如果不足10页的数据量就会把半年内发过招聘的公司还展示出来,如果还不足10页,就从各地抽调气氛组入场。。没有这个岗位的招聘就算了,没必要把半年都没活跃的公司拉进来充当气氛组吧,体验真的很差,强烈谴责!!!

这样就完成了,查看非常方便。如有需要可自取,谢谢star

nestjs真的很方便!!!

源码地址