【爬虫入门第6讲】Python爬虫并发方案全解析

发布时间:2026/7/29 22:52:15
【爬虫入门第6讲】Python爬虫并发方案全解析 Python爬虫并发方案全解析多线程、多进程与异步协程实战指南引言在爬虫开发中性能瓶颈往往不在于代码语法而在于对网络I/O、并发模型和资源调度的底层理解。当我们需要批量抓取网页数据时串行请求的效率显然无法满足需求。Python提供了三种主流的并发方案多线程threading、多进程multiprocessing和异步协程asyncio aiohttp。本文将深入剖析这三种方案的适用场景、使用方法及优缺点帮助你根据实际需求做出最佳选择。一、多线程threading / ThreadPoolExecutor1. 适用场景多线程适合I/O密集型任务如网络请求、文件读写等。由于Python的GIL全局解释器锁限制同一时刻只有一个线程执行Python字节码因此多线程无法利用多核CPU提升计算性能。但对于I/O密集型任务线程可以在阻塞时让出GIL实现并发效果。典型应用小批量网页爬取、同时处理多个客户端请求、日志记录等轻量级并发场景。2. 使用方法方式一threading.Thread 直接创建importthreadingimportrequestsimporttimedeffetch_url(url):try:resprequests.get(url,timeout5)print(f成功爬取:{url}状态码:{resp.status_code})exceptExceptionase:print(f失败:{url}错误:{e})urls[https://www.baidu.com,https://www.qq.com,https://www.sina.com.cn]threads[]forurlinurls:tthreading.Thread(targetfetch_url,args(url,))threads.append(t)t.start()fortinthreads:t.join()方式二Queue 多线程任务分发使用Queue队列做任务分发线程安全适合入门爬虫importthreadingimportqueueimportrequests url_list[https://www.baidu.com,https://www.qq.com,https://www.sina.com.cn]qqueue.Queue()forurlinurl_list:q.put(url)defspider():whilenotq.empty():urlq.get()try:resprequests.get(url,timeout5)print(f成功爬取:{url}状态码:{resp.status_code})exceptExceptionase:print(f失败:{url}错误:{e})finally:q.task_done()thread_num3for_inrange(thread_num):tthreading.Thread(targetspider)t.daemonTruet.start()q.join()方式三ThreadPoolExecutor 线程池使用concurrent.futures.ThreadPoolExecutor简化线程管理fromconcurrent.futuresimportThreadPoolExecutorimportrequestsdeffetch(url):resprequests.get(url,timeout5)returnresp.status_code urls[https://www.baidu.com,https://www.qq.com,https://www.sina.com.cn]withThreadPoolExecutor(max_workers3)asexecutor:futures[executor.submit(fetch,url)forurlinurls]forfinfutures:print(f.result())3. 优缺点优点实现简单易于理解适合入门共享内存空间数据传递方便轻量级调度I/O密集场景高效缺点受GIL限制无法真正并行执行CPU密集型任务线程切换开销较大实际有效并行度有限需要处理线程安全问题如加锁二、多进程multiprocessing / ProcessPoolExecutor1. 适用场景多进程适合CPU密集型任务如HTML解析、数据清洗、图像处理等。每个进程拥有独立的内存空间和Python解释器可以绕过GIL限制充分利用多核CPU。典型应用大规模数据解析、分布式爬虫、需要跨CPU核心并行计算的任务。2. 使用方法方式一multiprocessing.Processimportmultiprocessingdefcpu_task(n):totalsum(i*iforiinrange(n))print(f进程{multiprocessing.current_process().name}完成)if__name____main__:p1multiprocessing.Process(targetcpu_task,args(5_000_000,))p2multiprocessing.Process(targetcpu_task,args(5_000_000,))p1.start()p2.start()p1.join()p2.join()方式二进程间通信Queuedefproducer(q):foriinrange(5):q.put(f数据{i})q.put(None)defconsumer(q):whileTrue:dataq.get()ifdataisNone:breakprint(f消费{data})if__name____main__:qmultiprocessing.Queue()p1multiprocessing.Process(targetproducer,args(q,))p2multiprocessing.Process(targetconsumer,args(q,))p1.start()p2.start()p1.join()p2.join()方式三ProcessPoolExecutor 进程池fromconcurrent.futuresimportProcessPoolExecutordefsquare(n):returnn*nwithProcessPoolExecutor(max_workers4)asexecutor:resultsexecutor.map(square,range(10))print(list(results))3. 优缺点优点绕过GIL真正实现并行计算适合CPU密集型任务性能提升明显进程间隔离性强数据安全性高缺点内存占用高每个进程有独立内存空间进程间通信复杂需使用Queue、Pipe等工具创建和销毁进程的开销较大三、异步协程asyncio aiohttp1. 适用场景异步协程适合高并发I/O密集型任务如大规模网页爬取、实时数据抓取、Web服务等。它在单线程内通过事件循环实现并发可以管理数万级并发连接资源占用极低。典型应用百万级网页抓取、实时数据监控、高性能API服务。2. 使用方法基础异步抓取importasyncioimportaiohttpimporttime url_list[https://www.baidu.com,https://www.qq.com,https://www.sina.com.cn,https://www.163.com,https://www.taobao.com]asyncdeffetch(session,url):try:asyncwithsession.get(url,timeoutaiohttp.ClientTimeout(total5))asresp:print(f成功爬取:{url}状态码:{resp.status})returnawaitresp.text()exceptExceptionase:print(f失败:{url}错误:{e})asyncdefmain():asyncwithaiohttp.ClientSession()assession:tasks[fetch(session,url)forurlinurl_list]awaitasyncio.gather(*tasks)if__name____main__:starttime.time()asyncio.run(main())endtime.time()print(f\n协程异步爬虫总耗时:{end-start:.2f}秒)批量控制与限流使用信号量Semaphore控制并发数量避免对目标服务器造成过大压力asyncdeffetch_with_semaphore(sem,session,url):asyncwithsem:returnawaitfetch(session,url)asyncdefmain():semasyncio.Semaphore(10)# 限制并发数为10asyncwithaiohttp.ClientSession()assession:tasks[fetch_with_semaphore(sem,session,url)forurlinurl_list]awaitasyncio.gather(*tasks)批量分页处理batch_size100foriinrange(0,len(urls),batch_size):batchurls[i:ibatch_size]resultsawaitasyncio.gather(*[fetch(session,url)forurlinbatch])3. 优缺点优点单线程内实现高并发资源占用极低每个协程仅需几KB内存速度远超多线程尤其适合大批量请求代码逻辑清晰避免回调地狱缺点所有I/O操作必须是异步兼容的不能直接使用requests.get()、time.sleep()等同步函数调试相对复杂需要掌握事件循环和异步语法不适合CPU密集型任务会阻塞事件循环四、三种方案对比总结维度多线程多进程异步协程适用场景I/O密集型小批量CPU密集型I/O密集型大批量并发能力中等高受CPU核心数限制极高万级连接资源占用中等高低开发复杂度低中高GIL影响受限制绕过GIL单线程无GIL问题速度表现一般CPU任务快I/O任务慢I/O任务极快典型耗时2.2秒10个任务7.6秒CPU任务2.5秒100协程五、选型建议1. 根据任务类型选择I/O密集型 小批量几十个请求多线程即可简单高效I/O密集型 大批量成千上万请求优先选择异步协程性能碾压CPU密集型数据解析、计算多进程是唯一选择混合型抓取解析可采用多进程异步协程的混合架构多进程负责解析异步协程负责抓取2. 实际项目中的最佳实践连接复用使用aiohttp.ClientSession复用TCP连接减少握手开销限速控制添加随机延时避免触发反爬机制重试机制失败请求自动重试提高抓取成功率批量写入数据存储时批量操作减少I/O调用监控告警统计抓取成功率、延迟、内存占用等指标3. 反爬应对策略多线程/多进程可模拟多IP请求降低封禁风险而异步协程由于单线程特性在反爬场景中需要额外配置代理池。结语Python爬虫的并发方案没有绝对的“最优解”只有“最适合”。理解每种方案的底层原理和适用场景才能在实际项目中做出明智的选择。对于大多数爬虫场景异步协程凭借其极致的并发能力和低资源消耗正成为越来越多开发者的首选。但如果你需要处理CPU密集型任务或者项目对开发速度要求更高多线程和多进程依然是可靠的选择。