Python 爬虫接口实战:翻页取完再累加(CH-002)
1089 字
5 分钟
Python 爬虫接口实战:翻页取完再累加(CH-002)
- 1Python 爬虫入门实战:服务端渲染页面怎么取数(CH-001)
- 2Python 爬虫接口实战:翻页取完再累加(CH-002)本文
- 3Python 爬虫实战:随机下发路径的资源怎么下载(CH-003)
- 4Python 爬虫 TLS 指纹实战:requests 被挂住时怎么查(CH-004)
- 5Python 爬虫接口签名实战:参数签名 + 会话绑定(CH-005)
- 6Python 爬虫加密实战:ROT + Base32 + 凯撒 + HMAC-MD5 组合(CH-006)
- 7Python 爬虫字体映射实战:HTML 里一个字都没有(CH-007)
- 8Python 爬虫字体加密实战:码位混淆 + 自定义字体文件(CH-008)
- 9Python 爬虫验证码实战:AES 加密报文 + 每页算术验证码(CH-009)
- 10第 10 关:RSA 传密钥 + AES 加密报文(顺带一层 JavaScript 混淆)
- 11第 11 关:无限 debugger + VM 混淆 + 签名请求头
- 12第 12 关:接口下发 JS + 字符串混淆 + 内置 MD5 + 反调试
- 13第 13 关:点选验证码 + 一次一密的轨迹报文 + 行为风控
- 14第 14 关:Cookie 签名 —— 七层复合加密,和「密钥写在前端」这件事
- 15第 15 关:蜜罐与爬虫封禁 —— 数据是假的,陷阱是真的
本文是 LearnSpider 第 2 关的解密教程。这一关的数据不在 HTML 里,只能从分页接口拿;答案是全部电影「评分总人数」的累加值。重点是:怎么找到接口、怎么确认它没把总量直接给你、以及怎么把 21 页稳稳翻完。
现象:源码里没有数据了
打开 /movies?challenge=CH-002,电影卡片正常显示。但「查看源代码」里搜不到任何一个电影名 —— 页面是 Vue 渲染的,数据只能来自 XHR。
F12 → Network → Fetch/XHR,刷新页面,会看到一个请求:
GET /api/movies?page=1&page_size=12响应长这样(字段是精简过的):
{ "items": [ { "title": "肖申克的救赎", "rating": 9.7, "rating_text": "9.7", "rating_count_text": "3235402人评价", "rating_count": 3235402 } ], "page": 1, "page_size": 12, "total_movies": 249}接口故意不给你的东西
/api/movies 不会下发整批统计字段:
| 字段 | 状态 |
|---|---|
total_rating_score(评分总和) | 被剥掉了 |
total_rating_count(评价人数总和) | 被剥掉了 ← 正是答案 |
top_movie(评分最高那部) | 被剥掉了 |
total_movies(共多少部) | ✅ 有,用来算要翻几页 |
所以「直接读一个字段就交卷」这条路被堵死了,必须一页一页取回来自己累加。
顺带说一句:page_size 上限是 100。想「一次要 1000 条」也会被服务端截回 100。
数据规模
- 共 249 部电影,每页 12 条 → 21 页(最后一页可能不满)。
- 判断翻页结束的两种写法:
page * page_size >= total_movies,或者某一页返回的items为空。
完整脚本
"""CH-002:答案 = 全部电影「评分总人数」之和"""import reimport time
import requests
BASE = "https://spider.jsnote.top"API_URL = f"{BASE}/api/movies"PAGE_SIZE = 12
session = requests.Session()session.headers.update({ "Accept": "application/json, text/plain, */*", "Accept-Language": "zh-CN,zh;q=0.9", "Referer": f"{BASE}/movies?challenge=CH-002", "User-Agent": ( "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36" ),})
# 数据是按账号派生的,所以要带登录态:# 浏览器登录后 F12 -> Application -> Cookies -> 复制 session 的值session.cookies.set("session", "<把浏览器里的 session cookie 粘到这里>", domain="spider.jsnote.top")
def rating_count(movie): """优先用字段,字段缺失/为 0 时回头从 '3235402人评价' 里抠数字。""" count = movie.get("rating_count") or 0 if count: return int(count) match = re.search(r"([\d,]+)\s*人评价", movie.get("rating_count_text", "")) return int(match.group(1).replace(",", "")) if match else 0
def main(): page = 1 total_movies = None total_count = 0 movies = 0
while True: response = session.get( API_URL, params={"page": page, "page_size": PAGE_SIZE}, timeout=10 ) response.raise_for_status() payload = response.json()
items = payload.get("items", []) if not items: break
total_movies = payload.get("total_movies", total_movies) for movie in items: total_count += rating_count(movie) movies += 1
print(f"第 {page:>2} 页:{len(items)} 条,累计 {movies} 条," f"评价人数小计 {total_count}")
if total_movies and movies >= total_movies: break page += 1 time.sleep(0.5) # 别把靶场当压力测试
print(f"\n共 {movies} 部电影,评价人数总和:{total_count}")
if __name__ == "__main__": main()把最后的数字提交到 CH-002 答题框即可。
排查清单
- 源码里搜不到数据 → 打开 Network 的
Fetch/XHR,找首屏那个请求。 - 右键 →
Copy as cURL,先在终端跑通一次,确认只差 Cookie。 - 看响应里有没有现成的总量字段;有就省事,没有就老老实实翻页。
- 翻页前先拿
total_movies和page_size算出总页数,别写成死循环。 - 每页间隔 0.5s 以上。
- 解析用字段优先、文本兜底(
rating_count拿不到就正则([\d,]+)人评价)。 - 累加前把一千分隔符去掉(
"1,234"→1234)。 - 结束条件写成「取到的条数 ≥ 总数」或「这一页为空」,不要写「拿到 21 页」。
常见问题
为什么我直接写 page_size=1000 也只能拿到 100 条?
服务端有上限(min(100, max(1, page_size))),超了就按 100 处理。这是接口很常见的保护写法。
数据为什么每个人不一样?
电影数据是共用的一份,但很多关卡的数据是按账号派生的(这道题的答案会不会变,取决于靶场实现)。所以别抄别人的答案,自己跑一遍。
需要登录吗?
/api/movies 本身公开可读;提交答案必须登录。
只抓到 20 页就停了?
说明结束条件写得不对:最后一页可能只有 9 条(不满 12 条),这时要停,但前面 20 页都是满的。用「累计条数 ≥ total_movies」判断最稳。
最后提醒
翻页是很典型的「礼貌爬取」场景:按页取、留间隔、别并发轰炸。真实站点上,疯狂翻页最容易触发限流甚至封号。
去挑战广场提交,然后进 CH-003:数据不再是文本,而是一个你猜不到路径的文件。
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!
Python 爬虫接口实战:翻页取完再累加(CH-002)
https://jsnote.top/posts/ls-ch2/相关文章智能推荐
1
Python 爬虫入门实战:服务端渲染页面怎么取数(CH-001)
爬虫靶场本文是 LearnSpider 第 1 关的解密教程。它练的不是「怎么破加密」,而是先判断数据到底在哪:这一关的数据只在服务端渲染出来的 HTML 里,源码里没有接口。答案是第一页 HTML 源码的字符数。
2
Python 爬虫接口签名实战:参数签名 + 会话绑定(CH-005)
爬虫靶场本文是 LearnSpider 第 5 关的解密教程。这一关的接口参数要签名,而且取数只认一个「会话标识」(uuid)—— 这个 uuid 是页面自己生成的、并且已经在后端和你的账号绑定过。答案是 5 页共 50 个数字的总和。
3
Python 爬虫验证码实战:AES 加密报文 + 每页算术验证码(CH-009)
爬虫靶场本文是 LearnSpider 第 9 关的解密教程。这一关把政务站点常见的一套反爬凑齐了:请求/响应都是 AES-ECB 加密的 hex,还要带一个前端写死的 Adam-AppKey,而且每翻一页都得先过一道算术验证码。目标是取 10 页…
4
Python 爬虫字体映射实战:HTML 里一个字都没有(CH-007)
爬虫靶场本文是 LearnSpider 第 7 关的解密教程。这一关把大众点评那套「字体库 + 坐标」搬了过来:页面 HTML 里只有随机类名,文字全在字体库(SVG 文字地图)里,而「每个类名要把图挪多少像素」是接口加密下发的。目标是拿到 5 段…
5
Python 爬虫实战:随机下发路径的资源怎么下载(CH-003)
爬虫靶场本文是 LearnSpider 第 3 关的解密教程。这一关要把图片下载下来,答案是这张图片的字节数。真正的考点是:拿到接口给的 image_id / image_url 之后,别把「字节」和「字符」搞混,也别拖太久(图片是随机下发的)。
随机文章随机推荐












