欢迎来到 嗅灵易学

零基础也能上手的脚本技术课,一对一答疑带你入门

三万条评论爬了一整夜?我是怎么把异步爬虫救回来的

三万条评论爬了一整夜?我是怎么把异步爬虫救回来的

去年接了个小活,要扒一个电商站上大概三万条商品评论做情感分析。我第一版脚本跑起来,进度条悠悠地走,瞄了眼「预计剩余时间」——十一个小时。那一刻我意识到,问题不在网速,在我自己。

那版代码长这样(节选):

import requests

def fetch(url):
    return requests.get(url, timeout=10).text

for url in urls:          # 三万个,串行
    html = fetch(url)
    parse(html)

requests 是同步阻塞的,一个请求卡 200ms,三万个就是 6000 秒起步。更要命的是,我在循环里每回都 new 一个 session 的坏习惯没改——连接不复用,TCP 三次握手每次重来,TLS 也重来。这俩开销加起来,比请求本身还贵。

卡在哪,就先解决哪

换成 aiohttp 之后,核心思路就一句话:用协程把等待的时间交出来,让事件循环去跑别的请求。但光换库没用,并发不控一样死。我最终的写法:

import aiohttp, asyncio

async def fetch(session, url, sem):
    async with sem:                      # 控并发,别一窝蜂
        try:
            async with session.get(url, timeout=aiohttp.ClientTimeout(total=15)) as r:
                return await r.text()
        except Exception as e:
            return f"ERR:{e}"

async def main(urls):
    connector = aiohttp.TCPConnector(limit=20, ttl_dns_cache=300)
    sem = asyncio.Semaphore(20)
    async with aiohttp.ClientSession(connector=connector,
            headers={"User-Agent": "Mozilla/5.0"}) as session:
        tasks = [fetch(session, u, sem) for u in urls]
        return await asyncio.gather(*tasks)

htmls = asyncio.run(main(urls))

这里有三个我踩出来的点,值得单拎出来说:

  • connector 的 limit 和 Semaphore 是两个东西。TCPConnector 管底层连接池,Semaphore 管你同时发多少任务。只设一个,另一个可能成为隐形瓶颈。我两个都卡在 20,是因为那站单 IP 超过这个数就开始 503。
  • ttl_dns_cache 别漏。三万个请求反复解析同一个域名,DNS 查询累积的延迟很可观,缓存 5 分钟直接砍掉这块。
  • 超时一定显式设。aiohttp 默认没总超时,一个慢请求能吊着整批。

但别高兴太早

并发拉到 50 的时候,我遇到了平生第一次「自己把自己 ban 了」——不是对方反爬多高级,是我请求太密,触发了人家的基础限流。所以并发数不是越大越好,它本质是「速度和被封概率」的权衡。我的经验是先用小批量探出对方 tolerant 的阈值,再定一个留余量的数。

还有个坑:异步代码里一旦混进一个同步阻塞调用(比如时间长的 parse 或同步 requests),整个事件循环就被你那个调用卡住,协程的优势瞬间归零。解析逻辑要么也异步化,要么丢到线程池(loop.run_in_executor)里跑。

改完那版,同样三万条,二十分钟跑完,而且因为连接复用,服务器那边压力反而比之前友好。异步不是银弹,它救不了烂的网络和更强的反爬;但在「IO 等待多、CPU 活儿少」的爬虫场景里,它确实能把你从无聊的等待里解放出来。这事之后我再看任何「慢」的脚本,第一反应都是先问一句:你在等什么,又为什么不在等的时候干点别的。

注意:上传附件及图片大小不得大于30M。

⚠️ 版权声明:
本博客所有内容(含教程、源码、工具)仅供个人技术学习与研究交流使用,严禁商用、倒卖、二次分发及非法用途
未经作者书面授权,任何组织或个人不得转载、复制或用于其他平台,违者将追究相关责任。

0 0 0 举报
复制成功