什么是 Playwright?
Playwright 是由微软开发的一款开源跨平台 Node.js 库,可用于在网页浏览器中自动化执行任务。它设计可靠且易于使用,支持 CSS3 和 JavaScript 等现代网页功能。
Playwright 可用于测试 Web 应用程序、自动化 Web 任务以及 Web 爬取等任务。它基于流行的 Web 测试库 Puppeteer 构建,旨在提供更易于使用且便于维护的 API。
Playwright 支持多种浏览器,包括 Chromium、Firefox 和 WebKit。这意味着您可以利用它在不同的浏览器上自动化任务并抓取数据。Playwright 允许您轻松地在浏览器之间切换,并充分利用它们各自的独特功能。
入门指南
要在本地机器上安装并配置 Playwright,您需要先安装 Node.js 和 npm(Node.js 的包管理器)。如果您尚未安装这些软件,可以从 Node.js官方网站下载并安装。
安装好 Node.js 和 npm 后,您可以按照以下步骤安装 Playwright:
- 打开终端或命令提示符
- 创建一个名为 `playwright` 的文件夹,用于存放我们的项目代码
- 转到新创建的文件夹
- 运行命令 `npm init` 来初始化项目(并创建 package.json 文件)
- 使用命令 `npm install playwright` 安装所需的依赖项
使用 Playwright 进行基础网页抓取
创建一个名为 `index.js` 的文件,并将以下代码粘贴进去:
const { chromium } = require('playwright');
(async () => {
// Launch a Chromium browser
const browser = await chromium.launch();
// Create a new page in the browser
const page = await browser.newPage();
// Navigate to a website
await page.goto('https://coinmarketcap.com');
// Get the page content
const content = await page.content();
// Display the content
console.log(content);
// Close the browser
await browser.close();
})();
这段代码将启动一个无界面的 Chromium 浏览器,创建一个新页面,导航至CoinMarketCap主页,获取页面内容,记录该内容,然后关闭浏览器。
您可以通过引入相应的模块,并将“chromium”替换为“firefox”或“webkit”,使用类似的代码来启动其他受支持的浏览器(Firefox 和 WebKit)。
查找用于数据提取的元素
既然我们已经为爬虫打好了基础,接下来就从目标网站中提取一些数据吧。在本例中,我们将提取页面上显示的前10种货币的名称和价格。
要查看 CoinMarketCap 主页的 DOM 树,请在浏览器中打开该网站,右键单击某种币种的名称,然后选择“检查”。这将打开开发者工具并显示 DOM 树。

