简易爬虫 Spider (superagent + cheerio + ts)

1,520 阅读3分钟

前言

最近浏览到一些关于爬虫相关的文章,于是打算结合 ts 玩一下。整个爬取过程中看上去还是比较简单的,较为繁琐的在于使用 cheerio 解析页面的过程及存储数据(格式)问题【当然这里并没有存储到数据库】,其它地方还好。除此之外,还有一个问题:有很多网站其实设置了一些反爬机制,针对这样的网站又应如何去破解并顺利爬取信息呢?当然这个问题在本文并不会进行解答,相信在网上也可以找到一些答案,如果有兴趣的朋友我们也可以一起探讨探讨。

项目地址:github.com/niezicheng/…

概述

爬虫的基本三部曲:获取页面数据解析页面数据数据内容存储

本文使用到的主要库的相应作用:

  • superagent 获取页面数据 superagent【可以使用 axios 代替】
  • cheerio 解析页面数据 cheerio

爬取网站地址:voice.baidu.com/act/newpneu…

说明

为什么选择使用 cheerio 而不直接使用 jQuery呢?在 node 下,如果使用 jQuery,需要结合使用 jsdom 库模拟一个 window 对象

项目初始化

创建项目文件夹并进入项目文件

mkdir spider-ts && cd ./spider-ts

项目初始化

npm init -y

安装需要的依赖包

npm i cheerio superagent @types/superagent typescript ts-node -D

初始化 ts 配置,会自动生成 tsconfig.json 配置文件

tsc --init

项目初始化基本就到这里了!下面主要讲述一下项目核心代码.

核心代码

  • getSpecifyData:解析页面数据获取所需内容信息

说明$ 使用和 jQuery 类似,具体可查看 cheerio,可以通过选取元素获取所需信息

// 获取指定内容信息
const getSpecifyData = (html: string): Promise<string> => {
  const $ = cheerio.load(html); // 加载 html

  return new Promise((resolve, reject) => {
    // 获取 html 页面第 12 个 script 标签第 1 个孩子 data 数据信息【指定内容信息】
    if (($('script')[11]?.children[0] as any)?.data) {
      const pageData = ($('script')[11]?.children[0] as any)?.data;
      const sumData = JSON.parse(pageData); // 解析页面 JSON 数据

      const updatedTime = formatDate(sumData.component[0].mapLastUpdatedTime); // 获取更新时间
      const summaryDataIn = sumData.component[0].summaryDataIn; // 获取国内
      const summaryDataOut = sumData.component[0].summaryDataOut; // 获取国外
      const provinceData = sumData.component[0].caseList; // 获取省份

    	// formatDataInfo 格式化输出所需的数据类型
      resolve(formatDataInfo({
        updatedTime,
        summaryDataIn,
        summaryDataOut,
        provinceData
      }));
    }

    reject('获取数据失败');
  });
}
  • formatDataInfo:获取所需数据按指定格式内容输出

说明:爬取到数据后我们可以通过实现一个 数据格式处理 函数来输出想要的数据信息

/**
 * 格式化数据信息
 * @param summaryData 需要格式化的数据信息
 * @returns 格式化后的 JSON 数据
 */
const formatDataInfo = (data: Data) => {
  const {
    updatedTime,
    summaryDataIn,
    summaryDataOut,
    provinceData
  } = data;
  let dataJson: DataJSON = {};

  dataJson['数据更新时间'] = updatedTime;
  dataJson['国内'] = returnDataType('国内', summaryDataIn);
  dataJson['国外'] = returnDataType('国外', summaryDataOut);

  // 循环省份数据并存入 dataJson
  forEach(provinceData, (province: ProvinceData) => {
    dataJson[province?.area] = returnDataType('省份', province);
    dataJson[province?.area].subList = {};

    // 循环省份下市区数据并存入 dataJson
    forEach(province?.subList, (area: AreaData) => {
      (dataJson[province?.area]?.subList)[area?.city] = returnDataType('市区', area)
    })
  })

  return JSON.stringify(dataJson);
}
  • getHtmlContent 获取内容函数

说明:实例化 Spider,通过在类 Spider 执行器中调用获取内容函数。该函数即是概述中提到的基本三部曲

getHtmlContent = async (url: string, getSpecifyData: GetSpecifyData) => {
  // 1. 获取页面 html 信息
  const html = await this.getHtmlByAxios(url);
  // 2. 获取对应标签内容信息(可以适当处理输入内容格式)
  const data = await getSpecifyData(html);
  // 3. 内容写入文件
  this.writeFile(data);
}

更多代码实现可以查看 项目地址 【后面如果网页改版的话,项目爬虫代码不再更新,因此可能会存在问题】