跳到内容

如何使用 HTTPX 和 Python 进行网页抓取

网络抓取是自动从网站提取数据的过程。这是一种常用技术,用于收集大量数据以进行分析、机器学习等。在 Python 中,有许多很棒的库可以使网络抓取变得容易。一种流行的选项是 HTTPX。

HTTPX 是一个强大且现代的 Python HTTP 客户端。它是由 Requests 背后的开发人员创建的,从 Requests 中汲取了很多灵感,同时还添加了 HTTP/2 支持等新功能。

在本综合指南中,我们将探讨如何使用 HTTPX 在 Python 中有效地抓取网站。

HTTPX 入门

首先,可以通过 pip 安装 HTTPX:

pip install httpx

或者,可以使用诗歌:

poetry add httpx

安装后,可以导入并使用 HTTPX:

import httpx

response = httpx.get(‘https://example.com‘)
print(response.text)

这将向 example.com 发出 GET 请求并打印主页的 HTML。

HTTPX 有一个简单的 API,支持所有流行的 HTTP 动词,如 GET、POST、PUT、DELETE、HEAD、OPTIONS 等。

一些主要功能包括:

  • HTTP/1.1 和 HTTP/2 支持
  • 异步 aiohttp 风格的 API
  • 连接池和保活
  • 代理支持
  • 超时配置
  • Cookie 持久性
  • 熟悉的请求式 API

接下来让我们看看一些常见的使用模式。

使用 HTTPX 发出请求

要发出 GET 请求, httpx.get() 可以使用方法:

response = httpx.get(‘https://example.com‘)

同样, httpx.post(), httpx.put(), httpx.delete()等可用于其他 HTTP 动词。

headers、cookies、timeout 等参数可以作为关键字参数传入:

response = httpx.get(
  ‘https://httpbin.org/headers‘,
  headers = {
    ‘User-Agent‘: ‘MyBot 1.0‘
  },
  timeout = 10.0
)

响应包含如下属性 status_code, headers, text, json()等:

print(response.status_code)
print(response.headers)
print(response.text)
json = response.json()

您还可以通过迭代响应对象来增量流式传输响应。

使用 HTTPX 客户端

对于大多数抓取任务,建议使用持久性 httpx.Client 实例。

客户端跨多个请求处理连接池、会话、cookie 等事务。

import httpx

client = httpx.Client()

response = client.get(‘https://example.com‘)
print(response.text)

response = client.get(‘https://httpbin.org/cookies/set?name=foo‘)
print(response.text) 

response = client.get(‘https://httpbin.org/cookies‘)
print(response.json())

在这里,我们使用同一个客户端发出多个请求,该客户端会自动处理 cookie 持久性。

您还可以在创建客户端时配置标头、代理、身份验证等选项:

client = httpx.Client(
  headers = {
    ‘User-Agent‘: ‘MyBot 1.0‘,
    ‘Authorization‘: ‘Bearer xxx‘   
  },
  proxies = ‘http://192.168.1.1:8181/‘,
  auth = (‘username‘, ‘password‘)
)

现在让我们看看异步的制作请求。

使用 HTTPX 的异步请求

为了在 Python 中异步发出请求,HTTPX 提供了一个 AsyncClient:

import httpx

async with httpx.AsyncClient() as client:
  response = await client.get(‘https://example.com‘) 

我们使用 async with 初始化客户端, await 根据请求,然后自动关闭客户端。

要同时抓取多个 URL,我们可以使用 asyncio.gather():

import httpx
import asyncio

async def get_url(url):
  async with httpx.AsyncClient() as client:
    response = await client.get(url)
    return response.text

urls = [‘https://example.com‘, ‘https://httpbin.org‘, ‘https://python.org‘]

async def main():
  responses = await asyncio.gather(*[get_url(url) for url in urls])
  print(responses)

asyncio.run(main())

asyncio.gather() 同时等待多个协程并按照可等待的顺序返回结果。

还有其他选项,例如 asyncio.as_completed() 在它们完成时对其进行处理:

tasks = [get_url(url) for url in urls]

async def main():
  for result in asyncio.as_completed(tasks):
    print(await result)

异步 IO 可以一次同时获取多个页面,这对于加快抓取速度很有用。

接下来让我们看看从 HTML 和 JSON 响应中抓取数据。

抓取 HTML 和 JSON 响应

对于 HTML 抓取,我们可以使用像 Beautiful Soup 这样的解析器来提取数据:

from bs4 import BeautifulSoup

response = httpx.get(‘https://en.wikipedia.org/wiki/Python_(programming_language)‘)

soup = BeautifulSoup(response.text, ‘html.parser‘)

for link in soup.select(‘.toctext‘):
  print(link.text.strip())

这将打印维基百科页面目录的内容。

对于 JSON 响应,HTTPX 提供了内置的 .json() 方法:

response = httpx.get(‘https://api.github.com/repos/encode/httpx‘)

json = response.json()
print(json[‘description‘])

json= 参数还可以用于序列化请求中的 JSON 数据。

与解析器一起,我们可以构建抓取工具来从 API 和网站中提取数据。

处理问题和错误

抓取时,经常会出现连接错误、超时、速率限制等问题。

HTTPX 提供了异常和适当处理它们的工具。

超时时间

要处理缓慢的响应,您可以指定一个自定义 timeout 范围。默认值为 5 秒。

response = httpx.get(‘https://example.com‘, timeout=10.0) 

如果超过这个值, httpx.TimeoutException 发生:

try:
  response = httpx.get(‘https://example.com‘, timeout=0.001)
except httpx.TimeoutException:
  print(‘Request timed out‘)

某些网站或页面可能需要更长的超时。

HTTP 错误

对于 400、500 等 HTTP 错误 response.raise_for_status() 可以使用方法:

try:
  response = httpx.get(‘https://httpbin.org/status/500‘)
  response.raise_for_status()
except httpx.HTTPStatusError:
  print(‘HTTP Error occurred‘)  

这将引发任何 4xx 或 5xx 状态代码的异常。

重试请求

要添加重试逻辑,外部包如 tenacity 可以使用:

from tenacity import retry, stop_after_attempt

@retry(stop=stop_after_attempt(3))
def make_request():
  response = httpx.get(‘https://example.com‘)
  response.raise_for_status()
  return response.json()

data = make_request()

在这里,我们对任何异常最多重试 3 次。还可以定义更高级的重试逻辑。

并行请求限制

并行发出多个请求时,您可能会遇到连接限制。

limits 参数可用于配置诸如最大连接数之类的选项:

client = httpx.AsyncClient(
  limits = httpx.Limits(max_connections=20)
)

根据目标站点调整此参数可以帮助避免限制。

通过处理这些常见问题,可以构建更具弹性的刮刀。

抓取最佳实践

以下是使用 HTTPX 创建有效的网络抓取工具的一些技巧:

  • 使用 HTTPX 客户端 – 客户端提供连接池、cookie 持久性和其他好处。

  • 礼貌地刮擦 – 限制请求率以避免服务器不堪重负。使用随机延迟和限制。

  • 处理错误 – 使用 try/ except 块、状态检查和重试来处理问题。

  • 使用异步IO – 同时抓取页面以提高速度。但限制并发以避免禁令。

  • 随机化用户代理 – 轮换随机用户代理字符串以显得更加人性化。

  • 使用代理 – 轮换不同的代理/IP 来分发请求。

  • 缓存和保留数据 – 将抓取的数据保存到文件/数据库以避免重新抓取。

通过遵循这些最佳实践,可以构建更强大且可维护的刮刀。

先进的刮擦技术

让我们看看 HTTPX 的一些更高级的抓取功能。

抓取身份验证页面

为了抓取经过身份验证的页面,HTTPX 支持多种身份验证类型,例如基本身份验证、摘要身份验证和不记名身份验证:

client = httpx.Client(
  auth = (‘username‘, ‘password‘)
)

response = client.get(‘https://api.example.com/users/me‘)

身份验证凭据会在请求之间自动保留。

处理 Cookie

cookies 参数可用于发送自定义cookie:

client = httpx.Client(
  cookies = {
    ‘sessionId‘: ‘xxxx‘
  }
)

response = client.get(‘https://example.com/dashboard‘) 

并且服务器设置的cookie会自动保存在客户端。

流式响应

对于大型响应,您可以通过迭代响应对象来增量地流式传输它们:

response = client.get(‘https://example.com/bigfile‘)
for chunk in response:
  process(chunk) 

这避免了必须将整个响应加载到内存中。

代理支持

要通过代理服务器路由请求, proxies 可以使用参数:

proxy = ‘http://192.168.0.1:8888‘ 

client = httpx.Client(proxies = {‘http‘: proxy, ‘https‘: proxy})

轮换不同的代理有助于分发来自不同 IP 的请求。

自定义标题

您可以像用户代理一样欺骗或随机化请求标头:

client = httpx.Client(headers = {
  ‘User-Agent‘: ‘MyBot 1.0‘ 
})

这模仿了真实浏览器的标题。

通过这些更高级的功能,可以使用 HTTPX 和 Python 构建强大的抓取工具。

刮刀示例

现在让我们看一些使用 HTTPX 构建的抓取工具示例。

Reddit API 抓取器

这是 Reddit API 的基本抓取工具:

import httpx

client = httpx.Client()

subreddit = ‘python‘ 
listing = ‘hot‘
limit = 10

response = client.get(f‘https://www.reddit.com/r/{subreddit}/{listing}.json?limit={limit}‘)

data = response.json()[‘data‘]

for post in data[‘children‘]:
   title = post[‘data‘][‘title‘]
   score = post[‘data‘][‘score‘]
   print(f"{title} (Score: {score})")

这将从 Python subreddit 中获取热门帖子的数据。 API 返回我们可以解析的 JSON。

我们可以扩展这个抓取工具以从多个 Reddit 子版块中提取数据、将结果存储在数据库中等。

新闻文章抓取

这是一个简单的新闻抓取工具,可以从网站中提取文章:

from bs4 import BeautifulSoup
import httpx

client = httpx.Client()

response = client.get("https://example.com/news")
soup = BeautifulSoup(response.text, ‘html.parser‘)

for article in soup.select(‘.article‘):

  title = article.select_one(‘.article-title‘).text
  content = article.select_one(‘.article-content‘).text

  print(title) 
  print(content)
  print() 

这找到了所有 .article 元素,提取标题和内容字段,并打印文章。

同样,这可以扩展到抓取其他字段、解析日期、存储在数据库中等。

搜索结果刮刀

这是 Google 搜索结果的抓取工具示例:

import httpx

query = "httpx python"
url = f"https://www.google.com/search?q={query}"

client = httpx.Client()
response = client.get(url)

from bs4 import BeautifulSoup
soup = BeautifulSoup(response.text, ‘html.parser‘)

for result in soup.select(‘.tF2Cxc‘):

  title = result.select_one(‘.DKV0Md‘).text
  link = result.select_one(‘.yuRUbf a‘)[‘href‘]

  print(title)
  print(link) 
  print()

这会在 Google 中搜索给定的查询,解析结果链接/标题,然后将其打印出来。

同样,可以进行许多增强功能,例如提取搜索结果计数、附加字段、抓取结果页面、检测验证码等。

这些示例演示了 HTTPX 的常见抓取模式。相同的技术可以应用于为许多网站和 API 构建抓取工具。

总结

总而言之,HTTPX 提供了一个强大的 HTTP 客户端来构建 Python 网络抓取工具。以下是一些要点:

  • HTTPX 有一个简单的、请求式的 API 用于发出请求。

  • 异步支持允许同时发出请求。

  • 具有超时、重试和状态检查功能的强大错误处理。

  • 使用 Beautiful Soup 和 JSON API 轻松抓取 HTML 页面。

  • 持久客户端提供连接池、会话和 cookie 处理。

  • 代理、标头和身份验证等先进技术可实现复杂的抓取工具。

  • 遵循最佳实践,例如使用限制、随机延迟和用户代理。

HTTPX 使使用 Python 开始抓取变得容易。通过强大的错误处理和异步并发,可以开发可扩展的抓取器。

在您的下一个 Python 网络抓取项目中尝试一下 HTTPX!

标签:

加入谈话

您的电邮地址不会被公开。 必填带 *