在公共卫生监测领域,每分钟都在产生大量关键数据。去年参与某流行病监测项目时,我们团队曾面临一个棘手问题:传统爬虫方案每天只能获取不到60%的目标数据,且频繁遭遇IP封禁。这直接影响了数据分析的时效性和完整性。
现代网站普遍采用的反爬机制主要包含三个层面:
我们的解决方案需要同时突破这三重防线。经过两个月的实战迭代,最终形成的技术方案实现了98.7%的数据获取成功率,平均延迟控制在300ms以内。下面将完整还原这套系统的技术细节。
选择aiohttp而非Requests的主要原因:
典型异步爬虫骨架:
python复制import aiohttp
import asyncio
async def fetch(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
connector = aiohttp.TCPConnector(limit=100) # 控制并发连接数
async with aiohttp.ClientSession(connector=connector) as session:
tasks = [fetch(session, url) for url in url_list]
return await asyncio.gather(*tasks)
关键参数说明:TCPConnector的limit值建议设为目标网站允许的最大并发数(可通过检测响应头的X-RateLimit字段获取)
Playwright相比Selenium的优势:
处理动态内容的黄金组合:
python复制from playwright.async_api import async_playwright
async def render_dynamic_page(url):
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
page = await browser.new_page()
# 关键等待策略
await page.goto(url, wait_until="networkidle")
await page.wait_for_selector(".data-table")
content = await page.content()
await browser.close()
return content
我们采用分层代理方案:
代理轮换实现逻辑:
python复制class ProxyRotator:
def __init__(self):
self.proxy_list = self._load_proxies()
self.current_index = 0
def get_next_proxy(self):
proxy = self.proxy_list[self.current_index]
self.current_index = (self.current_index + 1) % len(self.proxy_list)
return {
"http": f"http://{proxy.ip}:{proxy.port}",
"https": f"http://{proxy.ip}:{proxy.port}"
}
完整请求头模板:
python复制headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
"Accept": "text/html,application/xhtml+xml",
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Connection": "keep-alive",
"Referer": "https://www.google.com/",
"Upgrade-Insecure-Requests": "1"
}
注意事项:每个目标网站需要单独分析其请求头特征,使用Chrome开发者工具的Network面板抓取真实流量
分片集群配置示例:
yaml复制# mongod.conf
storage:
wiredTiger:
engineConfig:
cacheSizeGB: 2 # 建议分配物理内存的50%
sharding:
clusterRole: "shardsvr"
索引优化策略:
典型ETL流程:
python复制def clean_data(raw):
# 处理缺失值
df = raw.fillna({
'cases': 0,
'deaths': -1 # -1表示数据缺失
})
# 标准化字段
df['date'] = pd.to_datetime(df['date'], format='%Y-%m-%d')
df['region'] = df['region'].str.upper()
# 去重处理
return df.drop_duplicates(subset=['date', 'region'])
使用Plotly Express创建交互式地图:
python复制import plotly.express as px
def create_heatmap(df):
fig = px.density_mapbox(df,
lat='lat',
lon='lon',
z='cases',
radius=20,
center=dict(lat=30, lon=0),
zoom=2,
mapbox_style="stamen-terrain")
fig.update_layout(title="实时疫情热力图")
return fig
基于Z-Score的简单预警实现:
python复制from scipy import stats
def detect_anomalies(series, threshold=3):
z_scores = stats.zscore(series)
return np.where(np.abs(z_scores) > threshold)
优化后的Dockerfile:
dockerfile复制FROM python:3.9-slim
RUN apt-get update && \
apt-get install -y \
gcc \
python3-dev \
libssl-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "scheduler.py"]
使用APScheduler实现智能重试:
python复制from apscheduler.schedulers.blocking import BlockingScheduler
scheduler = BlockingScheduler()
@scheduler.scheduled_job('interval', hours=1, max_instances=3)
def crawl_job():
try:
run_crawler()
except Exception as e:
logger.error(f"Job failed: {str(e)}")
# 指数退避重试
scheduler.add_job(run_crawler, 'date', run_date=datetime.now()+timedelta(minutes=5))
在三个月生产环境运行中,我们积累的关键经验:
python复制def adaptive_delay(last_response_time):
base_delay = 1.0 # 基础延迟
if last_response_time > 500: # 毫秒
return base_delay * 1.5
return max(base_delay * 0.9, 0.2) # 最低200ms
这套系统目前稳定运行在20个数据源的实时采集场景,日均处理请求量超过200万次。最令人满意的不是技术指标,而是它真正帮助公共卫生团队提前7天预测到了某次区域性爆发。