使用 Puppeteer 和 Node.js 抓取(几乎)任何内容的简介
TL;DR(30 秒)
分解
拥有强大的力量……
从页面内容中抓取数据
使用 Chrome Devtools 制作选择器
使用屏幕截图进行调试
其他提示
限制
谢谢你!
尽管名字听起来有点吓人,但无头浏览器并不可怕(至少对大多数人来说)。它们与标准 Web 浏览器类似,但通过代码控制,而不是鼠标和键盘。使用无头浏览器,你几乎可以在页面上执行所有使用普通 Web 浏览器可以执行的操作,包括提交表单、等待异步 JavaScript 以及设置 Cookie。与现代云平台结合使用时,创建自动化数据抓取工具比以往任何时候都更加容易。
在本文中,我将向您介绍一些网页数据抓取的技巧和示例,这些技巧和示例使用了Puppeteer(一款基于 Chromium 的无头浏览器,现已成为行业标准)和 Node.js。我们还将使用Autocode轻松运行和迭代我们的抓取代码。
您只需要一个免费的 Autocode 帐户即可开始使用。让我们开始吧!
TL;DR(30 秒)
让一个基本的爬虫程序运行起来很简单。首先访问https://autocode.com并创建或登录你的账户。创建一个新项目,然后将以下代码粘贴到编辑器中:
// authenticates you with the API standard library
const lib = require('lib')({token: process.env.STDLIB_SECRET_TOKEN});
const puppeteer = require('autocode-puppeteer');
let browser = await puppeteer.launch();
let page = await browser.newPage();
await page.goto('https://www.youtube.com/'); // Any URL you'd like
let title = await page.title();
await browser.close();
return title;
Autocode 会自动添加所需的依赖项,所以您现在只需按下“保存”或“运行”按钮即可将代码推送到线上。就这样!现在,您就可以使用 Puppeteer 抓取页面标题(即在 Chrome 中打开网站时在标签栏中看到的内容)。
分解
上面的代码中发生了以下事情:
- 我们需要配置一个可在 Autocode 环境中运行的 Puppeteer 变体。请注意,由于依赖项大小限制,默认的 Puppeteer 包将无法运行。
- 我们通过启动 Puppeteer 并打开一个新页面来准备它。
- 我们使用 导航到所需的页面
await page.goto()
。 - 一旦进入正确的页面,我们就使用该
page.title()
方法来抓取页面标题。 - 完成后我们关闭浏览器实例。
此流程类似于在计算机上打开 Chrome,https://youtube.com/
在导航栏中输入内容,查看页面标题,最后关闭浏览器。在使用 Puppeteer 进行网页抓取时,我们也将遵循这一通用模式。
我们只是触及了可能性的表面,但首先要强调一些重要的事情。
拥有强大的力量……
许多网站不允许抓取数据,并使用 reCAPTCHA 之类的工具,或包含robots.txt文件,其中包含抓取工具和其他自动化工具的指南。在抓取数据之前,请务必检查并遵守网站规则。
从页面内容中抓取数据
现在你已经掌握了基础知识,让我们来探索如何从页面中抓取更多有用的数据。Puppeteer 提供的一项关键功能是使用CSS 选择器查询页面中的 HTML 元素。例如,Puppeteer 的page.$$eval()
方法接受一个选择器,并允许你在浏览器上下文中对所有与该选择器匹配的元素运行代码。
实际操作起来是这样的:
// authenticates you with the API standard library
const lib = require('lib')({token: process.env.STDLIB_SECRET_TOKEN});
const puppeteer = require('autocode-puppeteer');
let browser = await puppeteer.launch();
let page = await browser.newPage();
await page.goto('https://www.youtube.com/');
let videoData = await page.$$eval('a#video-title-link', (titleLinkEls) => {
return titleLinkEls.map((titleLinkEl) => {
return {
title: titleLinkEl.getAttribute('title'),
link: 'https://youtube.com' + titleLinkEl.getAttribute('href')
};
});
});
await browser.close();
return videoData;
一旦我们将 YouTube 加载到页面中,我们就可以使用该page.$$eval()
函数查询首页上的每个视频链接,并返回视频名称及其链接。我们实际上创建了一个自定义的热门视频 API!
使用 Chrome Devtools 制作选择器
a#video-title-link
创建爬虫的一个棘手之处在于弄清楚页面中哪些元素包含相关数据——毕竟,要找到匹配 YouTube 上所有视频链接的元素并非一目了然。Chrome 开发者工具中的检查器是一个便捷的工具。
您可以在顶栏中的“视图”>“开发人员”>“检查元素”下打开检查器,或者使用键盘快捷键CMD + Option + C:
打开检查器后,您可以将鼠标悬停在页面上的元素上,并查看它们是否被高亮显示。单击某个元素后,DevTools 窗口中将显示该元素,包括其属性和在 DOM 中的位置。
通过检查器,您应该能够找到一种方法来引用正确的元素并抓取您想要的数据。
使用屏幕截图进行调试
Puppeteer 允许你使用 方法来截取页面的屏幕截图。在编写涉及与或交互的流程(类似于语句)page.screenshot()
时,此功能对于查看当前页面状态尤其有用。page.click()
page.select()
console.log
例如,假设你想构建一个流程,点击 YouTube 首页上的第一个视频,获取其点赞和踩点数。你可以尝试以下方法:
// authenticates you with the API standard library
const lib = require('lib')({token: process.env.STDLIB_SECRET_TOKEN});
const puppeteer = require('autocode-puppeteer');
/**
* An HTTP endpoint that acts as a webhook for HTTP(S) request event
* @returns {object.http} result
*/
module.exports = async (context) => {
let browser = await puppeteer.launch();
let page = await browser.newPage();
await page.goto('https://www.youtube.com/');
await page.click('a#video-title-link');
let screenshot = await page.screenshot();
await browser.close();
return {
statusCode: 200,
body: screenshot,
headers: {
'Content-Type': 'image/png'
}
};
}
注意:要使屏幕截图返回图像而非二进制数据,我们必须传回正确的Content-Type
标头。上面的代码示例展示了如何在 Autocode 中使用object.http
返回类型执行此操作。要返回其他类型的数据,您需要移除返回类型或将返回类型更改为与要返回的数据类型匹配。有关更多信息,您可以阅读Autocode 用于参数和返回类型的FunctionScript 规范。
如果您尝试运行上述代码几次,您会注意到屏幕截图要么与主页没有变化,要么您会看到类似这样的内容:
这将提示您,您需要等待页面的某些部分加载,例如page.waitForSelector()
:
// authenticates you with the API standard library
const lib = require('lib')({token: process.env.STDLIB_SECRET_TOKEN});
const puppeteer = require('autocode-puppeteer');
/**
* An HTTP endpoint that acts as a webhook for HTTP(S) request event
* @returns {object.http} result
*/
module.exports = async (context) => {
let browser = await puppeteer.launch();
let page = await browser.newPage();
await page.goto('https://www.youtube.com/');
await page.click('a#video-title-link');
await page.waitForSelector('#top-level-buttons');
let screenshot = await page.screenshot();
await browser.close();
return {
statusCode: 200,
body: screenshot,
headers: {
'Content-Type': 'image/png'
}
};
}
其他用例包括将屏幕截图与Autocode 调度程序、 Box等文件托管平台以及pixelmatch等包相结合,以创建用于回归测试的每日屏幕截图差异。
其他提示
调整你的抓取工具
网络世界充满变数,新的标准层出不穷。如果某种技术在第一次尝试时未能达到预期效果,您可能需要尝试其他方法,例如等待选择器,或者干脆等待一段时间而不是等待事件DOMContentLoaded
,或者使用其他选择器。幸运的是,Puppeteer 提供了丰富的选择!
增加自动编码超时
涉及访问多个页面的复杂流程或访问速度较慢的页面的流程可能需要超过 Autocode 默认的 10 秒超时时间。如果您的抓取工具持续超时,您可以尝试在Autocode 编辑器左下角的“高级设置”下增加超时时间。
免费帐户的超时时间最多可增加至 30 秒,而专业级帐户的超时时间最多可增加至 2 分钟。
模拟登录状态(高级)
许多网站会根据当前查看者是否登录来显示不同的数据。为了在 Puppeteer 中模拟此状态,您可以使用page.setCookie()
从 Chrome 开发者工具获取的 Cookie 来模拟。您也可以尝试直接使用用户名和密码登录并提交网站的登录表单,但许多网站会使用验证码来阻止这种操作。
如果您尝试这样做,请注意不要在错误的域下设置 cookie!
限制
由于网页抓取工具通常依赖于其访问页面的 DOM 结构,前端更新可能会导致抓取工具崩溃。因此,如果可以使用受支持的 API,通常最好使用它。
如果您所需的数据遵循特定格式,并且您正在抓取的页面对其 CSS 属性进行了混淆或频繁更改,Puppeteer 确实有一个page.content()
方法可以将页面的当前 DOM 结构以字符串形式返回。然后,您可以使用正则表达式或其他方法来提取数据。
谢谢你!
Puppeteer 是一个非常强大的工具,如果您有兴趣深入了解,我建议您查看官方文档。
如果您有任何问题或反馈,可以通过 Autocode 社区 Slack 频道联系我。您可以在 autocode.com 顶部栏的“文档”>“在 Slack 中寻求帮助”下获取邀请。您也可以通过 Twitter @Hacubu联系我。
如果您想了解 Autocode 的最新动态,我也建议您关注@AutocodeHQ。祝您黑客愉快!
文章来源:https://dev.to/hacubu/an-introduction-to-scraping-almost-anything-with-puppeteer-and-node-js-e9g