楼层: 首页/ 软件技术/ Python 爬虫技术/ 异步爬虫 aiohttp
9

异步爬虫 aiohttp

aiohttp: 10x Faster

requests 串行爬 100 个网页要 100 秒,aiohttp 并发爬只要 1-2 秒。asyncio + aiohttp 是爬虫提速的终极武器。

基础:ClientSession

pip install aiohttp import aiohttp, asyncio async def fetch(url): async with aiohttp.ClientSession() as session: async with session.get(url) as resp: return resp.status, await resp.text() async def main(): status, text = await fetch("https://example.com") print(status, len(text)) asyncio.run(main())

并发:gather + Semaphore 限流

async def fetch_limited(session, url, sem): async with sem: # 同时最多10个 async with session.get(url) as resp: return url, resp.status async def main(urls): sem = asyncio.Semaphore(10) async with aiohttp.ClientSession() as session: tasks = [fetch_limited(session, u, sem) for u in urls] results = await asyncio.gather(*tasks) return results urls = ["https://example.com"] * 100 results = asyncio.run(main(urls))

连接池与 Session 复用

# 别每个请求建一个 ClientSession!一个 Session 复用连接池 import aiohttp connector = aiohttp.TCPConnector(limit=20) # 最大连接数 async def main(urls): # 整个爬取过程用一个 Session async with aiohttp.ClientSession(connector=connector) as session: # ... 用这个 session 发所有请求 pass

请求头、Cookie 与错误处理

# aiohttp 完整请求:headers / cookies / 超时 / 重试 import aiohttp, asyncio headers = {"User-Agent": "Mozilla/5.0 ..."} cookies = {"sessionid": "abc123"} timeout = aiohttp.ClientTimeout(total=10) async def fetch(session, url): try: async with session.get(url, headers=headers, cookies=cookies, timeout=timeout) as resp: if resp.status == 200: return await resp.text() else: print(f"{url} 状态 {resp.status}") except Exception as e: print(f"{url} 失败:{e}") return None

完整案例:同步 vs 异步速度对比

# speed_test.py —— 10个请求,对比 requests 和 aiohttp import time, requests, asyncio, aiohttp URL = "https://httpbin.org/delay/1" # 服务器延迟1秒返回 URLS = [URL] * 10 # 同步:串行等,10秒 start = time.time() [requests.get(u) for u in URLS] print(f"同步:{time.time()-start:.2f}s") # 异步:并发,约1秒 async def async_main(): sem = asyncio.Semaphore(10) async with aiohttp.ClientSession() as s: async def one(u): async with sem: async with s.get(u) as r: return r.status await asyncio.gather(*[one(u) for u in URLS]) start = time.time() asyncio.run(async_main()) print(f"异步:{time.time()-start:.2f}s")

输出

# 同步:10.23s # 异步:1.15s # 10倍提速,请求越多越夸张

异步解析:aiohttp + BeautifulSoup

# aiohttp 拿 HTML,BS4 解析(解析是 CPU 操作,同步) async def parse_page(session, url, sem): async with sem: async with session.get(url) as resp: html = await resp.text() # 解析(同步操作,但很快) soup = BeautifulSoup(html, "lxml") title = soup.select_one("h1").text return title

完整案例:异步爬 100 个网页

# async_crawl.py —— aiohttp 爬 100 个 URL import aiohttp, asyncio, time URLS = [f"https://httpbin.org/delay/1"] * 100 async def fetch(session, url, sem): async with sem: try: async with session.get(url, timeout=10) as resp: return resp.status except: return 0 async def main(): sem = asyncio.Semaphore(20) async with aiohttp.ClientSession() as session: tasks = [fetch(session, u, sem) for u in URLS] results = await asyncio.gather(*tasks) print(f"成功 {results.count(200)}/100") start = time.time() asyncio.run(main()) print(f"耗时 {time.time()-start:.2f}s")

输出

# 成功 100/100 # 耗时 5.32s # 同步 requests 串行要 100s,异步 20 并发只要 5s

aiohttp 和 requests 怎么选

requestsaiohttp
速度串行慢并发快 10 倍+
复杂度简单要懂 async/await
适合少量请求、脚本几百上千个请求
错误处理try/excepttry/except + gather
别无限制并发

不加 Semaphore,1000 个请求同时飞出去——对方服务器直接 DOS,你也立刻被封 IP。Semaphore 限并发(一般 5-20),既快又礼貌。