选择合适的选择器本身就是一门艺术。在这种情况下,我们要选择的表格元素就是上下文。该表格有一个名为“cmc-table”的类,这对我们来说非常有用。
要查找货币名称的选择器,我们需要沿着 DOM 树向下遍历。我们要找的元素位于 `.cmc-table tbody tr td:nth-child(3) a .name-area p` 元素内部。
一般而言,在选择元素时应尽可能准确,因为这将最大限度地减少需要处理的错误数量。
使用同样的方法,我们可以找到价格的样式选择器,即 `.cmc-table tbody tr td:nth-child(3) a span`。总结一下,以下是我们的样式选择器列表:
- 名称:`.cmc-table tbody tr td:nth-child(3) a .name-area p`
- 价格:`.cmc-table tbody tr td:nth-child(4) a span`
数据提取
利用我们之前确定的选择器,让我们从网页中提取数据并将其存储在一个列表中。$$eval 辅助函数会返回一个数组,其中包含符合选择器条件的元素,这些元素是在页面 DOM 的上下文中进行评估的。
我们将 console.log(content); 这行代码替换为以下内容:
// Extract the currencies data
const currencies = await page.$$eval('.cmc-table tbody tr:nth-child(-n+10)', trs => {
const data = []
trs.forEach(tr => {
data.push({
name: tr.querySelector('td:nth-child(3) a .name-area p').innerHTML,
price: tr.querySelector('td:nth-child(4) a span').innerHTML,
})
})
return data
})
// Display the results
console.log(currencies)
该代码使用 $$eval 函数选取 .cmc-table 中的前 10 个 tr 元素。随后,它遍历这些元素,使用我们之前确定的选择器选取表格数据单元格,并提取其文本内容。最终,数据以对象数组的形式返回。
您可以在官方文档中进一步了解 $$eval 函数。
完整的代码如下:
const { chromium } = require('playwright');
(async () => {
// Launch a Chromium browser
const browser = await chromium.launch();
// Create a new page in the browser
const page = await browser.newPage();
// Navigate to a website
await page.goto('https://coinmarketcap.com');
// Extract the currencies data
const currencies = await page.$$eval('.cmc-table tbody tr:nth-child(-n+10)', trs => {
const data = []
trs.forEach(tr => {
data.push({
name: tr.querySelector('td:nth-child(3) a .name-area p').innerHTML,
price: tr.querySelector('td:nth-child(4) a span').innerHTML,
})
})
return data
})
// Display the results
console.log(currencies)
// Close the browser
await browser.close();
})();处理动态内容
我们只抓取了前10种货币,因为CoinMarketCap在页面初次加载时只显示这10种。若要抓取更多,我们需要执行一项模拟人类操作——即滚动页面。幸运的是,Playwright非常适合完成这项任务。
首先,让我们重构之前使用的 $$eval 函数,并实现分页功能。我们将这个新函数命名为 extractData:
const extractData = async (page, currentPage, perPage = 10) => {
}
我们通过分段选择(即从 0 到 10、从 11 到 21、从 22 到 32 等)来扩展 :nth-child 选择器。我们定义初始选择器(前 10 个元素):
let selector = `:nth-child(-n+${currentPage * perPage})`;
最后但同样重要的是,我们添加了对下一页的支持。代码如下:
if(currentPage > 1) {
selector = `:nth-child(n+${(currentPage - 1) + perPage}):nth-child(-n+${(currentPage * perPage) + 1})`;
}
最终函数将如下所示:
const extractData = async (page, currentPage, perPage = 10) => {
let selector = `:nth-child(-n+${currentPage * perPage})`;
if(currentPage > 1) {
selector = `:nth-child(n+${(currentPage - 1) + perPage}):nth-child(-n+${(currentPage * perPage) + 1})`;
}
return await page.$$eval(`.cmc-table tbody tr${selector}`, trs => {
const data = [];
trs.forEach(tr => {
data.push({
name: tr.querySelector('td:nth-child(3) a .name-area p').innerHTML,
price: tr.querySelector('td:nth-child(4) a span').innerHTML,
});
});
return data;
});
};
现在是时候回到我们的爬虫代码中,实现滚动功能,并扩展数据提取功能了。我们将在以下这行代码之后进行所有操作:
await page.goto('https://coinmarketcap.com');
我们重新定义了 currencies 变量:
// Extract the currencies data
let currencies = await extractData(page, 1, 10);
利用 evaluate 函数,我们将页面滚动到视口高度的 1.5 倍处。这将触发表格中的后续元素加载:
// Scroll the page to a little more than the viewport height
await page.evaluate(() => {
window.scrollTo(0, window.innerHeight * 1.5);
});
这一秒的等待将为界面留出一些时间,以便将从 API 获取的数据填充到表格中:
// Wait for the new elements to load
await page.waitForTimeout(1000);
最后,让我们从第二页提取数据并记录结果:
// Extract the next 10 elements
currencies = [...currencies, ...await extractData(page, 2, 10)]
// Display the results
console.log(currencies)
该爬虫程序的完整代码应如下所示:
const { chromium } = require('playwright');
const extractData = async (page, currentPage, perPage = 10) => {
let selector = `:nth-child(-n+${currentPage * perPage})`;
if(currentPage > 1) {
selector = `:nth-child(n+${(currentPage - 1) + perPage}):nth-child(-n+${(currentPage * perPage) + 1})`;
}
return await page.$$eval(`.cmc-table tbody tr${selector}`, trs => {
const data = [];
trs.forEach(tr => {
data.push({
name: tr.querySelector('td:nth-child(3) a .name-area p').innerHTML,
price: tr.querySelector('td:nth-child(4) a span').innerHTML,
});
})
return data;
})
};
(async () => {
// Launch a Chromium browser
const browser = await chromium.launch();
// Create a new page in the browser
const page = await browser.newPage();
// Navigate to a website
await page.goto('https://coinmarketcap.com');
// Extract the currencies data
let currencies = await extractData(page, 1, 10)
// Scroll the page to a little more than the viewport height
await page.evaluate(() => {
window.scrollTo(0, window.innerHeight * 1.5);
});
// Wait for the new elements to load
await page.waitForTimeout(1000);
// Extract the next 10 elements
currencies = [...currencies, ...await extractData(page, 2, 10)];
// Display the results
console.log(currencies);
// Close the browser
await browser.close();
})();
简而言之,该爬虫程序将打开CoinMarketCap的首页,提取前10种加密货币的数据,然后滚动页面,提取接下来10种加密货币的数据,并显示结果。
你应该会得到类似于以下的结果:
[
{ name: 'Bitcoin', price: '$16,742.58' },
{ name: 'Ethereum', price: '$1,244.45' },
{ name: 'Tether', price: '$0.9997' },
{ name: 'USD Coin', price: '$1.00' },
{ name: 'BNB', price: '$255.78' },
{ name: 'XRP', price: '$0.335' },
{ name: 'Binance USD', price: '$1.00' },
{ name: 'Dogecoin', price: '$0.07066' },
{ name: 'Cardano', price: '$0.2692' },
{ name: 'Polygon', price: '$0.7762' },
{ name: 'Dai', price: '$0.9994' },
{ name: 'Litecoin', price: '$73.80' },
{ name: 'Polkadot', price: '$4.59' },
{ name: 'Solana', price: '$12.95' },
{ name: 'TRON', price: '$0.0505' },
{ name: 'Shiba Inu', price: '$0.000008234' },
{ name: 'Uniswap', price: '$5.29' },
{ name: 'Avalanche', price: '$11.43' },
{ name: 'UNUS SED LEO', price: '$3.47' },
{ name: 'Wrapped Bitcoin', price: '$16,725.03' },
{ name: 'Cosmos', price: '$9.97' }
]高级技巧
既然我们已经介绍了使用 Playwright 进行网页抓取的基础知识,例如创建抓取器、查找选择器、提取数据以及实现无限滚动,现在是时候深入探讨 Playwright 提供的某些更高级的功能了。
这些方法包括截屏、填写表单、使用 XPath 代替类选择器,以及使用代理服务器绕过 IP 封禁。
截屏
在进行网页抓取时截屏的一大优势在于,它能让你看到网站或网络应用在不同浏览器和分辨率下的显示效果。
这对开发人员尤其有用,他们可以利用截图来排查布局问题,并测试其 Web 应用程序在不同平台上的显示效果。
要截取整页屏幕截图,可以使用 Page 对象的截图方法。以下是一个示例:
const screenshot = await page.screenshot();
这段代码会截取整个页面的屏幕截图,并将其作为 Buffer 返回。`screenshot` 函数支持传入属性。我们可以指定保存屏幕截图的路径,并决定截图应仅包含视口还是整个页面。
这是完整的代码:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://coinmarketcap.com');
// Take a full page screenshot and save the file
await page.screenshot({
path: "screenshot.png",
fullPage: false
});
await browser.close();
})();
你可以将这段代码保存到一个名为 `screenshot.js` 的文件中,并通过命令 `node screenshot.js` 运行该代码。执行后,项目文件夹中将生成一个名为 `screenshot.png` 的文件。
我们可以使用 `clip` 属性截取页面中某个区域的屏幕截图。我们需要定义四个属性:
- x:相对于左上角的水平偏移量
- y:相对于左上角的垂直偏移量
- 宽度:区域宽度
- 高度:区域高度
设置了 clip 属性的截图功能将呈现如下效果:
// Take a screenshot of a part of the page
await page.screenshot({
path: "screenshot.png",
fullPage: false,
clip: {
x: 50,
y: 50,
width: 320,
height: 160
}
});填写表格
使用 Playwright 的一个优势在于,它允许您访问受保护的页面。通过模拟点击按钮、滚动页面和填写表单等人类操作,Playwright 可以绕过登录要求并访问受限内容。
让我们使用 Playwright 访问我们的 Reddit 账户(如果你还没有 Reddit 账户,请立即注册一个!)。
请访问登录页面,并运用我们在“查找用于数据提取的元素”一节中学习到的技巧,提取用户名和密码输入框的类名。
选择器应如下所示:
- 用户名输入:`#loginUsername`
- 密码输入:`#loginPassword`
- 提交按钮:`button[type="submit"]`
让我们在代码中使用这些选择器。创建一个名为 `login.js` 的文件,并将以下代码粘贴进去:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://reddit.com/login');
// Fill up the form
await page.fill('#loginUsername', "YOUR_REDDIT_USERNAME");
await page.fill('#loginPassword', "YOUR_REDDIT_PASSWORD");
// Click the submit button
await page.click('button[type="submit"]');
// Wait for the new page to load
await page.waitForNavigation()
// Take a screenshot of the new page
await page.screenshot({
path: "reddit.png",
fullPage: false
});
// Close the browser
await browser.close();
})();
要运行代码并截取受保护页面的屏幕截图,只需在终端中输入 node login.js。几秒钟后,项目目录中就会出现一个名为 reddit.png 的屏幕截图。
很遗憾,我无法把生成的截图分享给你们,因为我的Reddit账号里大部分内容都不适合在工作场所查看。
使用 XPath 进行数据提取
对于网页抓取而言,XPath 可能是一个更稳妥的选择,因为它比类名更不容易发生变化。像 Reddit 和 Google 这样的网站经常更新其类名,这可能会给依赖这些类名的抓取工具带来问题。
相比之下,XPath 表达式基于 HTML 文档的结构,因此变更的可能性较小。这意味着即使在经常更新类名的网站上,您也可以通过 XPath 以更可靠、更稳定的方式识别元素。
因此,使用 XPath 可以帮助您构建一个更强大、更具弹性的网页抓取工具,使其在网站更新时不易出现故障。
本文仅对 XPath 的应用进行了浅显的介绍。如需更全面的指南,请参阅这篇文章。
让我们回到 Reddit,打开一个子版块,然后打开开发者工具。我将使用/r/webscraping子版块,但你可以选择任何你喜欢的子版块。

