在 Python 中使用 HTTPX 进行网页抓取

了解如何使用功能强大的 Python HTTP 客户端 HTTPX 进行网页抓取。学习它的相关设置、功能、高级使用技巧及其与 Requests 之间的异同。
5 min read
使用 HTTPX 和 Python 进行网络抓取

您将通过本文学到以下知识:

  • HTTPX 库简介及其功能
  • 通过指南掌握使用 HTTPX 进行网页抓取的具体操作
  • 用于网页抓取的高级 HTTPX 功能
  • 比较 HTTPX 与 Requests在自动请求方面的差异

现在就来一探究竟吧!

什么是 HTTPX?

HTTPX 是一款基于 retryablehttp 库构建的 Python 3 HTTP 客户端,功能非常齐全。即使在多线程情况下,其也能确保获得可靠的结果。HTTPX 提供同步和异步 API,支持 HTTP/1.1 和 HTTP/2 协议。

⚙️ 功能

  • 简单和模块化的代码库,易于贡献代码。
  • 快速且完全可配置的参数标志,支持探测多个元素。
  • 支持多种基于 HTTP 的探测方法。
  • 默认情况下,可智能自动地从 HTTPS 回退到 HTTP。
  • 接受主机、URL 和 CIDR 作为输入。
  • 支持代理、自定义 HTTP 标头、自定义超时、基本身份验证等。

👍 优点

  • 可通过 httpx[cli] 命令行直接使用。
  • 功能丰富,例如支持 HTTP/2、提供异步 API 等。
  • 该项目仍处于积极开发中……

👎 缺点

  • ……更新频繁,可能会在新版本中引入破坏性更改。
  • 不如 Requests 库受欢迎。

使用 HTTPX 进行抓取:分步指南

HTTPX 是可用于获取网页原始 HTML 内容的 HTTP 客户端。不过,在获取内容后,您还需使用 BeautifulSoup 之类的 HTML 解析器,以解析 HTML 内容并提取相关数据。

事实上,HTTPX 并非普通的 HTTP 客户端,而是用于网页抓取的最佳 Python HTTP 客户端之一。

请按照本教程进行操作,学习如何使用 HTTPX 和 BeautifulSoup 进行网页抓取!

警告:虽然人们仅在抓取流程的早期阶段使用 HTTPX,但本教程将带您了解整个工作流程。如您想要了解更高级的 HTTPX 抓取技巧,可直接跳至步骤 3 后的内容。

步骤 #1:项目设置

在开始之前,请确保您的电脑已安装 Python 3 或更高版本。如未安装,请先从官方网站下载软件,并按照说明进行安装。

现在,请使用下方命令为您的 HTTPX 抓取项目创建目录:

mkdir httpx-scraper

导航至该目录,并在其中初始化虚拟环境

cd httpx-scraper
python -m venv env

在您喜欢的 Python IDE 中打开项目文件夹。带 Python 扩展的 Visual Studio Code 或 PyCharm Community Edition 都是不错的选择。

接着,在项目文件夹中创建 scraper.py 文件。scraper.py 目前仍是空的 Python 脚本,但其很快就会包含抓取逻辑。

在您的 IDE 终端中,激活虚拟环境。如使用的是 Linux 或 macOS 系统,则运行以下命令:

./env/bin/activate

如在 Windows 系统上,则运行下方命令:

env/Scripts/activate

太棒了!您现在已完成项目设置。

步骤 #2:安装抓取库

在已激活的虚拟环境中,使用下方命令安装 HTTPX 和 BeautifulSoup:

pip install httpx beautifulsoup4

这可以将 httpx 和 beautifulsoup4 都添加至项目的依赖项中。

将它们都导入至您的 scraper.py 脚本:

import httpx
from bs4 import BeautifulSoup

非常好!现在可以进入抓取工作流程中的下一步。

步骤 #3:获取目标网页的 HTML

在本示例中,目标网页为 “Quotes to Scrape” 网站:

Quotes To Scrape 主页

使用 HTTPX 的 get() 方法获取该网站主页的 HTML 内容:

# Make an HTTP GET request to the target page
response = httpx.get("http://quotes.toscrape.com")

HTTPX 会在后台向服务器发出 HTTP GET 请求,服务器将予以响应,返回目标网页的 HTML 内容。您可通过 response.text 属性访问 HTML 内容:

