我见过太多人,用 Python requests 库两年了,翻来覆去就是 requests.get() 和 requests.post()。偶尔加个 headers,改个 data 格式。别的功能?基本没碰过。这不怪你,真的。日常写脚本,爬个不需要登录的页面,确实够了。
但你有没有遇到过这种情况。明明一个接口就在那里,你手动拿浏览器访问,数据全回来了。换成 requests 怎么都拿不到。你改 headers,加上 User-Agent,还是返回空。你怀疑是不是 IP 被封了,换代理继续试,依然不行。这时候问题往往出在 Session 对象上。
你每次请求都新建一个独立的 Session,等于浏览器每次打开新窗口。人家服务器不认你。正确的做法是用同一个 Session 发请求,就像同一个浏览器标签页。Session 会自动帮你处理 Cookie。你不需要手动从响应里提取 Set-Cookie 再手动加到下一次请求里。
有了 Session,你还可以干另一件事。挂载适配器。你设置一个 HTTPAdapter,告诉 Session 超时时间设成 30 秒,最多重试 3 次。这样网络波动的时候,你的程序不会直接崩溃报错。
很多人连接口都没有响应就不动了。其实你可以对响应对象做很多事情。比如 response.elapsed,直接告诉你这次请求花了多少秒。你在业务代码里埋一个日志,记录每个接口的耗时。哪个接口变慢了,你一眼就能看到。
response.history 也挺好用。它会列出所有重定向记录。你写爬虫的时候,可以用它跟踪跳转链,看网站是怎么把你带到最终页面的。如果中间有 302 跳转到登录页,你加一个登录逻辑就行。
还有 response.cookies 和 response.headers,这些大家都知道。但你知道 response.links 吗?很多 API 会在响应头里带分页链接信息。直接用 response.links['next'] 就能拿到下一页的 URL,根本不用自己解析头字段。
文件上传也是个常见的需求。很多人用 files 参数传文件,但没注意 files 参数可以传一个元组,里面带上文件名和文件类型。比如 files={'file': ('photo.jpg', open('img.jpg','rb'), 'image/jpeg')}。有些接口校验文件名的后缀,你传错了类型就 400 了。
处理超时也需要注意。很多人只写了一个 timeout=3,以为设了一个全局超时。其实 requests 的 timeout 是连接超时和读取超时的总和。你想单独控制,可以传一个元组:timeout=(2, 5)。前面是连接服务器的时间,后面是服务器返回数据的时间。
你写循环请求的时候,可能会遇到连接被耗尽的问题。因为默认情况下,每次请求都会创建新的 TCP 连接。用 Session 并且设置 keep_alive=True,连接会被复用。你还可以通过 urllib3 的 PoolManager 配置连接池大小,避免高并发时资源不够。
有些朋友喜欢用 try except 捕获所有异常,然后打印一个错误提示就继续运行。更精确的做法是捕获 requests.exceptions.Timeout 和 requests.exceptions.ConnectionError。不同异常代表不同问题,你分别处理,日志里才能看出重试策略对不对。
还有种情况,你请求一个 API,返回的 JSON 数据嵌套很深。你去手动提取很麻烦。其实你可以用 response.json() 直接解析,然后配合 Python 的 .get() 方法加默认值。这样就算字段不存在,程序也不会崩。
代理配置也要灵活点。你写一个 proxies = {'http': '...', 'https': '...'} 就能给 Session 挂上代理。你再配合 get 请求时的 proxies 参数,可以随时覆盖全局代理。某个请求需要走不同代理,单独传一次就行。
最容易被忽略的,是 SSL 证书校验。你开发环境可能关掉了校验,写到生产环境忘了打开。这时候最好显式传一个 verify='/path/to/cert.pem',或者用 requests.packages.urllib3.disable_warnings() 关掉警告但不关闭校验。
你写一个自动注册账号的脚本,发现每次都报验证错误。可能你少处理了 CSRF Token。你手动查看页面源码,找到 Token 的值,然后 GET 一次主页拿到 Token,再 POST 登录。这一招能解决很多类似场景。
你还可以用 requests.Request 对象构造一个预请求,然后调用 session.prepare_request(req) 得到 PreparedRequest。这个对象可以被修改,比如你在发送前检查 URL 是不是正确的,或者手动改一下 headers。调试的时候很好用。
很多人觉得 requests 太简单,不值得深入研究。但它就像一个工具箱,你只用了起子和锤子。里面还躺着扳手、钳子、电钻。你现在回头看看,之前花一整天调试的 bug,可能只是缺了一个 Session 或者一个正确的超时设置。
用两年也好,用两年半也罢。从今天开始,让 requests 不只是 get 和 post。它值得你多花半天时间,把文档剩下部分过一遍。你会发现,原来那些接头接口、异常情况、重试逻辑,都能用简单的代码搞定。