如果你检查 DOM 树,可能会发现元素的类名似乎是随机生成的。使用这些类名会降低爬虫的可靠性,并且需要持续维护。
右键单击 `h3` 标签,选择“复制”,然后选择 `复制 XPath`。结果应如下所示:
//*[@id="t3_104ocrd"]/div[3]/div[2]/div[1]/a/div/h3
在应用这条规则之前,让我们先移除 @id 部分,这样路径就能涵盖所有文章,而不仅仅是第一篇。
是时候动手实践了。创建一个名为 `xpath.js` 的文件,并将以下代码粘贴进去:
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://reddit.com/r/webscraping');
const titles = await page.$$eval("//*/div[3]/div[2]/div[1]/a/div/h3", element => {
const data = []
element.forEach(el => {
data.push(el.innerHTML)
})
return data
})
console.log(titles)
// Close the browser
await browser.close();
})();
要运行该代码,请使用 `node xpath.js` 命令。这将返回你所选子版块中的一系列标题。例如,我的列表如下所示:
[
“回顾一年来的网络爬虫写作”,
“LinkedIn评论爬虫——用于从LinkedIn帖子的URL中爬取评论(包括姓名、头像、职位、邮箱(如有)和评论内容)的脚本”,
“最好的代理列表?”,
'抱歉如果发错了子版块,但能否从旧的 YouTube 账号中提取邮箱?',
'寻找出售数据集的人!',
“Reddit 的分类名称会定期变化吗(那些由随机字母和数字组成的)”,
“从多个网站抓取视频”,
“有人愿意帮我写个程序,自动生成杂志订阅的 PDF 文件吗?”,
“使用 rSelenium 抓取动态网页”,
“有没有办法将 LinkedIn Sales Navigator 的链接转换为 LinkedIn 的公开链接?”,
“Gurufocus / 是否已实施新的 Cloudflare 防护措施?”,
“用于从 Web3 前端收集 APY 数据的抓取工具”
]
您可以在 Playwright 的官方文档中了解更多关于 XPath 的信息。文档链接在此。
剧作家 vs 其他人
Playwright、Puppeteer 和 Selenium WebDriver 都是可用于网页抓取和自动化的工具。
由微软开发的Playwright是一款较新的工具,旨在成为网络自动化的“完整”解决方案。它支持多种浏览器(Chromium、Firefox 和 WebKit)以及多种编程语言(JavaScript、TypeScript、Python 和 C#)。
Puppeteer 是由 Google 开发的一款基于 Chrome DevTools 协议的网络爬虫和自动化工具。它主要与 JavaScript 配合使用,具备多种功能,包括截屏、生成 PDF 文件以及与 DOM 进行交互。
Selenium WebDriver 是一款用于 Web 自动化和测试的工具,支持多种编程语言和浏览器。它专为测试而设计,因此可能比其他一些工具需要更多的配置。
性能对比
在选择网页抓取工具时,性能是一个重要的考量因素。在本节中,我们将对比 Playwright、Puppeteer 和 Selenium WebDriver 的性能,以确定哪款工具速度最快、效率最高。

在测试过程中,我们发现Playwright和Puppeteer在执行网页抓取和自动化任务时的响应时间几乎完全相同。然而,与其他两款工具相比,Selenium WebDriver的启动速度明显较慢。
结论
网络爬虫技术能够从网站中提取和处理数据,为企业和个人提供了丰富的信息与机遇。网络爬虫技术的主要优势包括:
- 节省成本:网络爬虫是一种经济高效的数据采集方式,因为它能帮助您避免购买昂贵的数据库或API。
- 时间效率:与手动收集相比,抓取大量数据的速度要快得多。
- 最新数据:通过定期抓取和刷新数据源,网络爬虫可帮助您保持数据的最新状态。
Playwright 是一款功能强大的网络爬虫工具,其丰富的功能使其成为众多用户的首选。使用 Playwright 的优势包括:
- 与 Chromium 的兼容性:Playwright 基于 Chromium 构建,这是一个为 Google Chrome 提供支持的开源浏览器项目,因此您可以使用最新的 Web 平台功能,并能与各类网站广泛兼容。
- 跨浏览器支持:Playwright 支持在多个浏览器(包括 Chrome、Firefox 和 Safari)上自动化执行任务。
- 易用性:Playwright 拥有简单直观的 API,让您能够轻松上手网页抓取和自动化任务。
- 多功能性:Playwright 可用于多种任务,包括网页抓取、测试和自动化。
总体而言,网络爬取带来的好处以及使用 Playwright 的优势,使其成为任何希望从网络中提取和处理数据的人不可或缺的工具。
如果您正在寻找一种更简便的网络爬虫解决方案,不妨考虑使用 WebScrapingAPI。我们的服务让您能够从任何网站抓取数据,而无需处理配置和维护网络爬虫的复杂流程。
使用 WebScrapingAPI,您只需向我们的 API 发送一个包含目标网站 URL 的 HTTP 请求,我们就会将数据以您指定的格式(JSON、HTML、PNG)返回给您。
我们的 API 会为您处理所有繁重的工作,包括绕过验证码、处理 IP 封禁以及处理动态内容。
既然只需通过几个简单的API调用就能使用WebScrapingAPI获取所需数据,何必浪费时间和资源去开发和维护自己的网页抓取工具呢?快来试用一下, 看看我们如何帮助您简化网页抓取流程。




