我之前的笔记使用puppeteer来截屏是使用puppeteer来截屏。其实puppeteer也可以用来当爬虫。

superagent + cheerio

先按照我之前的笔记Node.js之使用superagent + cheerio 来爬取网页内容使用superagent+cheerio来试下水,来抓取一下当当的内容。

以爬取当当网的图书畅销榜近七日的数据为例,爬取第一页的图书

先来看一下图书畅销榜里面的HTML结构

html

按照之前这篇笔记Node.js之使用superagent + cheerio 来爬取网页内容里面的代码,修改一下


var superagent = require('superagent');  
var cheerio = require('cheerio');

var url = "http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent7-0-0-1-1";  

var items = [];  
superagent.get(url)  
    .end(function(error, res) {
        if (error) {
            throw error;
        }

        var $ = cheerio.load(res.text);
        $('.bang_list_box ul li .name').each(function (idx, element) {
          var $element = $(element);
          var title = $element.find('a').attr('title');
   

          items.push(title);
      
        });

        console.log($items);
    });

但是发现打印出来是乱码!如下图所示:

乱码

肿么回事?打开当当网这个网页的head看一下,原来编码形式是gb2312,不是utf-8

charset

那么我就想,使用iconv-lite进行转码一下吧,示例代码如下,但还是乱码,只不过乱得和刚才不一样了

var superagent = require('superagent');
var cheerio = require('cheerio');
var iconv = require('iconv-lite');


var items = [];

var host = "http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent7-0-0-1-1";

superagent.get(host)
  .end(function (error, res) {
      if (error) {
        throw error;
      }

      var $ = cheerio.load(res.text);
      $('.bang_list_box ul li .name').each(function (idx, element) {
          var $element = $(element);
          var title = $element.find('a').attr('title');
          items.push(iconv.decode(title, 'gb2312'));

      });
        console.log(items);

  });

still 乱码

有一个warning,是Iconv-lite warning: decode()-ing strings is deprecated. Refer to https://github.com/ashtuchkin/iconv-lite/wiki/Use-Buffers-when-decoding

换一种其他的方法,来解决这个乱码问题—— 给superagent设置charset

示例代码如下:


var superagent = require('superagent');
var cheerio = require('cheerio');
var charset = require("superagent-charset");

charset(superagent); 

var items = [];

var host = "http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent7-0-0-1-1";

superagent.get(host)
  .charset('gb2312') //设置字符
  .end(function (error, res) {
      if (error) {
        throw error;
      }

      var $ = cheerio.load(res.text);
      $('.bang_list_box ul li .name').each(function (idx, element) {
          var $element = $(element);
          var title = $element.find('a').attr('title');
   

          items.push(title);
      });
      console.log($items);
  });

给superagent设置charset之后,打印出来就是正常的中文了

correct

用puppeteer爬取网页内容

抓取第一页的数据

下面,我就用puppeteer来实现和上面一样的功能——抓取图书畅销榜近七日的的第一页的书

示例代码如下:

const puppeteer = require('puppeteer');

let scrape = async () => {
    const browser = await puppeteer.launch({
      args:  ['--no-sandbox', '--disable-setuid-sandbox']
    });
    var page = await browser.newPage();

    await page.goto('http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent7-0-0-1-1');
    await page.setViewport({width: 1920, height: 1080});

    const result = await page.evaluate(() => {
        let data = []; // 初始化空数组来存储数据
        let elements = document.querySelectorAll('.bang_list_box ul li .name'); // 获取所有书籍元素
        for (var i = 0; i < elements.length; i ++) {
            let info = elements[i].querySelector('a');

            data.push({'title' : info.innerText});
        }

        return data; // 返回数据
    });

    browser.close();
    return result;
};

scrape().then((value) => {
    console.log(value);
});

打印的结果如下图所示,并没有出现乱码的问题

不乱码


使用puppeteer与网页交互,抓取下一页的数据

上面只是抓取了第一页的数据,我想要抓取后面几页的数据怎么办?

puppeteer还可以操作界面上的控件,比如说按下一页这个按钮,它的HTML源码如下图所示

下一页这个按钮的html

可以在chrome里面,在下一页这个按钮上右击 -> Inspect,chrome会自动打开调试界面并在Elements里面自动定位到这个元素。然后再右击,选择Copy Selector,就得到了它的选择器。比如说写一页这个按钮的选择器就是body > div.bang_wrapper > div.bang_content > div.bang_list_box > div.paginating > ul > li.next > a

inspect-item-in-chrome

copy-selector-in-chrome

puppeteer的page.click()方法可以用来点击页面元素。

比如说我要在打印出图书畅销榜近七日前两页的书名,示例代码如下:

const puppeteer = require('puppeteer');

let scrape = async () => {
    const browser = await puppeteer.launch({
      args:  ['--no-sandbox', '--disable-setuid-sandbox']
    });
    var page = await browser.newPage();

    await page.goto('http://bang.dangdang.com/books/bestsellers/01.00.00.00.00.00-recent7-0-0-1-1');
    await page.setViewport({width: 1920, height: 1080});

    var items = [];
    var i = 1;
    for (;i < 3; i ++) {
      const result = await page.evaluate(() => {
        let data = []; // 初始化空数组来存储数据
        let elements = document.querySelectorAll('.bang_list_box ul li .name');
        for (var j = 0; j < elements.length; j ++) {
            let info = elements[j].querySelector('a');

            data.push({
              'title' : info.innerText
            });
        }

        return data; // 返回数据
      });
      await page.click('body > div.bang_wrapper > div.bang_content > div.bang_list_box > div.paginating > ul > li.next > a');
      await page.waitFor(1000);
      items.push(result);
    }
    
    console.log(items);
    browser.close();
};


scrape();

puppeteer和cheerio的区别

cherrico本质上只是一个使用类似jquery的语法操作HTML文档的库,使用cherrico爬取数据,只是请求到静态的HTML文档,如果网页内部的数据是通过ajax动态获取的,那么便爬不到的相应的数据。

而Puppeteer能够模拟一个浏览器的运行环境,能够请求网站信息,并运行网站内部的逻辑。然后再通过WS协议动态的获取页面内部的数据,并能够进行任何模拟的操作(点击、滑动、hover等),并且支持跳转页面,多页面管理。甚至能注入node上的脚本到浏览器内部环境运行。