Python 爬虫并发抓取实现方法

3次阅读

Python 爬虫并发抓取实现方法

python 爬虫并发抓取的核心在于合理利用 I/O 等待时间,避免单请求阻塞整个流程。关键不是“开越线程/协程越好”,而是匹配目标网站的响应特性、自身网络带宽和反爬策略。

用 asyncio + aiohttp 实现高并发 HTTP 请求

aiohttp 是 Python 异步生态中最常用的 HTTP 客户端,配合 asyncio 可轻松实现数百级并发,且内存占用低、响应快。它适合大量轻量级请求(如列表页、API 接口)。

  • 需显式创建 Clientsession,复用连接池,避免频繁握手开销
  • 限制并发数(如使用 asyncio.Semaphore),防止被目标站限流或封 IP
  • 务必设置超时(timeout 参数),避免某个请求卡死拖垮整体任务
  • 示例:抓取 100 个 URL,限制同时 20 个请求

async with aiohttp.ClientSession() as session:
  sem = asyncio.Semaphore(20)
  tasks = [fetch(session, url, sem) for url in urls]
  await asyncio.gather(*tasks)

用 requests + ThreadPoolExecutor 控制中等并发

requests 简单易用、兼容性强,配合 concurrent.futures.ThreadPoolExecutor 可快速实现 10–50 级并发,适合对稳定性要求高、需复用 cookies 或 session 的场景(如登录后抓取)。

  • 线程数不宜超过 CPU 核心数的 2–4 倍;I/O 密集型任务可稍多,但通常 32 线程已是多数情况上限
  • 每个线程应独立维护 requests.Session,避免线程间状态干扰
  • 建议配合 retry 模块(如 tenacity)处理临时失败,比手动 try-except 更清晰

注意反爬与请求调度的协同设计

并发本身不解决反爬问题,反而容易触发风控。必须把并发控制和反爬策略绑定设计:

  • 随机化请求间隔(如 uniform(0.5, 2.0) 秒),避免固定频率被识别为脚本
  • 轮换 User-Agent 和代理 IP(尤其使用 aiohttp 时,可在每次 request 中动态设置 headers)
  • 对返回 429/503 的请求做退避重试(exponential backoff),而非立即重发
  • 关键:监控响应状态码和响应体特征(如是否含“验证”字样),自动降速或切换代理

避免常见陷阱

并发提升效率的前提是资源不冲突、逻辑不耦合:

  • 不要在协程里调用 time.sleep() —— 会阻塞整个事件循环,改用 await asyncio.sleep()
  • 全局共享变量(如计数器、列表)在多线程/协程下需加锁(threading.Lock)或用线程安全结构(如 queue.Queue)
  • DNS 解析可能成为瓶颈,可在 aiohttp 中启用 TCPConnector(use_dns_cache=True, limit=100)
  • 大量并发写文件易导致 IO 冲突,建议先缓存结果,再批量落盘
text=ZqhQzanResources