很多网页用 requests 拿到的 HTML 里没有数据:数据是页面加载后由 JavaScript 请求接口取来的。找到那个接口,直接请求它,往往比启动浏览器渲染便宜得多。这篇用 Chrome 的 DevTools 协议记录一个本机单页应用的请求,再逐步弄清直接重放需要什么。
状态 200,HTML 大小 136 字节;<li> 个数 0;<script src> = ['/app.js'];ul#list 里的内容 [] ——数据不在 HTML 里,要等 JS 去取
首页只有骨架和一个 <script src>。数据由 /app.js 里的 fetch 请求接口得到,再用 JS 写进 DOM。
从打开页面到 JS 渲染完成:63 ms;DOM 里渲染出 25 条,第一条 'Item 1 - 10.37'
方法 路径 状态 类型 发起者 请求头里值得注意的
GET / 200 Document other
GET /app.js 200 Script parser
GET /api/config 200 Fetch script {}
GET /api/products?page=1&limit=10 200 Fetch script {'X-Requested-With': 'XMLHttpRequest'}
GET /api/products?page=2&limit=10 200 Fetch script {'X-Requested-With': 'XMLHttpRequest'}
GET /api/products?page=3&limit=10 200 Fetch script {'X-Requested-With': 'XMLHttpRequest'}
POST /api/track ? Ping script {'Content-Type': 'text/plain;charset=UTF-8'}
GET /favicon.ico ? Other other 通过 Chrome DevTools 协议的 Network 事件,可以看到页面发的每个请求:方法、URL、资源类型(Document、Script、Fetch、Ping)、发起者、响应状态。本例 7 个请求里 3 个 /api/products 才是数据;/api/config 是配置,/api/track 是统计。再用 Network.getResponseBody 可以读到响应内容。
什么都不带:401;只带 Cookie:403;只带 X-Requested-With:401;两者都带:200 → 缺 Cookie 返回 401,缺 X-Requested-With 返回 403;这两个都是浏览器自动带上的,直接请求时要自己补(Cookie 的值来自第一次访问 / 时服务器设置的 Set-Cookie)
直接请求接口会遇到 401(没有会话 Cookie)和 403(没有 X-Requested-With 头)。Cookie 来自首页响应的 Set-Cookie,用 requests.Session 先访问首页即可;必需的头可以从 CDP 记录的请求头里看出来。这个服务器是我写的,真实站点的校验可能更多。
3 次请求取到 25 条,2 ms(浏览器从打开页面到渲染完成要 63 ms,这里 Chrome 已经启动好,时间包括加载页面、运行 JS 和同样的 3 次接口请求;两者都在本机回环上,绝对值只供比较) 条数一致:True;名称一致:True;API 返回的字段:['id', 'name', 'price_cents', 'internal_sku'] DOM 里只显示了 名称 和 价格(元);API 里还有页面没显示的字段:internal_sku='SKU-00001',价格是分 price_cents=1037
三次请求取到 25 条,和 DOM 里渲染出的一致;而且接口返回的字段比页面显示的多(内部编号、以分为单位的价格)。是否应该使用这些额外字段,取决于你的数据用途和站点的使用条款——本文没有涉及。