使用 Puppeteer 进行爬虫
一. 使用Puppeteer进行爬虫的流程
1.安装Puppeteer
在终端中运行npm install puppeteer --save
命令安装Puppeteer库。
2.引入Puppeteer
在代码中使用const puppeteer = require('puppeteer')
引入Puppeteer库。
3.创建浏览器实例
使用const browser = await puppeteer.launch()
创建一个headless Chrome浏览器实例。如果需要打开有界面的浏览器进行调试,可以通过传参选项对象来配置启动参数,例如:{ headless : false }
。
4.打开目标网站页面
使用const page = await browser.newPage()
创建一个新的页面对象,并使用await page.goto(url)
打开目标网站的页面。在打开页面之前,还可以使用page.setUserAgent(userAgent)
方法设置用户代理,以此来模拟不同的浏览器环境。
5.提取数据
使用page.evaluate()
方法执行Javascript脚本,在浏览器环境中运行脚本,并将结果返回给爬虫程序 。还可以使用选择器或者其他方式提取所需的数据,例如使用document.querySelector(selector)
或者document.querySelectorAll(selector)
方法选取元素。
6.处理异步操作
由于页面某些操作(页面滚动,拖拽,点击按钮)可能是异步的,因此需要使用await
关键字等待这些操作完成。也可以使用page.waitForSelector(selector)
方法等待某个元素出现后再进行操作。
7.关闭浏览器实例
使用browser.close()
关闭浏览器实例,并释放资源。
8.反爬虫机制
Puppeteer 可以模拟用户对网站的操作,因此可以抵御一些反爬虫机制。但需要注意不要过度使用,以免给目标网站带来过大的负担。还可以使用一些类似于随机延迟、更换 IP 等技巧来模拟人工操作,减少被识别为爬虫的风险。
9.错误处理
在使用Puppeteer进行爬虫时,可能会遇到各种错误,例如页面加载超时,元素不存在等。因此需要进行适当的错误处理,例如使用try···catch
捕获异常,并输出错误的信息进行重试操作。
二. Puppeteer的一些常用的API
1. puppeteer.launch([options])
:
启动一个 headless Chrome 实例,并返回一个 Browser 对象。options 参数可以包含一些启动选项,例如是否显示浏览器界面等。
2. browser.newPage()
:
创建一个新的 Page 对象,代表一个浏览器页面。
3. page.goto(url, [options])
:
在当前浏览器页面中打开指定的 URL。可以通过 options 参数设置超时时间、referer 等请求选项。
4. page.evaluate(pageFunction, [...args])
:
在浏览器上下文中执行 JavaScript 脚本,并将结果返回给 Node.js 程序。pageFunction 可以是一个函数或字符串,args 是传递给函数的参数。
5. page.$(selector)
:
selector
<string> 选择器- 返回: <Promise<?ElementHandle>>
此方法在页面内执行 document.querySelector
。返回第一个匹配指定选择器的元素,如果没有找到则返回 null。
6. page.$$(selector)
:
selector
<string> 选择器- 返回: <Promise<Array<ElementHandle>>>
此方法在页面内执行 document.querySelectorAll
。返回所有匹配指定选择器的元素数组,如果没有找到任何元素则返回空数组。
7. elementHandle.click([options])
:
selector
<string> 要点击的元素的选择器。 如果有多个匹配的元素, 点击第一个。options
<Object>button
<string>left
,right
, 或者middle
, 默认是left
。clickCount
<number> 默认是 1. 查看 UIEvent.detail。delay
<number>mousedown
和mouseup
之间停留的时间,单位是毫秒。默认是0
- 返回: <Promise> Promise对象,匹配的元素被点击。 如果没有元素被点击,Promise对象将被rejected。
// click() 触发了一个跳转,会有一个独立的 page.waitForNavigation() Promise对象需要等待。
const [response] = await Promise.all([
page.waitForNavigation(waitOptions),
page.click(selector, clickOptions),
]);
8. page.waitForSelector(selector, [options])
:
等待指定选择器的元素出现在页面中,然后返回该元素的 ElementHandle 对象。
9. page.setCookie(...cookies)
:
设置浏览器页面的 cookie。
10. page.setUserAgent(userAgent)
:
设置浏览器页面的用户代理。
三. 下面是用Puppeteer爬取wegame折扣速递模块的代码
//引入Puppeteer
const puppeteer = require('puppeteer');
//引入fs
const fs = require('fs');
//动态获取折扣速递板块的数据
(async() => {
// 启动浏览器
const browser = await puppeteer.launch({
headless: false, // 默认是无头模式,这里为了示范所以使用正常模式
})
// 控制浏览器打开新标签页面
const page = await browser.newPage()
// 控制浏览器全屏
await page.setViewport({ width: 1920, height: 1080 });
// 在新标签中打开要爬取的网页
await page.goto('https://www.wegame.com.cn/store')
// 由于折扣速递板块位于最下方,所以需要页面滚动到最底端(封装页面滚动函数)
await autoScroll(page);
//返回的data
let data = [];
//获取点击换一换的按钮
const change = await page.$('#discount-express-container > div > div.skin-panel-inner > div.tui-panel-hd > div > a:nth-child(1)');
//任意模拟点击事件50次
for (let i = 0; i < 50; i++) {
//模拟点击事件
change && await change.click();
//每次点击之后,留出1s给页面某些元素(图片/视频等)加载
await page.waitForTimeout(1000);
data = await page.evaluate((data) => {
let titles = document.querySelectorAll('#discount-express-container > div > div.skin-panel-inner > div.tui-panel-bd > ul > li > div > div > div.gcard-bd > div > div.gcard-tit');
let imgs = document.querySelectorAll('#discount-express-container > div > div.skin-panel-inner > div.tui-panel-bd > ul > li > div > div > div.gcard-cover > a > div > img:nth-child(1)');
let prices = document.querySelectorAll('#discount-express-container > div > div.skin-panel-inner > div.tui-panel-bd > ul > li > div > div > div.gcard-bd > div > div:nth-child(2) > div')
//遍历每次点击换一换,页面更新的数据组(3个)
for (let i = 0; i < titles.length; i++) {
// 拆分字符串,对页面数据进行处理
const [discount, new_price, old_price] = prices[i].textContent.split('¥');
data.push({
img: {
src: imgs[i].getAttribute('src'),
},
title: titles[i].textContent,
price: {
discount,
newprice: '¥' + new_price,
oldprice: '¥' + old_price,
}
})
}
return data
}, data)
}
//换一换的数据进行去重处理
const newdata = Array.from(new Set(data.map(JSON.stringify)), JSON.parse);
// 关闭浏览器实例
await browser.close();
// 将数据作为 JSON 写入json文件
const jsonData = JSON.stringify(newdata);
//写入文件路径
const filePath = './discount.json';
//调用fs.writeFile()进行写入文件
fs.writeFile(filePath, jsonData, (err) => {
if (err) {
console.error(err);
return;
}
console.log('Data written to file');
});
})()
//封装页面滚动函数
async function autoScroll(page) {
await page.evaluate(async() => {
await new Promise((resolve, reject) => {
var totalHeight = 0;
var distance = 100;
var timer = setInterval(() => {
var scrollHeight = document.body.scrollHeight;
window.scrollBy(0, distance);
totalHeight += distance;
if (totalHeight >= scrollHeight) {
clearInterval(timer);
resolve();
}
}, 50);
});
});
}
转载自:https://juejin.cn/post/7228258200239538235