2-爬虫翻页

分页

一、为什么要分页

想象一个新闻网站,数据库里有 10000 条 新闻。当你打开 https://example.com/news 这个页面的时候,网站不会把 10000 条一次性全倒给你,原因很现实:

  • 网页一下子加载 10000 条,浏览器会卡死;

  • 服务器一次性吐 10000 条,数据库压力太大;

  • 用户屏幕也就那么大,一次看几十条就够了,剩下的都是浪费流量。

于是网站定了个规矩:每次只给你 20 条,看完再翻下一页。 这个"每次 20 条"就是所谓的 一页(page)。

所以"页"根本不是网页本身的天然结构,而是后端把一大堆数据切成了均匀的若干份,一份叫一页:

复制代码
第 1 页:第 1~20 条新闻
第 2 页:第 21~40 条新闻
第 3 页:第 41~60 条新闻
...
第 500 页:第 9981~10000 条新闻

你在浏览器里点"下一页""2""3""...""500",或者往下滚动加载出更多内容------本质都是在说:"把第 2 页那 20 条给我""把第 3 页那 20 条给我"。

那爬虫为什么要操心翻页?

这就是关键了。你写爬虫时请求的,永远只是某一页。

假如你只发一次请求 https://example.com/news,服务器默认只把第 1 页那 20 条返回给你。程序解析完,你以为抓到了新闻,其实 10000 条里只拿到了 20 条,剩下的 9980 条你压根没碰过。

所以爬虫要拿到全部数据,就必须模拟人不停点"下一页"这个动作 :抓完第 1 页,再请求第 2 页,再请求第 3 页......直到没有更多数据为止。这就是"翻页"在爬虫里的含义------不是页面在翻,是你的程序在一页一页地把数据分批要回来。

二、爬虫里常见的 4 种翻页方式

它们本质上是同一个问题:网站怎么告诉你"还有下一页"?你怎么拿到下一页?

翻页方式 下一页在哪里 爬虫怎么做
URL 参数分页 ?page=1、?offset=20 自己拼 URL,循环请求
下一页链接分页 [下一页]() 解析页面,取 href,再请求
API 分页 JSON 接口返回数据 直接调 API,按参数或 cursor 翻页
无限滚动 / Load More 滚动后触发 AJAX 请求 先找真实接口,找不到再模拟滚动

1、URL 参数分页

URL 参数分页是爬虫最常见的一种翻页方式。它的核心思路很简单:每一页对应一个 URL,只是 URL 里的参数在变化。程序只要按规律修改这些参数,就能像人点"下一页"一样,一页一页把数据抓完。

两种常见形式

1. 页码式分页:page

复制代码
/news?page=1
/news?page=2
/news?page=3

这里的 page 表示"第几页"。程序只需要从 page=1 开始,每次把页码加 1,直到没有数据、没有下一页链接,或者达到你设定的最大页数。

程序一圈圈跑,就是在替你把"下一页、下一页、下一页......"这一下午要点几百次的鼠标,自动按了几百次。

2. 偏移量分页:offset + limit

复制代码
/news?offset=0&limit=20
/news?offset=20&limit=20
/news?offset=40&limit=20

这里有三个关键参数:

参数 含义 示例
limit 每页返回多少条 20 表示一页 20 条
offset 跳过前面多少条 0 表示从第 1 条开始
page 第几页 page=3 表示第 3 页

它们之间有一个很常用的换算关系:

复制代码
offset = (page - 1) * limit

比如每页 20 条:

  • 第 1 页:offset = 0

  • 第 2 页:offset = 20

  • 第 3 页:offset = 40

所以 page=3&limit=20 和 offset=40&limit=20 本质上指向同一片数据。

爬虫里怎么处理?

通常做法是写一个循环,不断修改 URL 参数:

javascript 复制代码
for page in range(1, 101):
    url = f"https://example.com/news?page={page}"
    # 请求、解析、存储

或者用 offset 方式:

javascript 复制代码
limit = 20
for offset in range(0, 2000, limit):
    url = f"https://example.com/news?offset={offset}&limit={limit}"
    # 请求、解析、存储
需要注意的两个问题

一是性能问题 。offset 很大时,后端数据库可能需要跳过大量记录,查询会变慢。 二是数据漂移问题。如果数据在翻页过程中被新增或删除,下一页可能会出现重复或遗漏。

所以写爬虫时,除了改参数翻页,还要做好去重、异常处理和最大页数限制。

2、下一页"链接分页

页面上没有 ?page=2 这种明显规律,但有一个按钮或链接:

javascript 复制代码
<a href="/news?page=2">下一页</a>

这时不要硬猜 page+1,而是:

复制代码
解析当前页
  ↓
找到“下一页”链接
  ↓
取出 href
  ↓
请求这个新 URL
  ↓
继续解析

这种方式更稳,因为有些网站的页码可能不连续,比如:

复制代码
/news/2026/10
/news/2026/09
/news/2026/08

它不是 page=1,2,3,而是按时间、分类、ID 生成链接。所以爬虫要跟着链接走,而不是假设页码一定连续。


3、API 分页

很多网页看起来是普通页面,但数据其实来自 JSON 接口。比如浏览器里看到新闻列表,但 HTML 源码里几乎没有数据。

常见接口形式是:

