1. 本课定位:是什么、为何重要
上一课我们假设:打开页面源码,数据就在 HTML 标签里,用选择器就能抽。可你一定遇到过另一种网站------浏览器里列表清清楚楚,一查看网页源代码却是转圈空壳。那不是你选择器没写好,而是数据根本稍后才通过 XHR/Fetch 拉 JSON 填上去。
这一课主线叫「接口优先」:先找到 JSON 接口,用 requests 直接拿字段;浏览器自动化只作半页扫盲,不是必会实战。它把 Day33/34 的 HTTP 肌肉和 Day37 的解析场景接起来,并告诉你何时不该爬页面。
第 37 课假设 HTML 里已经有 要抽的字。
现实中很多站:首屏 HTML 是空壳,列表数据由浏览器再发 XHR/Fetch 拉 JSON 填上------这就是「页面上看得到,View Source 里没有」。
| 路径 | 做法 | 评价 |
|---|---|---|
| 硬爬最终 HTML | 等 JS 渲染完再解析 | 慢、脆、重 |
| 接口优先(主菜) | 直接请求 JSON 接口 | 稳、省、好存 |
| 浏览器自动化(半页) | Playwright / Selenium | 最后手段 |
为何重要: 能少爬页面就少爬;能走官方/公开 API 就走 API------采集与对接业务接口是同一套 HTTP 肌肉(Day33/34)。
对比已学:
| Day37 静态 HTML | 本课 |
|---|---|
| 选择器抽文本 | JSON 键取值 |
| 怕改版 class | 怕接口鉴权与限流 |
| BeautifulSoup | requests + r.json() |
| 本地 sample 可控 | 真实 API 有限额与字段变更 |
2. 为什么会有「空壳页」?
先用一张浏览器时序图建立直觉:先拿到壳 HTML,再请求 /api/items,JS 再渲染。你以为数据在 HTML,实际在第二次请求。
想通这一点,后面「打开 Network」才有动力。只 requests 页面 URL 可能一无所获;改请求 API 才能拿到数组字段。
典型前端流程:
text
浏览器
|-- GET /page --> 得到壳 HTML(转圈、空列表)
|-- GET /api/items --> 得到 JSON 数据
|-- JS 把 JSON 填进 DOM --> 你才「看见」列表
| 你以为 | 实际 |
|---|---|
| 数据在 HTML 里 | 数据在第二次请求里 |
| 该加强选择器 | 该打开 Network 找 API |
修改前(只 requests 页面 URL): 可能只有 loading 结构。
修改后(请求 /api/items): 直接拿到数组字段。
3. 本质、约束与坑
动态页本质是壳加数据请求;数据请求往往就是普通 HTTP API。接口优先三步:Network、筛 XHR、用客户端复现。
同时摊开约束:鉴权、限流、合规、字段变更;以及一上来就 Selenium、漏 Header、死磕选择器等坑。技术选择之前,先把边界立住。
本质: 动态页 =「壳 HTML」+「数据请求」。数据请求往往就是普通 HTTP API。
接口优先三步:
- 开发者工具 → Network
- 筛选 Fetch/XHR,找响应为 JSON 的请求
- 复制 URL / 方法 / 必要 Header,改写成
requests/httpx
HTML 方式: 下大页 + 易碎 CSS。
API 方式: 字段如 full_name、stargazers_count 直接可用。
约束:
- 有的接口要登录 Cookie / Token。
- 有频率限制(如 GitHub 匿名限额)。
- 未授权乱抓可能违法违规------本课只用公开 API 演示。
- 字段名可能变更,要写容错(
.get)。
常见坑:
| 坑 | 说明 |
|---|---|
| 一上来就上 Selenium | 成本高,维护难 |
| 忽略 Request Headers | 缺 UA/Token 会 401/403 |
| 把 HTML 解析失败全怪选择器 | 可能根本不该解析 HTML |
| 把浏览器里的「查询参数」抄错 | 漏参数导致空数据 |
| 无 timeout | 程序挂死(Day34 教训) |
4. 能力分组
把本课能力拆成发现、复现、抽取、存储、自动化扫盲五类,对应工具从 DevTools 到 requests 再到 sqlite。
分组是为了学习路径清晰:你不是在学「又一个库」,而是在学一条从浏览器到入库的工作流。
| 类别 | 做什么 | 工具 |
|---|---|---|
| 发现 | Network 里找 JSON 请求 | 浏览器 DevTools |
| 复现 | 带齐必要头发请求 | requests / httpx |
| 抽取 | data["key"] / 多层 dict |
Python |
| 存储 | 入库 | sqlite3(Day39) |
| 自动化扫盲 | 知步骤与成本 | Playwright/Selenium 概念 |
5. Network 面板怎么看(操作说明)
即使本课演示直接给了公开 API,你也必须会自己在浏览器里找接口------这是工作中的核心技能。
按步骤走:F12、筛 Fetch/XHR、刷新或点加载更多、看 Headers 与 Preview。像侦探一样:页面是海报,出货单在 API。
即使本课实践直接给公开 API,你也要会自己找:
| 步骤 | 动作 |
|---|---|
| 1 | F12 → Network |
| 2 | 勾选 Fetch/XHR(或 All 里找 type=json) |
| 3 | 刷新页面或点一下「加载更多」 |
| 4 | 点开请求 → Headers 看 URL/方法 |
| 5 | Preview/Response 看是否为 JSON 列表 |
| 6 | 若需要,看 Request Headers 里的 Authorization |
像侦探: 页面只是海报,真正的货在仓库出货单(API)上。
6. JSON 抽取小步
找到 JSON 后,取值比 HTML 简单得多:dict 的键。但要用 get 做容错,避免 KeyError。
这一节用最小 dict 例子练 full_name、嵌套 slideshow,为后面 GitHub 实战预热。
python
data = {
"name": "cpython",
"full_name": "python/cpython",
"stargazers_count": 1,
}
print(data.get("full_name"))
print(data.get("stargazers_count"))
print(data.get("not_exist")) # None,不抛错
预期输出:
text
python/cpython
1
None
多层时:
python
slideshow = data.get("slideshow") or {}
title = slideshow.get("title")
7. 实践:公开 JSON API(主菜)
用 GitHub 公开仓库接口跑通「不打开浏览器也能拿元数据」。带 UA、timeout、raise_for_status,打印字段。
star 数会变,正常。重点是链路跑通,并理解 print 不会改远端数据。自动化示意只打印 JSON 说明,不要求装驱动。
GitHub 公开仓库接口(无需浏览器):
bash
mkdir -p ~/python-lab/src/day38
cd ~/python-lab/src/day38
pip install requests
Windows 推荐 Cygwin 或 WSL。
bash
cat > api_first_demo.py << 'EOF'
# Day38: API-first over scraping HTML
import json
import requests
def main():
url = "https://api.github.com/repos/python/cpython"
print("--- fetch json api ---")
r = requests.get(
url,
timeout=20,
headers={"User-Agent": "python-lab-day38/1.0", "Accept": "application/json"},
)
r.raise_for_status()
data = r.json()
print("name:", data.get("name"))
print("full_name:", data.get("full_name"))
print("stargazers_count:", data.get("stargazers_count"))
print("language:", data.get("language"))
print("html_url:", data.get("html_url"))
print("--- compare ---")
print("HTML way: download big page + CSS selectors, brittle when layout changes")
print("API way: stable fields in JSON, fewer surprises, easier to store")
print("--- automation sketch (not required this lesson) ---")
sketch = {
"tool": "playwright or selenium",
"steps": ["launch browser", "goto url", "wait selector", "text_content"],
"cost": "slow, fragile, heavy",
"prefer": "official API / XHR JSON first",
}
print(json.dumps(sketch, ensure_ascii=False))
if __name__ == "__main__":
main()
EOF
python3 api_first_demo.py
实测输出(star 数会变):
text
--- fetch json api ---
name: cpython
full_name: python/cpython
stargazers_count: 73867
language: Python
html_url: https://github.com/python/cpython
--- compare ---
HTML way: download big page + CSS selectors, brittle when layout changes
API way: stable fields in JSON, fewer surprises, easier to store
--- automation sketch (not required this lesson) ---
{"tool": "playwright or selenium", "steps": ["launch browser", "goto url", "wait selector", "text_content"], "cost": "slow, fragile, heavy", "prefer": "official API / XHR JSON first"}
调用前: 只有 URL。
调用后: 本地 dict 持有仓库元数据;GitHub 上的 star 不会 因你 print 而改变。
7.1 为什么要带 User-Agent?
部分服务对默认/空 UA 不友好;教学与对接时写明自己的客户端名,便于对方识别与排错。
7.2 raise_for_status 在这里的意义
| 状态 | 不 raise 的风险 |
|---|---|
| 404 | 把错误页当 JSON 解析 |
| 403 | 误以为字段缺失 |
| 5xx | 用脏数据入库 |
8. HTML 方式 vs API 方式(对照加深)
同一目标「拿 cpython 的 star」,对比爬主页 HTML 与打 REST API 的步骤与成本。
结论几乎总是:有 API 走 API。HTML 是退路,不是默认首选。
假设目标:拿 python/cpython 的 star 数。
| 步骤 | HTML 思路 | API 思路 |
|---|---|---|
| 1 | GET 仓库主页 HTML | GET /repos/python/cpython |
| 2 | 找显示 star 的 span | data["stargazers_count"] |
| 3 | class 一变就挂 | 字段相对稳定(仍可能变) |
| 4 | 体积大 | JSON 小 |
结论: 同目标时 API 几乎总是更优;HTML 是没有接口时的退路。
9. 自动化半页扫盲(非本课必装驱动)
当确认没有接口、又必须浏览器事件时,才出现 Playwright/Selenium。这里只讲步骤与成本。
本课验收不要求安装浏览器驱动。避免一遇到动态站就条件反射上自动化。
当确实没有可用接口、又必须依赖浏览器事件时:
| 工具 | 角色 |
|---|---|
| Playwright | 现代、自动等元素较方便 |
| Selenium | 历史长、资料多 |
概念步骤:
text
启动浏览器 → 打开 URL → 等待选择器出现 → 取文本/点击 → 关闭
python
# 伪代码 / 示意,不是本课必跑
# from playwright.sync_api import sync_playwright
# with sync_playwright() as p:
# browser = p.chromium.launch()
# page = browser.new_page()
# page.goto("https://example.com")
# print(page.locator("h1").inner_text())
# browser.close()
| 成本 | 说明 |
|---|---|
| 慢 | 真渲染页面 |
| 重 | 要浏览器内核 |
| 脆 | UI 文案/结构一变就挂 |
| 难部署 | CI/服务器环境更麻烦 |
本课验收:会跑 API 脚本即可,不要求安装 Playwright/Selenium。
10. 伦理与法律(必读)
技术能做不等于允许做。合规、授权、限流、禁止撞库与未授权攻击------和数据课安全篇同一精神。
公开 API 也有配额与条款。红线举例帮助建立敬畏,而不是吓退学习。
- 遵守服务条款与 robots;有授权再抓
- 控制频率;不撞库、不做验证码黑产链路
- 禁止未授权攻击与隐私窃取
- 教学与内部系统优先官方 API
- 公开 API 也要遵守 rate limit 与署名要求
红线举例: 绕过付费墙批量扒取、打码平台破解登录、扫未授权内网接口。
11. 常见问答
Cookie、大 JSON、和 Scrapy 关系、star 数变化如何断言------集中答疑。
减少「我是不是该上框架/自动化」的焦虑,回到接口优先。
Q:接口要 Cookie 怎么办?
A:先确认是否允许;用官方 OAuth/Token;不要随手导出别人的登录 Cookie 滥用。
Q:JSON 很大怎么办?
A:只取需要字段;分页参数;流式/分批入库(进阶)。
Q:和「爬虫框架 Scrapy」关系?
A:框架帮你管调度与管道;本课先掌握「接口优先」的判断力。
Q:star 数每次不一样?
A:正常,真实数据在变;断言应用「有该字段且为 int」而不是写死数字。
12. 落地场景
同步仓库元数据、内部 list 接口、老站无接口、登录点选报表------分别优先什么策略。
场景是为了让你出门后会选型,而不是只会跑本课 demo。
| 场景 | 优先策略 |
|---|---|
| 同步 GitHub 仓库元数据 | REST API |
| 内部后台表格(有 list 接口) | 复现 XHR |
| 纯展示、无接口的老站 | 静态 HTML 或自动化(评估成本) |
| 需要登录点选的报表 | 自动化或 RPA(合规前提下) |
13. 请求头最小集合(复现 XHR 时)
从 DevTools 复现请求时,容易把浏览器几十个头全抄上。这一节强调最小必要集合。
只加需要的 UA、Accept、Authorization 等,保持请求干净可维护。
| Header | 何时需要 |
|---|---|
User-Agent |
经常需要 |
Accept: application/json |
明确要 JSON |
Authorization |
私有接口 |
Cookie |
仅限授权场景,慎用 |
Referer / 自定义头 |
少数站校验 |
原则: 只加必要头;从 DevTools 对照,而不是一次复制全部乱七八糟的浏览器头。
14. 状态码决策表(接 Day33/34)
接口优先仍是 HTTP:200 解析,401 查鉴权,429 降速,不要用 Selenium 硬闯权限。
把状态码从「背数字」变成「下一步动作」,和 Day34 稳健客户端衔接。
| 码 | 接口优先场景下的动作 |
|---|---|
| 200 | 解析 JSON、取字段 |
| 401/403 | 检查 Token/权限,不要改用 Selenium「硬闯」 |
| 404 | URL 抄错或资源不存在 |
| 429 | 降速、退避重试 |
| 5xx | 有限重试,仍失败则报警 |
15. 「什么时候才上自动化」检查单
用检查单克制自动化冲动:无接口、有授权、真依赖浏览器、能接受慢脆。
不满足就回去找 API 或静态 HTML。
同时满足再考虑:
- 已确认没有可用 JSON/XML 接口
- 有合规授权
- 交互确实依赖浏览器事件(复杂点击/验证码合规方案另议)
- 能接受慢与脆,并有维护预算
否则:继续接口或静态 HTML。
16. 字段容错模板
真实 JSON 可能缺字段或类型不对。给一个 pick_repo 式模板,避免流水线轻易崩溃。
修改前硬下标会炸,修改后可降级并继续------第 39 课 store 前很需要这习惯。
python
def pick_repo(data: dict) -> tuple[str, int]:
name = data.get("full_name") or data.get("name") or ""
stars = data.get("stargazers_count")
try:
stars_i = int(stars)
except (TypeError, ValueError):
stars_i = 0
return name, stars_i
修改前: data["stargazers_count"] 缺失直接炸。
修改后: 降级为 0,流水线可记录问题行。
17. 自我检查清单
打勾:空壳页、三步走、requests 拉 JSON、自动化非必须、合规。
过关即可进入流水线项目课。
- 能说明空壳页与 XHR 的关系
- 会描述接口优先三步
- 会用 requests 拉公开 JSON 并打印字段
- 知道自动化成本与非必选项
- 知道合规红线
18. 从浏览器复制为 cURL 再翻译(思路)
工作中常用 Copy as cURL,再翻译成 requests。比猜选择器快。
步骤:复制、识别方法 URL 头体、改写、删多余头。这是接口优先的实操路径。
许多浏览器支持:请求右键 → Copy as cURL。
| 步骤 | 说明 |
|---|---|
| 1 | 复制 cURL |
| 2 | 识别 -X 方法、URL、-H 头、--data 体 |
| 3 | 改写成 requests.get/post(..., headers=..., json=...) |
| 4 | 删掉明显无用的浏览器私有头,保留鉴权相关 |
这是工作中「接口优先」的常用路径,比猜选择器快。
19. 与第 37 / 39 课的分工
用流程图总结:能 API 吗→38/39;HTML 有数据吗→37;否则才评估自动化。
三课分工清晰,避免工具混用时脑子乱。
| 课 | 数据形态 | 你的动作 |
|---|---|---|
| 37 | 静态 HTML | 选择器解析 |
| 38 | JSON API / XHR | 本课:发现并请求 |
| 39 | 多源 API | 流水线入库验收 |
text
能 API 吗? --是--> 38/39
|
否
v
HTML 里有数据吗? --是--> 37
|
否
v
才评估自动化(半页认知)
20. 术语表(本课)
XHR、空壳页、接口优先、自动化、限流------一页小抄。
盖住解释自述一遍,查漏补缺。
| 术语 | 含义 |
|---|---|
| XHR / Fetch | 浏览器异步拉数据的请求 |
| 空壳页 | 首屏 HTML 几乎无业务数据 |
| 接口优先 | 先找 JSON/API 再考虑解析页面 |
| 自动化 | 用驱动操控真浏览器 |
| 限流 | 429 或配额,需降速 |
21. 课堂 10 分钟小练习(可不联网)
不联网也能练:给定假 JSON,打印 title,并回答还要不要渲染 HTML。
用来巩固「数据已在 JSON 里」的直觉。
给定假响应:
python
payload = {
"items": [
{"id": 1, "title": "A"},
{"id": 2, "title": "B"},
]
}
请写出:打印所有 title 的循环;并说明若这是 XHR 结果,你还需要浏览器渲染吗?
参考:
python
for it in payload["items"]:
print(it["title"])
# 不需要再渲染 HTML,数据已在 JSON 中
22. 再对比一次「错误路径」
三种路径对照:只存空壳 HTML、Selenium 点详情、一次 API 拉全量。
把「快、稳、合法」设为默认优化目标,结束本课技术讨论。
| 错误路径 | 结果 |
|---|---|
| 只保存整页 HTML 到文件,从不看 Network | 可能存了一堆空壳 |
| 上 Selenium 点开每个详情 | 极慢,维护成本高 |
找到 /api/list 一次拉全量 |
快,字段清晰 |
把「快、稳、合法」当作默认优化目标。
总结
动态页优先 JSON 接口;自动化补充认知;与 37 互补;合规底线。
下一课把多源 API 拉通成可验收的入库流水线。
- 动态页优先挖 JSON 接口,再用 Day34 肌肉请求。
- 自动化只作补充认知;本课以 API 实践过关。
- 与第 37 课互补:有 HTML 用解析,有接口用接口。
- 合规与限流是底线;密钥不进仓库。
小练笔
自测题含概念与可选换仓库实践。先做后对。
可选实践请真实请求并观察字段。
题 1
Network 面板应优先看哪类请求找数据接口?
题 2
判断:所有动态站都必须上 Selenium 才能拿数据。
题 3
自动化主要缺点各举一个。
题 4
接口优先三步是什么?
题 5
判断:本课实践会修改 cpython 仓库在 GitHub 上的 star 数。
题 6
为什么空壳页会导致 BeautifulSoup「选不中」?
题 7
data.get("x") 比 data["x"] 在字段缺失时更安全的点是?
题 8
判断:只要网站能在浏览器打开,就可以无限制地高频抓取。
题 9(可选实践)
把 URL 换成 https://api.github.com/repos/psf/requests,打印 full_name 与 stargazers_count。
题 10
列出两种「不该用自动化」的情况。
小练笔参考答案
先独立完成。意思对即可。
与接口优先、合规冲突的理解需修正。
题 1
Fetch/XHR(或返回 JSON 的请求)。
题 2
错
题 3
慢、脆、资源占用高(合理即可)。
题 4
Network → 筛 XHR/JSON → 用客户端复现。
题 5
错(只读公开 API)。
题 6
数据不在首屏 HTML 里,而在后续接口响应中。
题 7
缺失时返回 None 而不是 KeyError。(合理即可)
题 8
错
题 9
以你运行输出为准。
题 10
已有稳定官方 API;无授权/违反条款时。(合理即可)