html = response.text
print(html)

这将打印网页的原始 HTML 内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Quotes to Scrape</title>
    <link rel="stylesheet" href="/static/bootstrap.min.css">
    <link rel="stylesheet" href="/static/main.css">
</head>
<body>
    <!-- omitted for brevity... -->
</body>
</html>

很好!现在可以解析这些内容并提取所需数据了。

步骤 #4:解析 HTML 内容

将 HTML 内容传递给 BeautifulSoup 构造函数,以进行解析:

# Parse the HTML content using
BeautifulSoup soup = BeautifulSoup(html, "html.parser")

html.parser 是用于解析内容的标准 Python HTML 解析器。

现在,soup 变量中保存了已解析的 HTML 内容,并提供提取所需数据的方法。

HTTPX 已完成 HTML 内容获取工作,您现在可使用 BeautifulSoup 进行传统的数据解析操作。如想了解更多信息,请参阅有关使用 BeautifulSoup 进行网页抓取的教程。

步骤 #5:从中抓取数据

您可使用下方代码从目标网页抓取相关引言 (quotes) 数据:

# Where to store the scraped data
quotes = []

# Extract all quotes from the page
quote_elements = soup.find_all("div", class_="quote")

# Loop through quotes and extract text, author, and tags
for quote_element in quote_elements:
    text = quote_element.find("span", class_="text").get_text().get_text().replace("“", "").replace("”", "")
    author = quote_element.find("small", class_="author")
    tags = [tag.get_text() for tag in quote_element.find_all("a", class_="tag")]

    # Store the scraped data
    quotes.append({
        "text": text,
        "author": author,
        "tags": tags
    })

此代码片段定义了一个名为 quotes 的列表,用于存储抓取的数据。然后,它会选择并遍历所有的引言 HTML 元素,以提取引言文本、作者和标签。每一条被提取出来的引言都会以字典形式存储在 quotes 列表,从而让数据变得有序、结构化,便于后续使用或导出。

很好!现在已实现抓取逻辑。

步骤 #6:导出抓取的数据

使用以下逻辑将抓取的数据导出为 CSV 文件:

# Specify the file name for export
with open("quotes.csv", mode="w", newline="", encoding="utf-8") as file:
    writer = csv.DictWriter(file, fieldnames=["text", "author", "tags"])

    # Write the header row
    writer.writeheader()

    # Write the scraped quotes data
    writer.writerows(quotes)

通过此代码片段,系统会以写入模式打开名为 quotes.csv 的文件,定义列标头(textauthortags),并将标头写入文件,然后将 quotes 列表中的每个字典写入 CSV 文件。其中的 csv.DictWriter 用于处理格式问题,从而让您可以轻松存储结构化数据。

千万别忘记从 Python 标准库中导入 csv

import csv

步骤 #7:整合所有代码

您的最终 HTTPX 网页抓取脚本将包含以下代码:

import httpx
from bs4 import BeautifulSoup
import csv

# Make an HTTP GET request to the target page
response = httpx.get("http://quotes.toscrape.com")

# Access the HTML of the target page
html = response.text

# Parse the HTML content using BeautifulSoup
soup = BeautifulSoup(html, "html.parser")

# Where to store the scraped data
quotes = []

# Extract all quotes from the page
quote_elements = soup.find_all("div", class_="quote")

# Loop through quotes and extract text, author, and tags
for quote_element in quote_elements:
    text = quote_element.find("span", class_="text").get_text().replace("“", "").replace("”", "")
    author = quote_element.find("small", class_="author").get_text()
    tags = [tag.get_text() for tag in quote_element.find_all("a", class_="tag")]

    # Store the scraped data
    quotes.append({
        "text": text,
        "author": author,
        "tags": tags
    })

# Specify the file name for export
with open("quotes.csv", mode="w", newline="", encoding="utf-8") as file:
    writer = csv.DictWriter(file, fieldnames=["text", "author", "tags"])

    # Write the header row
    writer.writeheader()

    # Write the scraped quotes data
    writer.writerows(quotes)

使用以下命令执行脚本:

python scraper.py

或者,如使用的是 Linux/macOS 系统,则通过下方命令执行脚本:

