返回博客
指南
Mihnea-Octavian ManolacheLast updated on Mar 31, 20262 min read

如何使用 Python 运行无头浏览器进行网页抓取:技巧与窍门

如何使用 Python 运行无头浏览器进行网页抓取:技巧与窍门

在网页抓取中,使用 Python 配合 Selenium 运行无头浏览器几乎已成为常态。但无头浏览器究竟是什么?哪种无头浏览器最适合 Selenium?既然有 `requests` 库,为什么还要在 Python 中使用无头浏览器?围绕这个话题的问题确实很多。这也意味着,我们将共同探索许多答案。但在深入探讨之前,让我们先明确一些学习目标。 读完本文后,您将能够:

  • 理解什么是无头浏览器及其应用场景
  • 掌握在 Python 中启动无头浏览器的方法
  • 使用 Python 和 Selenium 创建网页爬虫

最后,我们还将探讨 Python 无头浏览器的替代方案。尽管本文重点在于 Python,但我的目标是寻找最佳的爬取解决方案,这包括响应时间、资源消耗等。那么,事不宜迟,让我们直接进入正题吧!

什么是 Python 无头浏览器?

从宏观层面来看,浏览器是一种允许用户浏览网页并与之交互的计算机程序。无头浏览器正是如此,只不过它没有图形用户界面。这意味着,Python 无头浏览器是一个能够:

  • 访问互联网上的任何网站
  • 渲染网站提供的 JavaScript 文件
  • 与该网页的组件进行交互

考虑到它没有关联的图形用户界面,人们可能会对交互方式产生疑问。但答案其实很简单。正因为没有图形用户界面,人类无法直接与网页交互。而这就是 Web 驱动程序发挥作用的地方。Web 驱动程序是一种允许进行内省和控制的接口。简而言之,Web 驱动程序是一组框架,允许我们通过编程方式控制各种网页浏览器。

Python中有几个支持浏览器自动化的框架,但主要的是Selenium。Selenium是一套主要用于自动化测试的工具集,但在实际应用中,它也被广泛用于网页抓取。

为何要在 Python 中使用无头浏览器?

根据 Selenium 主页的标题:

“Selenium 实现浏览器自动化。仅此而已!如何运用这种能力,完全取决于你。”

这让我们相信,自动化浏览器拥有多种应用场景。但为何要以无头模式运行它们呢?答案依然很简单。与有头浏览器相比,Python 无头浏览器消耗的资源(CPU 和内存)更少。这主要是因为无需渲染图形元素。

然而,与 Python 的 `requests` 这样的基础 HTTP 客户端相比,少即是多。这是因为无头浏览器仍会开启大量进程来与页面交互并渲染 JavaScript 文件。众所周知,`requests` 无法渲染 JavaScript,只能用于获取原始 HTML。而在当今时代,这对于网页抓取来说远远不够。 大多数现代 Web 平台高度依赖 JavaScript 来构建 DOM。例如,如果你尝试使用 `curl` 抓取一个 React 应用,你会得到一个空白的网页,提示你“启用 JavaScript”:

<!doctype html>

<html lang="en">

  <head>

 	...

  </head>

  <body>

 	<noscript> You need to enable JavaScript to run this app. </noscript>

 	<div id="root"></div>

  </body>

</html>

虽然 `requests` 无法实现这一点,但无头浏览器可以。这解答了我们最初提出的问题之一。现代网页爬虫之所以使用无头浏览器而非 `requests`,是因为否则响应结果将无法确定。

无头浏览器有哪些缺点?

Python 无头浏览器(以及绝大多数自动化浏览器)的主要缺点在于其指纹特征。如果你关注我的文章,就会知道我有时会提到“隐蔽性”。这指的是自动化浏览器不被察觉的能力。

而在 Python 中,无头浏览器很容易被识别出来。首先,检查浏览器的某个简单属性(如 `navigator.webdriver`)就能立即判断出该浏览器是否由 WebDriver 控制。在网页抓取中,主要挑战之一就是寻找避免被检测的方法。我们称这些方法或技巧为“规避策略”。你可以在这里阅读更多相关内容。

以 Web Scraping API 为例,我们有一支专门的团队持续致力于完善我们的隐身模式。这是为了确保我们的浏览器指纹在每次请求中都是独一无二且无法被检测到的。

支持 Python Selenium 的无头浏览器

首先,您需要了解 Selenium 功能非常强大。而且它不仅限于 Python。C#、Ruby、Java、Python 甚至 JavaScript 都有相应的 Selenium 客户端和 WebDriver。Selenium WebDriver 的兼容性更是令人印象深刻。它支持所有主流浏览器:

在网页抓取领域,最常用的 Python 无头浏览器是 Chrome 和 Firefox。我认为这主要是因为这两款浏览器既性能出色,又具备跨平台特性。例如,您可以在 MacOS 环境中开发网页抓取项目,然后轻松将其部署到 Linux 系统上。