javascript 复制代码
/api/news?page=2&size=20
/api/news?offset=20&limit=20

还有一种叫 cursor 分页:

javascript 复制代码
{
  "items": [...],
  "nextCursor": "abc123"
}

下一页请求变成:

javascript 复制代码
/api/news?cursor=abc123

cursor 不是页码,不能自己猜。它通常是服务器返回的一个"位置标记",可能编码了最后一条数据的 ID、时间戳或排序位置。

爬虫处理方式是:

复制代码
请求接口
  ↓
解析 JSON
  ↓
保存 items
  ↓
取出 nextCursor
  ↓
用 nextCursor 请求下一页
  ↓
nextCursor 为空时停止

cursor 分页适合数据量大、数据会变化、需要稳定顺序的场景;缺点是没法直接跳到"第 100 页"。


4、无限滚动 / Load More

有些页面没有页码,也没有"下一页"按钮。用户往下滚动,数据就一批一批加载出来。

它的常见表现是:

复制代码
页面滚动
  ↓
浏览器发出 AJAX / Fetch 请求
  ↓
返回 JSON 数据
  ↓
前端把数据渲染到页面上

这时不要一上来就用 Selenium 或 Playwright 模拟滚动。更高效的做法是:

复制代码
打开 F12
  ↓
切到 Network 面板
  ↓
筛选 XHR / Fetch
  ↓
滚动页面或点“加载更多”
  ↓
找到返回目标数据的接口
  ↓
用 requests 直接请求这个接口

只有当接口有签名、加密参数、复杂校验,实在绕不过去时,才考虑用 Playwright / Selenium 模拟浏览器行为。

三、分页终止条件:防止爬虫"无限跑

分页最怕的就是 while True 不加刹车。只要没有明确的终止条件,爬虫就可能一直请求空页面、重复页面,甚至把自己跑死。

常见停止条件可以分成几类:

类型 判断方式 适用场景
链接消失 没有 next 链接 / 下一页按钮消失 页码分页、下一页链接分页
数据为空 返回 items 为空 API 分页
数据不足 返回数量 < pageSize 偏移量分页
游标结束 nextCursor == null cursor 分页
安全上限 达到 max_pages 或 max_articles 所有场景兜底
重复检测 连续若干页没有新增数据 动态列表、无限滚动

其中 安全上限 尤其重要。比如设置 max_pages = 20 或 max_articles = 500,即使其他判断失效,也能保证爬虫不会无限循环。

四 、两个工程坑

1. 第一页和后续页可能不一样

很多网站第一页是:

复制代码
/news

从第二页开始才变成

复制代码
/news?page=2
/news?page=3

所以不要想当然认为第一页一定是 page=1。更稳的做法是:

  • 先正常请求第一页;

  • 从第二页开始再按分页规则构造 URL;

  • 或者优先跟随页面里的 next 链接,而不是硬拼页码。

2. 分页过程中要去重

新闻网站更新时,新文章可能插入第一页,原来的第二页数据就会整体后移。这样同一篇文章可能在两页里都出现。

所以常用 canonical URL 或详情页 URL 做去重依据:

复制代码
seen_urls = set()
​
for article in articles:
    if article.url in seen_urls:
        continue
    seen_urls.add(article.url)
    save(article)

如果页面本身有 ``,也可以把它作为规范 URL;但在爬虫去重里,更常见的做法是用详情页的唯一 URL 判断是否已经采集过。

五、分页分析检查顺序

遇到一个列表页,可以按这个顺序判断:

复制代码
页面有没有明确页码?
  ↓
URL 是否随翻页变化?
  ↓
有没有 Next 链接?
  ↓
F12 Network 里有没有分页 API?
  ↓
是 page / offset / cursor 哪一种?
  ↓
什么时候停止?
  ↓
如何防重复?

这套顺序本质上是在问三件事:

  1. 下一页在哪里?

  2. 怎么判断已经翻完了?

  3. 怎么保证不重复抓?

相关推荐
惊讶的猫8 小时前
python爬虫入门
爬虫
傻啦嘿哟11 小时前
天气数据爬虫:基于Scrapy爬取全国天气,做可视化大屏
c++·爬虫·scrapy
雪雪爱冲浪1 天前
2026 年网络爬虫领域的Puppeteer vs. Playwright
爬虫·网络协议·测试工具·接口隔离原则
深蓝电商API2 天前
mitmproxy高级用法:自定义脚本拦截改包
爬虫·mitmproxy
Fluxproxy2 天前
Python爬虫异步提速终极方案|线程池ThreadPoolExecutor实战,告别单线程慢速爬取
开发语言·爬虫·python
独立开发者阿乐2 天前
llms.txt 是什么?为什么 AI 爬虫需要这个文件
人工智能·爬虫·geo优化·ai爬虫·llms.txt·ai收录·robots.txt
傻啦嘿哟2 天前
高校信息爬虫:爬取全国高校列表与排名,做择校分析
爬虫
深蓝电商API3 天前
HTTP/2 协议逆向:从 Fiddler 抓不到包说起
爬虫·网络协议·http·fiddler
隐擎fox3 天前
深入网络风控底层:解密 IP 信誉评分模型、ASN 属性判定与多租户原生网络隔离架构
网络·爬虫·网络协议·tcp/ip·架构·跨境电商·指纹浏览器