我之前的笔记使用puppeteer来截屏是使用puppeteer来截屏。其实puppeteer也可以用来当爬虫。
superagent + cheerio
先按照我之前的笔记Node.js之使用superagent + cheerio 来爬取网页内容使用superagent+cheerio来试下水,来抓取一下当当的内容。
以爬取当当网的图书畅销榜近七日的数据为例,爬取第一页的图书
先来看一下图书畅销榜里面的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
那么我就想,使用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);
});
有一个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之后,打印出来就是正常的中文了
用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源码如下图所示
可以在chrome里面,在下一页
这个按钮上右击 -> Inspect
,chrome会自动打开调试界面并在Elements
里面自动定位到这个元素。然后再右击,选择Copy Selector
,就得到了它的选择器。比如说写一页
这个按钮的选择器就是body > div.bang_wrapper > div.bang_content > div.bang_list_box > div.paginating > ul > li.next > a
。
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上的脚本到浏览器内部环境运行。