用 Python 中的并发功能加速网络抓取

用 Python 中的并发功能加速网络抓取

在本指南中,我将向您介绍如何使用 asyncio 和 aiohttp 在 Python 中使您的 网络抓取 更快、更高效。我将逐步为你讲解,这样你就能提高刮擦脚本的性能,并迅速获得所需的数据。让我们开始吧

什么是并发?

并发性 是指同时或不按顺序处理多个任务的能力。就网络搜刮而言,这意味着同时发送多个请求,而不是等每个请求完成后再开始下一个请求。在传统的搜刮设置中,每个请求都需要时间,尤其是在服务器速度较慢或有许多页面需要搜刮的情况下。使用并发搜刮,您可以同时发送多个请求。在一个请求等待响应时,您可以处理其他请求。这样可以减少等待时间,加快你的搜刮速度。

Python 网络抓取的最佳替代方案

Bright Data 的网络抓取器 为您的数据提取需求提供完整的企业级解决方案。通过访问庞大的代理网络和先进的反僵尸措施,Bright Data 使您的网络刮擦项目的扩展变得更容易、更可靠。无论您是为市场调研收集数据,还是自动执行大规模刮擦任务,其强大的基础架构都能让您专注于提取高质量的数据,而无需费心费力。

如果您也想了解其他工具,请访问我的 最佳刮削工具列表.

顺序扫描:慢速方法

让我们先看看典型的刮擦脚本在不并发的情况下是如何工作的。想象一下,您需要抓取一个有 12 个页面的网站,每个页面的加载时间约为 2 秒。顺序方法意味着

  1. 您向首页提出申请。
  2. 等待 2 秒钟,等待服务器响应。
  3. 处理数据。
  4. 翻到下一页,重复这一过程。

如果每个页面耗时 2 秒,那么按顺序抓取全部 12 个页面的总时间就是 24 秒。

下面举例说明如何使用请求库在 Python 中实现这一功能:

import requests
from bs4 import BeautifulSoup
import csv
base_url = "https://example.com/products/page"
页 = range(1, 13) # 搜索 12 页
def 提取数据(page):
url = f"{base_url}/{页}/"
response = requests.get(url)
soup = BeautifulSoup(response.text、 "html.parser")
# 在此处提取数据(如产品名称、价格)
products = []
for product in soup.select(".产品"):
products.append({
"name": product.find("h2").text.strip()、
"price": product.find(class_="price").text.strip()、
"url": product.find("a").get("href")
})
return products
def 存储结果(products):
with open("products.csv", "w") as 文件
writer = csv.DictWriter(file, fieldnames=products[...0].keys())
writer.writeheader()
writer.writerows(products)
all_products = []
for page in 页面:
all_products.extend(extract_data(page))
store_results(all_products)

运行上述代码

运行该脚本时,程序将耗时约 24 秒(12 个页面中每个页面耗时 2 秒)。虽然这对小型网站来说没什么问题,但当你需要抓取大量网页时,这就成了一个问题。

顺序扫描的问题

上述方法虽然可行,但效率不高。正如你所看到的,等待服务器响应的空闲时间很长。如果要抓取许多页面,闲置时间就会迅速增加。更有效的处理方法是同时向多个页面发送请求。

并发性:使用 Asyncio 和 Aiohttp 加快抓取速度

为了解决顺序刮擦的低效问题,我们可以使用并发功能。这样就可以同时发送多个请求,并在收到响应时进行处理。我们将使用 asyncio 和 aiohttp,它们是专为 Python 异步编程而设计的。

什么是 Aiohttp?

Aiohttp 是 Python 的异步 HTTP 客户端/服务器库。它能与 asyncio 完美配合,让你在不阻塞主线程的情况下发送 HTTP 请求。与同步请求库不同,aiohttp 支持异步操作。

使用 Asyncio 和 Aiohttp 进行抓取

现在,让我们修改上述脚本,使用 asyncio 和 aiohttp 实现并发。

第 1 步:安装所需程序库

在开始之前,我们需要安装 aiohttp 和 beautifulsoup4:

pip install aiohttp beautifulsoup4

步骤 2:实现并发

以下是重写脚本以使用并发功能的方法:

import aiohttp
import asyncio
from bs4 import BeautifulSoup
import csv
base_url = "https://example.com/products/page"
页 = range(1, 13) # 搜索 12 页
async def 提取数据(页面、会话):
url = f"{base_url}/{页}/"
async with session.get(url) as 响应:
soup = BeautifulSoup(await response.text()、 "html.parser")
产品 = []
for product in soup.select(".产品"):
products.append({
"name": product.find("h2").text.strip()、
"price": product.find(class_="price").text.strip()、
"url": product.find("a").get("href")
})
return products
async def main():
async with aiohttp.ClientSession() as 会话:
任务 = [extract_data(page, session) for page in 页面]
结果 = await asyncio.gather(*tasks)
all_products = [item for 子列表 in 成果 for item in 子列表] # 扁平化列表
存储结果(所有产品)
def 存储结果(products):
with open("products.csv", "w") as 文件
writer = csv.DictWriter(file, fieldnames=products[...0].keys())
writer.writeheader()
writer.writerows(products)
# 运行 asyncio 事件循环
asyncio.run(main())

准则》的主要变化

  1. 异步请求: 我们更换了 请求.get() 与会话.get() 从 aiohttp。async with 语句确保异步处理请求。
  2. 异步功能: extract_data 函数现在是异步的,用 async def 表示,await 关键字用于等待服务器的响应,而不会阻塞整个程序。
  3. 并行任务: 我们使用 asyncio.gather() 并发执行多个任务。每个任务负责扫描一个页面。
  4. 扁平化结果: 并发刮擦所有页面后,刮擦结果将存储在结果中。因为 asyncio.gather() 返回的是一个列表,我们需要对其进行扁平化处理,以得到一个包含所有产品的单一列表。

步骤 3:运行代码

现在,当你运行更新后的脚本时,它会同时搜索所有 12 个页面。所需的时间将比顺序版本快得多。具体的速度取决于你的网络和服务器的响应时间,但一般来说,你会看到明显的改进。

性能比较

  • 顺序扫描: 如前所述,连续搜索 12 个页面大约需要 24 秒。
  • 使用 Asyncio 进行并发抓取: 在并发版本中,脚本可以在 5 到 10 秒左右完成相同的任务,具体取决于网络延迟。

限制并发量,防止服务器超载

虽然并发可加快刮擦速度,但同时发送过多请求会使服务器超负荷或 屏蔽你的IP.为了避免这种情况,我们可以在 asyncio 中使用一个 semaphore 来限制并发请求的数量。

使用 Semaphore 的示例

max_concurrency = 5 # 限制并发请求
sem = asyncio.Semaphore(max_concurrency)
async def 提取数据(页面、会话):
async with 半: # 这限制了并发请求的数量
url = f"{base_url}/{页}/"
async with session.get(url) as 响应:
soup = BeautifulSoup(await response.text()、 "html.parser")
产品 = []
for product in soup.select(".产品"):
products.append({
"name": product.find("h2").text.strip()、
"price": product.find(class_="price").text.strip()、
"url": product.find("a").get("href")
})
return products

结论

通过限制并发量,我们可以确保一次只发送特定数量的请求。这就降低了服务器不堪重负或被阻塞的风险。

现在,我们已经学会了如何使用 Python 中的 async io 和 aiohttp 并发功能来加快网页刮擦速度。通过同时发送多个请求,我们可以大大缩短刮擦网站所需的时间,尤其是在处理大量页面时。切记遵守网站服务条款,避免过多请求造成服务器超载。祝您搜刮愉快

类似文章