python3 scraper.py

您的项目根文件夹中将出现 quotes.csv 文件。打开此文件后,您会看到:

包含抓取数据的 CSV 文件

大功告成!您已了解如何使用 HTTPX 和 BeautifulSoup 进行网页抓取。

HTTPX 网页抓取高级功能和技巧

您现在已知道使用 HTTPX 进行网页抓取的基本操作,现在可以学习在更复杂的使用场景中使用 HTTPX 的技巧了。

在下方示例中,目标网站是 HTTPBin.io /anything 端点。这是一个特殊的 API,可返回调用者发送的 IP 地址、标头和其他信息。

熟练掌握 HTTPX 网页抓取技巧!

设置自定义标头

HTTPX 允许通过 headers 实参来指定自定义标头

import httpx

# Custom headers for the request
headers = {
    "accept": "application/json",
    "accept-language": "en-US,en;q=0.9,fr-FR;q=0.8,fr;q=0.7,es-US;q=0.6,es;q=0.5,it-IT;q=0.4,it;q=0.3"
}

# Make a GET request with custom headers
response = httpx.get("https://httpbin.io/anything", headers=headers)
# Handle the response...

设置自定义用户代理

User-Agent 是最重要的,用于网页抓取的 HTTP 标头之一。默认情况下,HTTPX 使用以下 User-Agent

python-httpx/<VERSION>

但目标网站很容易通过该值发现您的请求是自动发出的,从而可能将其屏蔽。

如下所示,为防止出现这种情况,您可对 User-Agent 进行自定义设置,以模拟真实的浏览器:

import httpx

# Define a custom User-Agent
headers = {
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36"
}

# Make a GET request with the custom User-Agent
response = httpx.get("https://httpbin.io/anything", headers=headers)
# Handle the response...

了解最适合进行网页抓取的用户代理

设置 Cookie

与 HTTP 标头一样,您可使用 cookies 实参在 HTTPX 中设置 Cookie:

import httpx

# Define cookies as a dictionary
cookies = {
    "session_id": "3126hdsab161hdabg47adgb",
    "user_preferences": "dark_mode=true"
}

# Make a GET request with custom cookies
response = httpx.get("https://httpbin.io/anything", cookies=cookies)
# Handle the response...

这样,您就可以将网页抓取请求所需的会话数据都包含其中。

代理集成

您可通过代理路由 HTTPX 请求,以保护您的身份,防止在进行网页抓取时 IP 被封禁。使用 proxies 实参即可做到这一点:

import httpx

# Replace with the URL of your proxy server
proxy = "<YOUR_PROXY_URL>"

# Make a GET request through a proxy server
response = httpx.get("https://httpbin.io/anything", proxy=proxy)
# Handle the response...

如想了解详情,请参阅有关如何通过代理使用 HTTPX 的指南。

错误处理

默认情况下,HTTPX 仅在出现连接或网络问题时才会报错。如需要对状态码为 4xx 和 5xx 的 HTTP 异常响应报错,请使用 raise_for_status() 方法,具体代码如下所示:

import httpx

try:
    response = httpx.get("https://httpbin.io/anything")
    # Raise an exception for 4xx and 5xx responses
    response.raise_for_status()
    # Handle the response...
except httpx.HTTPStatusError as e:
    # Handle HTTP status errors
    print(f"HTTP error occurred: {e}")
except httpx.RequestError as e:
    # Handle connection or network errors
    print(f"Request error occurred: {e}")

会话处理

使用 HTTPX 中的顶层 API 时,HTTPX 会为每个请求建立一个新连接。换言之,TCP 连接不会被复用。随着向主机发出的请求数量增加,该方法会变得低效。

而使用 httpx.Client 实例则可实现 HTTP 连接池功能,即向同一主机发出的多个请求可以复用现有的 TCP 连接,无需为每个请求创建新的连接。

与顶层 API 相比,使用 Client 有如下好处:

  • 减少请求延迟(避免重复握手)
  • 降低 CPU 占用率,减少往返次数
  • 减轻网络拥塞

此外,Client 实例支持会话处理,提供顶层 API 所不具备的功能,例如:

  • 实现跨请求的 Cookie 持久化。
  • 将配置应用于所有发出的请求。
  • 通过 HTTP 代理发送请求。

