基本使用
使用
const browser = await puppeteer.launch({headless: false}); // default is true
绑定已安装的 chrome
const browser = await puppeteer.launch({
executablePath: "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
});
参数参考:pptr.dev/api/puppete… 常用 headless、slowMo、args。
打开新的标签页
const page = await browser.newPage();
点击
await page.click(".btn");
输入框输入
await page.type("#user-mobile", telephone);
页面跳转
page.goto(url)
页面返回
page.goBack()
元素的点击
const element = await page.waitForSelector("text/销量");
await element.click();
等待导航
await page.waitForNavigation();
等待选择器
await page.waitForSelector("#main"),
获取元素,page.$$ 类似
const el = await page.$('.title');
获取元素内容,page.$$eval 类似
const title = await page.$eval('.title > span', el => el.textContent.trim());
打开新的 tab 页之后,需要获取当前 page 对象
let page = await browser.newPage();
await page.goto(url);
let btn = await page.waitForSelector('#btn');
//在点击按钮之前,事先定义一个 Promise,用于返回新 tab 的 Page 对象
const newPagePromise = new Promise(res =>
browser.once('targetcreated',
target => res(target.page())
)
);
await btn.click();
//点击按钮后,等待新tab对象
let newPage = await newPagePromise;
页面滚动
const { scrollPageToBottom } = require("puppeteer-autoscroll-down");
数据保存文件使用 xlsx
// 写入 Excel
async function writeToExcel(data, filename) {
const workbook = xlsx.utils.book_new();
const worksheet = xlsx.utils.json_to_sheet(data);
xlsx.utils.book_append_sheet(workbook, worksheet, 'sheet1');
await xlsx.writeFile(workbook, `./data/${filename}.xlsx`);
}
连接现有浏览器
修改chrome的,打开 chrome 所在文件夹位置,chrome 右键,属性,修改目标类似:
"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --user-data-dir="D:\work"
--user-data-dir 必须,修改成自己的路径。
访问 http://127.0.0.1:9222/json/version 得到:
{
"Browser": "Chrome/108.0.5359.99",
"Protocol-Version": "1.3",
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
"V8-Version": "10.8.168.21",
"WebKit-Version": "537.36 (@aa99c50ebc03bd334a4a8f11e99ab6fb5e0adf78)",
"webSocketDebuggerUrl": "ws://localhost:9222/devtools/browser/ce51e0b5-4598-4bcd-8c9a-9bccdfefed3b"
}
通过 puppeteer.connect 获取 browser,browserWSEndpoint 为上面获取的值:
// 连接现有的浏览器
const browser = await puppeteer.connect({
browserWSEndpoint: config.webSocketDebuggerUrl
});
const pages = await browser.pages();
const page = pages[0];
注意 webSocketDebuggerUrl 从 http://127.0.0.1:9222/json/version 中获取而不是 http://127.0.0.1:9222/json
报错:
(node:26404) UnhandledPromiseRejectionWarning: Error: connect ECONNREFUSED 127.0.0.1:9222
首先要打开浏览器。
报错:
(node:31528) UnhandledPromiseRejectionWarning: #<ErrorEvent>
(Use `node --trace-warnings ...` to show where the warning was created)
(node:31528) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:31528) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
检查 webSocketDebuggerUrl 是否正确,每次关闭浏览器,webSocketDebuggerUrl 都会改变,最好通过 Ajax 请求获取:
async function getDebugUrl() {
const res = await axios.get('http://127.0.0.1:9222/json/version');
return res.data.webSocketDebuggerUrl;
}
常见报错
-
等待选择器超时,没有找到选择
-
等待导航超时报错:
Error: Navigation Timeout Exceeded: 30000ms exceeded解决:
await Promise.all([ page.waitForNavigation(), page.click('a') ]);也可能为不在当前的连接浏览器桌面,导致断开
-
导航错误:
Puppeteer Execution context was destroyed, most likely because of a navigation导航时出现了问题,执行上下文被销毁
-
可能 page 对象改变
-
page 返回,比如循环中,需要重新获取元素
-
要点总结
- 选择器我只使用过简单的,类似
nth-child用法报错,还未解决; - 如果页面要前往新页面,元素没有链接,只能使用元素点击跳转,注意返回当前页面;
- 反爬虫:打开页面以及操作时增加等待时间;
- 反爬虫:增加
proxy,userAgent,device的使用; page.evaluate()是在 page 的上下文中执行代码并返回结果,所以其中的代码无法获取 page 等外部变量,即 page 是 Node 的环境变量,可以不用 page(例如获取元素使用document.querySelector)或者使用其他方法获取外部变量;- 与其通过选择器获取数据,不如分析页面内容,看是否能从 window 中获取;
- 如果是通过字符串解析,使用正则 match 模式匹配解析获取数据;
- 如何获取 window 对象?
page.evaluate无法获取整个 window 对象,但是可以获取到其中具体的值,参考:github.com/puppeteer/p…