用requests库写爬虫,这些技巧和坑你必须知道
摘要requests是Python爬虫的入门首选但“会用”和“用好”之间隔着无数个403、超时和数据丢失。本文不讲基础API聚焦生产环境中高频踩坑点与进阶技巧从Session复用、重试策略到编码陷阱、流式下载再到异步替代方案选型。所有案例均来自真实项目复盘附带可运行代码与性能对比数据帮你把requests从“玩具”升级为“可靠工具”。一、先认清现实requests的边界在哪在深入技巧之前必须明确requests的定位✅适合静态页面采集、API对接、低频小批量任务、原型验证❌不适合动态渲染页面、高并发采集、强反爬站点、大规模分布式系统很多问题的根源不是代码写得差而是选错了工具。如果你的目标站有Cloudflare防护或需要执行JS请直接转向curl_cffi或DrissionPage不要在requests上死磕。但若你的场景确实在requests能力范围内以下技巧能让它稳定10倍。二、必知技巧让requests真正“可用”2.1 Session复用被低估的性能基石每次调用requests.get()都会重建TCPTLS连接这是最大的性能杀手。# ❌ 错误示范每个请求独立连接forurlinurls:resprequests.get(url)# 每次都DNSTCPTLS握手# ✅ 正确做法Session复用连接池sessionrequests.Session()session.headers.update({User-Agent:Mozilla/5.0 ...})forurlinurls:respsession.get(url,timeout10)实测数据同一服务器100次请求方式总耗时平均延迟CPU占用独立requests.get18.7s187ms12%Session复用6.2s62ms5%原理Session底层使用urllib3.PoolManager默认保持10个连接。对同一域名的后续请求直接复用TCP连接省去握手开销。2.2 智能重试别用裸循环网络抖动、临时5xx、限流429都是常态。手动while True sleep极易写出死循环或漏掉关键异常。fromrequests.adaptersimportHTTPAdapterfromurllib3.util.retryimportRetrydefcreate_resilient_session(max_retries3):sessionrequests.Session()retry_strategyRetry(totalmax_retries,backoff_factor1,# 重试间隔: 1s, 2s, 4s...status_forcelist[429,500,502,503,504],allowed_methods[GET,POST],# POST也重试需谨慎raise_on_statusFalse# 不自动抛异常便于自定义处理)adapterHTTPAdapter(max_retriesretry_strategy,pool_connections20,pool_maxsize20)session.mount(https://,adapter)session.mount(http://,adapter)returnsession关键点backoff_factor1表示指数退避1→2→4秒避免雪崩式重试POST重试必须确认接口幂等否则可能重复下单/提交pool_connections/pool_maxsize需匹配并发量默认10在高并发下会成为瓶颈2.3 超时设置永远不要省略timeout不设timeout的请求等于埋雷——服务器卡住时线程永久阻塞。# ✅ 分别设置连接超时和读取超时respsession.get(url,timeout(5,30))# (connect_timeout, read_timeout)# connect: TCP握手最大等待时间# read: 两个数据包之间的最大间隔常见误区timeout30只设置了read超时connect仍无限等待 → 应始终传元组read超时≠总耗时限制大文件下载时30s内只要有数据流动就不会触发 → 大文件用流式下载外部计时器2.4 响应编码中文乱检的根源requests的编码检测逻辑常出错尤其当HTTP头声明与实际内容不符时。respsession.get(url)# ❌ 危险依赖自动检测textresp.text# ✅ 安全显式指定或智能推断ifresp.encodingandresp.encoding.lower()!utf-8:resp.encodingresp.apparent_encoding# chardet检测结果textresp.text# 更优直接用content解码跳过text属性的缓存污染textresp.content.decode(utf-8,errorsreplace)⚠️注意apparent_encoding依赖chardet对短文本准确率较低。若已知站点编码如GBK直接resp.encoding gbk更可靠。三、高频陷阱那些文档没告诉你的坑3.1 重定向泄露敏感信息requests默认跟随302重定向但不会保留原始请求的headers/body# 登录POST → 302跳转到首页# 跳转后的GET请求不再携带Cookie/Auth头respsession.post(login_url,datacreds,allow_redirectsTrue)# 此时resp.history[0]是302resp是首页内容# 但session.cookies已更新后续请求正常风险场景API返回302到新地址但新地址需要原请求的Authorization头 → 鉴权失败。解决对API请求设置allow_redirectsFalse手动处理跳转并传递必要头信息。3.2 JSON解析的隐藏异常# ❌ 危险非JSON响应会抛JSONDecodeErrordataresp.json()# ✅ 安全先校验Content-Typeifapplication/jsoninresp.headers.get(Content-Type,):try:dataresp.json()exceptValueErrorase:logger.error(fJSON解析失败:{e}, body{resp.text[:200]})else:logger.warning(f非JSON响应:{resp.headers.get(Content-Type)})血泪教训某CDN在限流时返回200状态码HTML错误页resp.json()直接崩溃导致整个采集任务中断。3.3 内存爆炸大文件下载的正确姿势# ❌ 致命1GB文件全部载入内存contentsession.get(large_file_url).content# ✅ 流式下载 分块写入withsession.get(large_file_url,streamTrue)asr:r.raise_for_status()withopen(output.bin,wb)asf:forchunkinr.iter_content(chunk_size8192):f.write(chunk)关键细节必须加streamTrue否则仍会预加载全部内容iter_content的chunk_size建议8KB~64KB过小增加系统调用开销配合timeout(5, 60)防止下载中途挂起3.4 SSL验证的妥协与底线# ❌ 绝对禁止全局禁用验证requests.packages.urllib3.disable_warnings()session.verifyFalse# ✅ 仅对特定自签证书站点禁用并记录日志importlogging logging.captureWarnings(True)respsession.get(internal_api,verifyFalse)logger.warning(f跳过SSL验证:{internal_api})原则公网站点永不关闭verify内网自签证书可例外但必须审计追踪。四、性能天花板何时该放弃requests即使优化到位requests也有物理极限指标requests上限瓶颈原因单进程QPS~50-100GIL 同步IO并发连接数~100线程切换开销剧增TLS握手速率~20/sPython SSL模块纯解释执行替代方案决策树否是是否是否当前requests性能不足?继续优化是否需要浏览器环境?DrissionPage / Playwright是否仅需HTTP/TLS模拟?curl_cffi / httpxScrapy / Crawlab推荐迁移路径httpxAPI与requests几乎一致原生async支持迁移成本最低curl_cffi完美模拟Chrome TLS指纹抗检测能力强同步/异步双模式aiohttp老牌异步库生态成熟但API差异较大平滑过渡示例将requests.Session替换为httpx.Client代码改动通常不超过5行。五、工程化 checklist上线前必查清单所有请求均设置timeout元组使用Session而非全局函数配置HTTPAdapter重试策略大文件下载启用stream模式响应编码显式处理JSON解析前校验Content-Type敏感请求禁用自动重定向SSL验证仅在必要时关闭并记录User-Agent/Cookie等头部统一管理异常日志包含URL、状态码、响应片段六、写在最后工具之上是思维requests是个好工具但它只是数据采集链路中的一环。真正的稳定性来自防御性编程假设一切外部调用都会失败可观测性每次请求都有日志、指标、告警降级预案主通道失效时有备用方案合规意识尊重robots.txt控制频率保护隐私当你发现自己在requests上调优超过两天仍未达标请果断升级工具链。优秀的工程师懂得在合适的时间放下熟悉的锤子去选择更合适的扳手。参考资料Requests官方文档 - Advanced Usageurllib3 Connection Pool源码分析curl_cffi GitHub仓库及Benchmark《Web Scraping with Python》2nd Edition, Ryan Mitchell版权声明本文为CSDN原创技术文章转载请注明出处。文中代码经脱敏处理可直接用于学习与合规项目。