使用Python发送并发请求进行抓取

1 投票
1 回答
2160 浏览
提问于 2025-04-18 17:15

我现在用的是Python 3.4,并且安装了requests库和一些其他必要的程序来进行网页抓取。我的问题是,我想抓取大约7000个页面(只是html或文本),但我不想一次性抓取完。我希望能设置一个延迟,这样就不会对服务器发出太多请求,避免被封禁。我听说过grequests,但好像它不支持Python 3.4(实际错误提示是找不到vcvarsall.bat,而在文档中我也没看到对3.4的支持)。有没有人知道其他可以管理网址请求的替代程序?换句话说,我并不是想要尽快抓取所有内容,而是希望能慢慢来,稳扎稳打。

1 个回答

2

我建议你自己写一个多线程程序来处理请求。我发现使用 concurrent.futures 是实现这类请求的最简单方法,特别是用 ThreadPoolExecutor。他们的文档里甚至有一个简单的多线程网址请求示例。

至于问题的第二部分,实际上这取决于你想限制请求的多少和方式。对我来说,设置一个足够低的 max_workers 参数,并在我的函数中加入 time.sleep 等待时间,就足以避免任何问题,即使是在抓取成千上万的页面时也是如此,但这显然还要看你要抓取的网站。实现某种批处理或等待机制应该不难。

下面的代码虽然没有经过测试,但希望能给你一个起点。从这里开始,你可能需要根据自己的需求修改 get_url_data(或者你使用的其他函数),比如进行解析或保存数据。

import concurrent.futures as futures
import requests
from requests.exceptions import HTTPError

urllist = ...

def get_url_data(url, session):
    try:
        r = session.get(url, timeout=10)
        r.raise_for_status()
    except HTTPError:
        return None

    return r.text

s = requests.Session()

try:
    with futures.ThreadPoolExecutor(max_workers=5) as ex:
        future_to_url = {ex.submit(get_url_data, url, s): url
                         for url in urlist}

    results = {future_to_url[future]: future.result() 
               for future in futures.as_completed(future_to_url)}
finally:
    s.close() 

撰写回答