Python爬虫进阶的核心在于突破限制 与架构设计 。这不仅仅是发送一个requests.get(),而是涉及到网络协议、浏览器渲染、数据流控制和反爬对抗。
1. 异步并发与速率控制(性能瓶颈突破)
当需要采集百万级数据时,同步IO会成为瓶颈。进阶需要使用asyncio + aiohttp,但直接暴力并发会瞬间被Ban,因此必须引入令牌桶算法控制速率。
python
import asyncio
import aiohttp
from asyncio import Semaphore
class AsyncRateLimiter:
"""滑动窗口速率控制器(令牌桶变种)"""
def __init__(self, rate, per):
self.semaphore = Semaphore(rate) # 最大并发数
self.interval = per / rate # 每次请求间隔
async def acquire(self):
await self.semaphore.acquire()
asyncio.create_task(self._release())
async def _release(self):
await asyncio.sleep(self.interval)
self.semaphore.release()
async def fetch(session, url, limiter):
await limiter.acquire()
async with session.get(url) as resp:
return await resp.text()
# 运行逻辑:每秒最多50个请求,且严格间隔0.02秒
2. 应对复杂JavaScript渲染(Pyppeteer 底层控制)
对于SPA应用,Selenium太慢。进阶使用pyppeteer(无头Chrome的Python异步接口)。高级技巧是拦截请求 和注入JS来绕过检测。
python
import asyncio
from pyppeteer import launch
async def smart_render():
browser = await launch(headless=True, args=['--no-sandbox'])
page = await browser.newPage()
# 1. 拦截网络请求(阻塞图片和字体,大幅提速)
await page.setRequestInterception(True)
page.on('request', lambda req: asyncio.ensure_future(
req.abort() if req.resourceType in ['image', 'font', 'media'] else req.continue_()
))
# 2. 在页面上下文中覆写WebDriver检测属性
await page.evaluateOnNewDocument('''
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
window.chrome = { runtime: {} };
''')
await page.goto('https://target.com', {'waitUntil': 'networkidle2'})
# 3. 等待特定元素出现(比time.sleep智能)
await page.waitForSelector('#data-loaded', {'timeout': 10000})
content = await page.content()
await browser.close()
return content
3. 深度指纹对抗(TLS 指纹伪装)
服务端不仅看User-Agent,还通过JA3指纹检测Python的urllib3/requests库特征。进阶必须使用curl_cffi模拟真实浏览器的TLS握手。
python
from curl_cffi import requests
# 冒充 Chrome 110 的 TLS 指纹和 HTTP/2 优先级设置
response = requests.get(
'https://httpbin.io/headers',
impersonate="chrome110", # 这一行关键:伪造底层加密套件
timeout=30
)
# 若结合代理,需额外处理代理头的转发
proxies = {"http": "socks5://127.0.0.1:1080", "https": "socks5://127.0.0.1:1080"}
response = requests.get('https://target.com', impersonate="edge101", proxies=proxies)
4. 增量式去重与布隆过滤器(大规模存储优化)
当数据量达到千万级,用Redis Set存储URL会耗尽内存。使用布隆过滤器(Scalable Bloom Filter)只存指纹,允许极低误判率,大幅节省空间。
python
from pybloom_live import ScalableBloomFilter
# 自动扩容的布隆过滤器,误判率 0.001%
url_bloom = ScalableBloomFilter(mode=ScalableBloomFilter.SMALL_SET_GROWTH)
def is_duplicate(url):
# 只存 URL 的哈希指纹,不存原文
if url in url_bloom:
return True
url_bloom.add(url)
return False
# 在异步管道中的使用
async def save_item(item):
fingerprint = f"{item['title']}_{item['publish_date']}"
if not is_duplicate(fingerprint):
await db.insert(item) # 异步写入数据库
5. 动态代理池与重试中间件(高可用架构)
进阶不是简单地用proxies参数,而是构建中间件,自动检测代理失效并切换。通过装饰器实现熔断重试。
python
import random
from functools import wraps
from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception
def proxy_retry_middleware(func):
@wraps(func)
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10),
retry=retry_if_exception(lambda e: "403" in str(e) or "Timeout" in str(e))
)
async def wrapper(*args, **kwargs):
# 从队列中取出可用代理(通过Redis维护高可用池)
proxy = await get_available_proxy()
kwargs['proxy'] = f"http://{proxy}"
try:
return await func(*args, **kwargs)
except Exception as e:
# 标记代理失效,从池中移除
await mark_proxy_dead(proxy)
raise e # 抛出让 tenacity 重试
return wrapper
6. 分布式调度器(去中心化协调)
使用Redis + Lua脚本实现原子性的任务分发,避免多台爬虫服务器抢占同一个任务导致重复。
lua
-- acquire_task.lua
local key = KEYS[1]
local task = redis.call('rpop', key) -- 从右端取
if task then
redis.call('lpush', 'processing_queue', task) -- 放入处理中队列
redis.call('expire', 'processing_queue', 600) -- 超时回收
end
return task
python
import redis
r = redis.Redis(host='localhost', decode_responses=True)
# Python 调用 Lua 实现原子操作
sha = r.script_load("""
local task = redis.call('rpop', KEYS[1])
if task then redis.call('lpush', 'processing', task) end
return task
""")
task_data = r.evalsha(sha, 1, 'task_queue')
总结:进阶思维导图
| 层面 | 初级做法 | 进阶做法 |
|---|---|---|
| 并发 | ThreadPoolExecutor |
asyncio + 令牌桶限流 |
| 渲染 | time.sleep 硬等 |
waitForSelector + 请求拦截 |
| 反爬 | 换 UA/加延时 | TLS指纹篡改 + JS注入 |
| 存储 | MySQL 全量存储 | 布隆过滤器 + 增量更新 |
| 架构 | 单机运行 | Lua原子任务 + 代理中间件 |