ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python 爬虫太慢被老板骂?从单线程到异步并发,效率提升 10 倍

Python 爬虫太慢被老板骂?从单线程到异步并发,效率提升 10 倍 爬虫太慢被老板骂从单线程到异步并发效率提升 10 倍搞爬虫时, 让人倍感痛苦的并非是反爬情况, 而是速度迟缓。 存在着这样一个单线程的爬虫, 对于 1000 个页面, 它会逐个去发起请求, 并且每个页面请求都要等待 0.5 秒 , 结果跑完这些页面请求竟然需要 8 分多钟。老板就在旁边看着那不断转动的圈圈, 这种情况下, 你难道能够不慌张吗?今日分享一套实战层面的提速方案, 该方案涵盖从线程池直至异步并发, 全部均为可有效运行的代码, 能够助力你将爬虫速度提升至超过 10 倍的程度, 并最终实现显著提速的效果。第一步先别急着优化把日志打全任何优化都建立在可观测性上。先加两个东西import logging, time logging.basicConfig(levellogging.INFO, format%(asctime)s %(message)s) def fetch(url): logging.info(fstart {url}) time.sleep(0.5) # 模拟网络请求 logging.info(fdone {url}) return url这样一来, 你能够清晰地看到, 瓶颈究竟是处于网络等待的状况, 还是存在于解析处理的环节。百分之八十的爬虫瓶颈皆是前者, 也就是 IO 等待, 如此便产生了优化的空间。第二步线程池一行代码提速 5 倍针对于IO密集任务而言, 运用多线程便能够获取到红利GIL仅仅锁定CPU计算, 却不锁定IO等待, 并且, 此情况是这样的。from concurrent.futures import ThreadPoolExecutor urls [fhttps://example.com/page/{i} for i in range(100)] with ThreadPoolExecutor(max_workers10) as pool: results list(pool.map(fetch, urls)) # 10 个线程并发耗时缩到 1/10留意, 切莫无休止地开启线程, 10至20个足矣。一旦线程数量超出那个临界数值, 切换所产生的开销反倒会降低速度。第三步异步并发终极提速方案要是线程池的速度仍未达到足够快的程度, 那就去采用异步方式。在单线程的范畴之内, 对成千上万个协程进行调度, 并且具备零线程切换所产生的开销。import asyncio, aiohttp async def fetch(session, url): async with session.get(url) as resp: return await resp.text() async def main(): async with aiohttp.ClientSession() as session: tasks [fetch(session, u) for u in urls] results await asyncio.gather(*tasks) # 全部并发耗时 最慢的一个 asyncio.run(main())关键在于这样一句话: 运用 / 去将任务进行打包实现并发操作, 并非是在循环之中逐个地进行 await 操作。第四步断点续爬 去重避免重复劳动崩掉的爬虫重新运行, 要把所有内容全部再次进行爬取一遍吗? 那无疑是一场灾难。去维护一个已经爬取过的集合, 在秒级的时间内实现去重:import redis seen redis.Redis().sismember # 已爬 URL 存 Set for url in urls: if seen(crawled, url): continue # 爬过的直接跳过 html fetch(url) redis.Redis().sadd(crawled, url)配上断点机制1 万条数据爬一半崩溃恢复后只补后半段。第五步被 429 封 IP指数退避重试当并发速度过快时, 极易遭到限流对待。重试并非一味地死等, 而是采用指数退避策略: 一旦失败, 便等待1秒, 接着等待2秒, 随后等待4秒, 再之后等待8秒, 依此类推, 直至成功, 以此给予服务器喘息的时间。import backoff backoff.on_exception( backoff.expo, aiohttp.ClientError, max_tries5, factor1,) async def fetch_with_retry(session, url): async with session.get(url) as resp: resp.raise_for_status() return await resp.text()避坑清单血的教训包含断点续爬, 以及分布式爬虫, 还有IP代理池, 以及验证码对抗全流程的教程, 是完整实战教程。站内存在基础教程, 有异步Web开发教程, 有数据采集教程, 有计算机视觉教程, 有NLP教程, 有大模型RAG全套免费教程, 依照这些去学习不会迷失方向。
返回列表