如何使用Playwright阻止网页抓取中的某些资源
您是否知道 Playwright 允许您阻止请求,从而加快抓取或测试速度?您可以阻止某些资源类型,例如图像、域的任何请求或许多不同的方式。
先决条件
为了使代码正常工作,您需要安装 python3。有些系统已经预装了它。之后,安装 Playwright 以及 Chromium、Firefox 和 WebKit 的浏览器二进制文件。
pip install playwright playwright install
Playwright简介
Playwright “是一个 Python 库,可通过单个 API 实现 Chromium、Firefox 和 WebKit 浏览器的自动化。” 它允许我们以编程方式使用无头浏览器浏览互联网。
Playwright 也可用于 Node.js,下面显示的所有内容都可以使用类似的语法完成。检查文档以获取更多详细信息。
以下是如何用几行代码启动浏览器(即 Chromium)、导航到页面并获取其标题。
from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch() page = browser.new_page() page.goto("https://www.zenrows.com/") print(page.title()) # Web Scraping API & Data Extraction - ZenRows page.context.close() browser.close()
记录网络事件
订阅请求或响应等事件并记录其内容以查看发生了什么。由于我们没有告诉 Playwright 否则,它将加载整个页面:HTML、CSS、执行 Javascript、获取图像等。在请求页面查看发生了什么之前添加这两行。
page.on("request", lambda request: print( ">>", request.method, request.url, request.resource_type)) page.on("response", lambda response: print( "<<", response.status, response.url)) page.goto("https://www.zenrows.com/") # >> GET https://www.zenrows.com/ document # << 200 https://www.zenrows.com/ # >> GET https://cdn.zenrows.com/images/home/header.png image # << 200 https://cdn.zenrows.com/images/home/header.png # ... and many more
整个输出几乎有 50 行长,有 24 个资源请求。我们可能不需要其中的大部分内容来进行网站抓取,因此我们将了解如何阻止它们并节省时间和带宽。
阻塞资源
为什么要加载我们不会使用的资源和内容?了解如何使用这些技术避免不必要的数据和网络请求。
按 Glob 模式阻止
page
还公开了一个方法route,该方法将为每个匹配的路由或模式执行处理程序。假设我们不想加载 SVG。使用类似的模式
"**/*.svg"
将匹配以该扩展名结尾的请求。至于处理程序,我们暂时不需要任何逻辑,只需中止请求即可。为此,我们将使用 lambda 和route
参数的 abort 方法。
page.route("**/*.svg", lambda route: route.abort()) page.goto("https://www.zenrows.com/")
注意:根据官方文档,类似的模式"**/*.{png,jpg,jpeg}"
应该有效,但我们发现并非如此。不管怎样,用下一个拦截策略是可行的。
通过正则表达式阻止
如果(出于某种原因😜)您喜欢正则表达式,请随意使用它们。但首先编译它们是强制性的。在这种情况下,我们将阻止三个图像扩展。正则表达式很棘手,但它们提供了很大的灵活性。
import re # ... page.route(re.compile(r".(jpg|png|svg)$"), lambda route: route.abort()) page.goto("https://www.zenrows.com/")
现在有 23 个请求,但只有 15 个响应。我们节省了 8 张图片的下载!
按资源类型阻止
但是如果他们使用“jpeg”扩展名而不是“jpg”会发生什么?或者 avif、gif、webp?我们应该维护更新的列表吗?
幸运的是,route
上面的 lambda 函数中公开的参数包括原始请求和资源类型。其中一种类型是image
,完美!您可以访问整个资源类型列表。
现在,我们将匹配每个请求 ( "**/*"
) 并向 lambda 函数添加条件逻辑。如果是图像,则像以前一样中止请求。否则,继续照常进行。
page.route("**/*", lambda route: route.abort() if route.request.resource_type == "image" else route.continue_() ) page.goto("https://www.zenrows.com/")
考虑到一些跟踪器使用图像。在抓取或测试时这可能不是什么大问题,但以防万一。
函数处理程序
我们还可以为处理程序定义函数,而不是使用 lambda。如果我们需要重用它或者它超出了单个条件,这会派上用场。
假设我们现在想要积极阻止。查看之前运行的输出将显示已使用资源的列表。我们将它们添加到列表中,然后检查该类型是否在该列表中。
excluded_resource_types = ["stylesheet", "script", "image", "font"] def block_aggressively(route): if (route.request.resource_type in excluded_resource_types): route.abort() else: route.continue_() # ... page.route("**/*", block_aggressively) page.goto("https://www.zenrows.com/")
我们现在完全掌控一切,而且多功能性是绝对的。从routes.request
,可以获取原始 URL、标头和其他一些信息。
更严格的是:阻止所有非document
类型的内容。这将有效地阻止加载除初始 HTML 之外的任何内容。
def block_aggressively(route): if (route.request.resource_type != "document"): route.abort() else: route.continue_()
现在只有一个回复!我们获得了 HTML,无需下载任何其他资源
。我们确实节省了大量时间和带宽,对吗?但是……具体是多少?
衡量性能提升
我们只能说,如果我们能够衡量差异,情况就会变得更好。我们将看看三种方法。但只需运行具有多个 URL 的脚本即可。剧透:我们对 10 个 URL 执行了此操作,时间为 1.3 秒 VS 8.4 秒。
哈尔文件
对于那些习惯检查 DevTools Network 选项卡的人来说,我们有好消息!Playwright 通过在方法中提供额外参数来允许 HAR 录制new_page
。就这么简单。
page = browser.new_page(record_har_path="playwright_test.har") page.goto("https://www.zenrows.com/")
有一些HAR 可视化工具,但最简单的方法是使用 Chrome DevTools。打开“网络”选项卡,然后单击导入按钮或拖放 HAR 文件。