如要在 HTTPX 中使用 Client,则建议使用上下文管理器(with 语句):

import httpx

with httpx.Client() as client:
    # Make an HTTP request using the client
    response = client.get("https://httpbin.io/anything")

    # Extract the JSON response data and print it
    response_data = response.json()
    print(response_data)

或者,您可手动管理客户端,使用 client.close() 显式关闭连接池:

import httpx

client = httpx.Client()
try:
    # Make an HTTP request using the client
    response = client.get("https://httpbin.io/anything")

    # Extract the JSON response data and print it
    response_data = response.json()
    print(response_data)
except:
  # Handle the error...
  pass
finally:
  # Close the client connections and release resources
  client.close()

注意:如您熟悉 Requests 库,则 httpx.Client() 的用途与 requests.Session() 类似。

异步 API

默认情况下,HTTPX 提供标准的同步 API。同时,它还可在有需要时提供异步客户端。如您在网页抓取时使用 asyncio,则使用异步客户端是高效发送 HTTP 请求的重要保障。

异步编程是一种并发模式,其效率明显高于多线程。它能显著提升性能,并支持 WebSockets 之类的长时间网络连接。这使得它成为加快网页抓取进程的一大重要因素。

要在 HTTPX 中发送异步请求,您需要使用 AsyncClient。如下所示,您需初始化该客户端并使用它来发送 GET 请求:

import httpx
import asyncio

async def fetch_data():
    async with httpx.AsyncClient() as client:
        # Make an async HTTP request
        response = await client.get("https://httpbin.io/anything")

        # Extract the JSON response data and print it
        response_data = response.json()
        print(response_data)

# Run the async function
asyncio.run(fetch_data())

with 语句可确保客户端在代码块结束后自动关闭。或者,如果您想手动管理客户端,则可使用 await client.close() 显示关闭它。

请记住,在使用 AsyncClient 时,所有 HTTPX 请求方法(get()post() 等)都是异步的。因此,在调用这些方法获取响应前,您必须添加 await

重试失败的请求

如在网页抓取过程中出现网络不稳定问题,则可能导致连接失败或超时。HTTPX 通过 HTTPTransport 接口简化了此类问题的处理。当出现 httpx.ConnectError 或 httpx.ConnectTimeout 错误时,该机制会重试相关请求。

以下示例演示了配置 Transport 以重试请求(最多 3 次)的代码操作:

import httpx

# Configure transport with retry capability on connection errors or timeouts
transport = httpx.HTTPTransport(retries=3)

# Use the transport with an HTTPX client
with httpx.Client(transport=transport) as client:
    # Make a GET request
    response = client.get("https://httpbin.io/anything")
    # Handle the response...

注意,只有与连接相关的错误才会触发重试。要处理读取/写入错误或特定的 HTTP 状态码错误,则需使用 tenacity 等库实现自定义重试逻辑。

HTTPX 与 Requests 在网页抓取方面的异同之处

以下汇总表对比了 HTTPX 和 Requests 在网页抓取方面的异同之处:

功能 HTTPX Requests
GitHub 星数 8000 52400
异步支持 ✔️
连接池 ✔️ ✔️
HTTP/2 支持 ✔️
自定义用户代理 ✔️ ✔️
代理支持 ✔️ ✔️
Cookie 处理 ✔️ ✔️
超时 自定义连接和读取 自定义连接和读取
重试机制 通过 Transport 提供 通过 HTTPAdapter 提供
性能 中等
社区支持和受欢迎程度 人气日益高涨 广受欢迎

结语

在本文中,您学习了使用 HTTPX 库进行网页抓取的具体操作。您了解了它的基本含义、功能和优势。在收集在线数据时,HTTPX 是快速可靠的 HTTP 请求发送工具。

问题在于自动发送的 HTTP 请求会泄露您的公共 IP 地址,暴露您的身份和位置,进而威胁您的隐私。因此,您最好使用代理服务器来隐藏 IP 地址,这是加强安全并保护您的隐私的最有效方法之一。

Bright Data 掌控着全球最出色的代理服务器,为财富 500 强企业和 20,000 多家客户提供服务。其代理网络涵盖不同类型的代理:

立即创建免费的 Bright Data 账户,测试我们的抓取解决方案和代理!