如何在 Python 中打开无头浏览器

既然我们已经介绍了些理论概念,现在可以放心地进入实践部分了。在本节中,我将向您展示如何使用 Selenium 构建一个网页爬虫。进行此项目时,请确保您的机器已安装 Python 和 Chrome。

#1:配置环境

照例,在 Python 中我们应将所有内容封装在虚拟环境内。如果您不熟悉虚拟环境,请先阅读这篇文章。现在,让我们打开一个新的终端窗口,并执行以下操作:

  • 创建一个新文件夹
  • 进入该文件夹
  • 创建新的虚拟环境
  • 激活虚拟环境

~ mkdir headless_scraper

~ cd headless_scraper

~ python3 -m venv env

~ source env/bin/activate

#2:安装依赖项

很明显,我们的项目需要 Selenium 和一个 Web 驱动程序。幸运的是,我们可以使用 Python 的包管理器 `pip` 安装这两者。在同一个终端窗口中,输入以下命令:

~ pip install selenium webdriver-manager

现在一切准备就绪!我们可以开始实际编码了。先说明一下,本文主要关注与无头浏览器的交互。完整的网页抓取解决方案需要付出更多努力。但我相信,只要您关注我们的博客文章,很快就能掌握。

#3:启动自动化浏览器

目前,我们已经有了项目,但还没有可执行的文件。让我们创建一个新的 `.py` 文件,并在 IDE 中打开它:

~ touch headles_scraper.py

~ code .

此时你应该已经进入 Visual Studio Code 或你的 IDE 了。你可以从在 `handle_scraper.py` 中导入必要的包开始:

from selenium import webdriver

from webdriver_manager.chrome import ChromeDriverManager

该包可帮助你轻松管理 Selenium 支持的各类浏览器的 WebDriver。你可以在此处阅读更多相关信息。接下来,我们需要使用 Selenium 创建一个新浏览器并打开一个网站:

driver = webdriver.Chrome(ChromeDriverManager().install())

driver.get('https://webscrapingapi.com')

现在运行此文件,你会发现它确实有效。但它并非使用 Python 无头浏览器,而是打开了一个带界面的 Chrome 窗口:

#4:实现无头模式

我们的目标是构建一个资源友好的网页爬虫。因此,理想情况下,我们希望使用 Selenium 打开无头浏览器。幸运的是,有一种简单的方法可以将 Selenium 从有头模式切换为无头模式。我们只需利用 Chrome WebDriver 的 `Options` 即可。因此,让我们导入 `Options` 并添加两行代码:

...

from selenium.webdriver.chrome.options import Options

...

options = Options()

options.headless = True

driver = webdriver.Chrome(ChromeDriverManager().install(), options=options)

driver.get('https://webscrapingapi.com')

再次运行脚本。如您所见,这次没有弹出窗口。但它真的在后台运行吗?一个快速可视化并验证的方法是使用 Selenium 截屏。只需在脚本末尾添加这一行:

driver.get_screenshot_as_file('headless.png')

如果一切顺利,你应该会得到和我一样的图片:

#5:添加数据抓取功能

什么是网页抓取工具?简而言之,网页抓取工具本质上是一个通过调用服务器端点并从中收集数据的程序。对于网站而言,这些数据通常由 HTML 文件组成。但如今某些服务器也会提供 JSON 对象。因此,我们统一使用“数据”这一术语。在接下来的部分中,让我们设定更高的目标。让我们尝试使用面向对象编程!我们的目标是:

  • 创建一个 Scraper 类
  • 添加一个提取原始数据的方法
  • 添加一个从单个元素中提取数据的方法
  • 添加一个从同类元素中提取数据的方法

因此,我们需要构建三个基本方法。但就学习而言,这三个方法不仅为网页抓取铺平了道路,也为 Python 中的面向对象编程(OOP)打开了大门。我觉得这非常酷!现在,让我们删除之前编写的所有代码,从头开始:

from selenium import webdriver

from selenium.webdriver.chrome.options import Options

from selenium.webdriver.common.by import By

from selenium.webdriver.remote.webelement import WebElement

class Scraper:

   def __init__(self, headless: bool = True) -> None:

       self.headless = headless

       pass

   def setup_scraper(self) -> None:

       self.options = Options()

       self.options.headless = self.headless

       self.driver = webdriver.Chrome(options=self.options)

   def navigate(self, target) -> None:

       self.driver.get(target) if target else print('[!] No target given. Please specify a URL.')

   def extract_raw_data(self) -> str:

       return self.driver.page_source

   def extract_single_element(self,  selector: str, selector_type: By = By.CSS_SELECTOR) -> WebElement:

      return self.driver.find_element(selector_type, selector)

  

   def extract_all_elements(self, selector: str, selector_type: By = By.CSS_SELECTOR) -> list[WebElement]:

       return self.driver.find_elements(selector_type, selector)

