网络抓取是自动从网站提取数据的过程。这是一种常用技术,用于收集大量数据以进行分析、机器学习等。在 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!