使用 puppeteer
zKing 2019-11-25 爬虫
摘要
一个基于 nodeJs 的高级爬虫工具,由 Chrome 官方团队维护
# 献上文档
# 快速上手
cnpm install puppeteer -S
const puppeteer = require('puppeteer')
;(async () => {
const browser = await puppeteer.launch() // 实例化一个浏览器对象
const page = await browser.newPage() // 创建通过浏览器来新建一个标签页
let url = 'https://example.com/'
await page.goto(url) // 访问页面
await page.screenshot({ // 调用截图 API
path: 'example.png'
});
await browser.close() // 关闭浏览器
})()
# 爬取数据
const puppeteer = require('puppeteer');
const sleep = timeout => new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, timeout);
});
;(async () => {
console.log("-------- 启动浏览器 ---------")
const browser = await puppeteer.launch({
args: ['--no-sandbox'] // 以非沙箱模式启动
})
const page = await browser.newPage()
console.log("------- 跳转页面 ----------")
let url = 'https://example.com/'
await page.goto(url, {
waitUntil: 'networkidle2' // 等待页面全部加载完成
})
const result = await page.evaluate(() => { // 在这个函数内可以写自定义的 JS 脚本
let dom = document.querySelector('h1');
return dom.innerText;
});
//const Handle = await page.$('h1');
//const result = await page.evaluate(dom => dom.innerText, Handle);
//await bodyHandle.dispose();
console.log(result)
await browser.close()
})()
# 实战
这里以 简书 的网站为例,写3个示例
- 直接爬取简书首页的文章列表
- 模拟使用简书的搜索框进行搜索
- 点击首页的“阅读更多”,获取更多数据
# 爬取简书首页文章示例
const puppeteer = require('puppeteer');
const sleep = timeout => new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, timeout);
});
;(async () => {
console.log("-------- 启动浏览器 ---------")
const browser = await puppeteer.launch({
args: ['--no-sandbox']
})
const page = await browser.newPage()
console.log("------- 跳转页面 ----------")
let url = 'https://www.jianshu.com/'
await page.goto(url, {
waitUntil: 'networkidle2'
})
const result = await page.evaluate(() => {
let res = [];
let $ = window.$; // 简书中使用了 jquery,所以这里直接引用了
let list = $('.note-list')
list.find('li').each((index, ele) => {
let item = $(ele);
let title = item.find('.title').text();
let abstract = item.find('.abstract').text();
let nickname = item.find('.nickname').text();
res.push({
title: title,
abstract: abstract,
nickname: nickname
})
})
return res;
});
console.log(result)
await browser.close()
})()
# 模拟使用简书的搜索框进行搜索
const puppeteer = require('puppeteer');
const sleep = timeout => new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, timeout);
});
;(async () => {
console.log("-------- 启动浏览器 ---------")
const browser = await puppeteer.launch({
args: ['--no-sandbox'],
headless: false, // 打开浏览器模型展示过程
})
const page = await browser.newPage()
console.log("------- 跳转页面 ----------")
let url = 'https://www.jianshu.com/'
await page.goto(url, {
waitUntil: 'networkidle2'
})
await page.focus('.search-input');
// 输入数据
await page.type('.search-input',"puppeteer", {
delay: 100
})
// await page.keyboard.press('Enter');
await page.click('.search-btn')
console.log(result)
// await browser.close()
})()
# 点击首页的“阅读更多”,获取更多数据
const puppeteer = require('puppeteer');
const sleep = timeout => new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, timeout);
});
;(async () => {
console.log("-------- 启动浏览器 ---------")
const browser = await puppeteer.launch({
args: ['--no-sandbox'],
// headless: false,
})
const page = await browser.newPage()
// 设置浏览器视口大小,其实好像无所谓
// page.setViewport({
// width: 1368,
// height: 768,
// });
console.log("------- 跳转页面 ----------")
let url = 'https://www.jianshu.com/'
await page.goto(url, {
waitUntil: 'networkidle2'
})
// 跳转到页面底部
for (let i = 0; i < 3; i++) {
await page.evaluate(() => {
document.scrollingElement.scrollTop = document.scrollingElement.scrollHeight;
})
await sleep(1000)
}
// 点击阅读更多
await page.waitForSelector('.load-more')
await page.click('.load-more')
await sleep(1000)
const result = await page.evaluate(() => {
let res = [];
let $ = window.$; // 简书中使用了 jquery,所以这里直接引用了
let list = $('.note-list')
list.find('li').each((index, ele) => {
let item = $(ele);
let title = item.find('.title').text();
let abstract = item.find('.abstract').text();
let nickname = item.find('.nickname').text();
res.push({
title: title,
abstract: abstract,
nickname: nickname
})
})
return res;
});
console.log(result)
console.log(result.length)
await browser.close()
})()
# 个人总结
puppeteer 是个非常好用的爬虫工具,而且设置 headless:falsse
就可以可视化看到过程,调试起来很舒服
回顾一下使用 puppeteer 的流程
- 导入 puppeteer
- 创建浏览器对象 puppeteer.launch()
- 创建新标签页 browser.newPage()
- 跳转页面 page.goto(url)
- 加载自己写入的脚本 page.evaluate(fn)
- 最后关闭浏览器
关于爬虫得到的数据应该如何传递,这里是使用了node
的 child_proccess
模块来进行数据传递
puppeteer_test.js
const puppeteer = require('puppeteer')
;(async () => {
const browser = await puppeteer.launch()
const page = await browser.newPage()
let url = 'https://example.com/'
await page.goto(url)
let data = await page.evaluate(()=>{
...
})
process.send(data)
process.exit(0)
await browser.close() // 关闭浏览器
})()
app.js
const child_process = require("child_process")
let child = child_process.fork(resolve(__dirname,'./puppeteer_test.js'));
child.on("message", data => {
console.log(JSON.stringify(data));
})
另外,在爬取数据中需要等待页面加载的问题,虽然本身 puppeteer 的 API 中就有 page.on('load',cb)
的方法,但是,这里还是定义了一个sleep
函数来进行等待页面加载,避免嵌套过多。
const sleep = timeout => new Promise(resolve) =>{
setTimeOut(()=>{
resolve()
},timeout)
}
# 部署 puppeteer 到 linux 服务器上遇到的问题
本来以为部署 puppeteer 很简单,但还是遇到了坑,puppeteer 给出了解决方案,点击 这里
我个人使用的 linux 是 Ubuntu,所以直接进行以下操作。
sudo apt-get update
sudo apt-get install gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget
另外,puppeteer 使用的时候还要进行设置
const browser = await puppeteer.launch({
args: ['--no-sandbox'] // 无沙箱模式
// headless: false 默认就是 false
})
好的,第一步问题解决。但是还没结束。个人在使用的时候一直报进程端口错误。发现是之前装 Ubuntu 的时候设置了 ufw 和 iptables 的原因,关闭之后就可以运行了。
#关闭防火墙
sudo ufw disable
#关闭 iptables
iptalbes -F
iptables -X
iptables -Z
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT
关于如何启动防火墙和 iptables 可以到“服务器部署”的专题中进行查看