我添加了类型注解,主要是为了便于理解,而非提升性能。这样,你就能从输入/输出的角度直观地理解这个程序。现在,这些方法基本上不言自明。我们并没有对数据执行任何操作,只是将其返回。如果你愿意,这可以作为你构建一个使用 Python 无头浏览器的复杂爬虫的起点。

目前,执行该文件不会产生任何效果。这是因为我们仅声明了 Scraper 及其方法,现在需要实际调用它们。因此,让我们添加以下代码:

# Initialize a new Scraper and navigate to a target

scraper = Scraper()

scraper.setup_scraper()

scraper.navigate('https://httpbin.org')

# Extract and print the entire HTML document

raw_data = scraper.extract_raw_data()

print(raw_data)

# Extract and print an element by its class name

single_element = scraper.extract_single_element('title', By.CLASS_NAME)

print(single_element.text)

# Extract and print all elements belonging to a tag type

all_elements = scraper.extract_all_elements('a', By.TAG_NAME)

print([el.get_attribute('href') for el in all_elements])

就这样。如果你现在运行脚本,就能看到一些操作发生了。再次强调,这仅仅是一个旨在帮助你入门的原型。如果你想进一步了解如何在网页抓取中使用 Python 无头浏览器,我建议你:

这样,你既能积累知识,又能为个人作品集增添一个项目。

Python 无头浏览器的最佳替代方案有哪些?

Python 是构建网络爬虫最流行的编程语言之一。但它并非唯一的选择,也并非最佳方案!在本节中,我们将探讨 Python 无头浏览器的替代方案。我们将首先探讨为何需要寻找替代方案,并分析具体示例。

选择替代方案而非自行构建 Python 网络爬虫的主要原因在于资源消耗。一套完整的网络爬取解决方案需要您实现 IP 轮换系统、一些规避检测技术、考虑性能问题,这些还只是其中的一部分。因此,构建网络爬虫不仅成本高昂,而且耗时费力。更不用说维护基础设施还会产生更多成本。

Python 无头浏览器的第二个缺点与性能有关。虽然 Python 是一门优秀的语言且非常用户友好,但它并不以速度见长。例如,与同样拥有 Selenium 包的 Java 不同,Python 既是动态类型语言,又是解释型语言。这两项特性使其与其他语言相比运行速度明显较慢。现在我们已经有了大致的了解,让我们具体探讨一下。 以下是 Selenium 和 Python 无头浏览器的五大替代方案:

#1:Web Scraping API

若想解决我们指出的第一个缺陷,您需要考虑第三方抓取服务提供商。Web Scraping API 提供了一套完整的抓取解决方案。此外,我们的服务还包含以下功能:

  • 适用于数据中心代理和住宅代理的 IP 轮换系统
  • 隐身模式
  • 验证码破解器

仅凭这三项功能,目标网站几乎不可能追踪并封锁我们的爬虫。此外还有丰富的爬取功能。借助 Web Scraping API,您可以基于选择器提取数据、切换设备类型、截取屏幕截图等。完整功能列表请参见此处

#2: Puppeteer

Puppeteer 相当于 JavaScript 领域的 Selenium,是 Web 自动化领域使用最广泛的库之一。与 Selenium 不同,Puppeteer 的默认运行模式即为无头模式,因此您无需额外编写代码即可实现无头操作。 更有趣的是,Puppeteer 还提供了 Python 版本的 API 实现。您可以阅读这篇博客,其中我详细介绍了如何使用 Pyppeteer 构建网页爬虫。

#3: Playwright

Playwright 是另一款由微软贡献者开发的网页自动化工具。它之所以广受欢迎,主要是因为它支持多种语言和平台。他们的口号实际上是“任何浏览器,任何平台”。其 API 可在任何操作系统上通过以下任一语言调用:

这些是 Python 无头浏览器的主要替代方案。但还有其他工具可供选择。ZombieJSHtmlUnit 只是众多选项中的两个。我认为选择哪种技术既取决于性能,也取决于个人偏好。因此,我建议您测试所有选项,然后挑选最中意的那个。

结论

使用 Python 无头浏览器有利有弊。一方面,您可以构建一个自定义解决方案,并随时为其添加更多功能;另一方面,开发和维护成本可能相当高昂。此外还有隐蔽性问题。如果您需要专业的解决方案,我认为最好选择第三方供应商。否则,如果是出于学习目的,我始终鼓励您尝试探索这项技术。

关于作者
Mihnea-Octavian Manolache, 全栈开发工程师 @ WebScrapingAPI
Mihnea-Octavian Manolache全栈开发工程师

Mihnea-Octavian Manolache 是 WebScrapingAPI 的全栈及 DevOps 工程师,负责开发产品功能并维护确保平台平稳运行的基础设施。

开始构建

准备好扩展您的数据收集规模了吗?

加入2,000多家企业,使用WebScrapingAPI在无需任何基础设施开销的情况下,以企业级规模提取网络数据。