前言在网络数据采集场景中分页展示是绝大多数网站通用的数据呈现形式无论是资讯平台、商品商城、内容社区还是信息查询站点都会通过分页机制对海量数据进行拆分展示以此优化页面加载速度、提升用户浏览体验。对于爬虫开发而言分页数据抓取是入门到进阶必须掌握的核心技能能否精准识别分页规则、设计稳定的分页遍历逻辑直接决定爬虫的数据采集完整性与运行效率。不同网站的分页实现方式存在明显差异主流分为静态分页链接、动态参数分页、AJAX 异步加载分页三大类部分站点还会结合页码加密、参数动态变化、分页上限限制等反爬手段增加采集难度。本文从实战角度出发系统梳理各类分页场景的识别方法、抓取思路、代码实现与底层原理同时结合实际案例拆解分页遍历逻辑、边界判断、重复数据过滤等实操要点帮助开发者建立标准化的分页爬虫开发流程。本文开发与运行依赖多款 Python 第三方库相关官方下载、文档地址统一整理如下requests网络请求核心库https://pypi.org/project/requests/bs4BeautifulSoup网页解析库https://pypi.org/project/beautifulsoup4/lxml高效网页解析引擎https://pypi.org/project/lxml/json内置库解析异步分页 JSON 数据Python 标准库无需额外安装re内置正则库提取分页参数Python 标准库无需额外安装一、分页基础认知与分类1.1 分页结构通用组成标准网页分页模块通常包含页码、上一页、下一页、首页、尾页、跳转输入框等元素从爬虫视角拆解核心有效信息分为两类一是分页标识参数用于拼接请求地址二是分页边界条件用于判断爬虫停止时机。分页参数是控制页面数据切换的核心也是爬虫构造循环请求的关键。不同站点的分页参数命名、传参位置、取值规则各不相同常见传参位置分为 URL 路径传参、URL 查询参数传参、请求体传参三种。分页边界则主要包含最大页码、数据总条数、无数据标识三类是避免爬虫无效循环、重复请求的判断依据。1.2 主流分页类型划分结合前端实现与数据加载方式行业内将网页分页划分为三大基础类型也是爬虫开发中最常遇到的场景各类分页的特征、识别方式与抓取难度对比如下表所示表格分页类型前端实现方式数据加载形式识别特征抓取难度适用解析方案静态链接分页后端模板渲染页面整体刷新整页 HTML 同步加载页码对应独立 URL切换页码地址发生变化低BeautifulSoup 正则解析 HTMLURL 参数分页后端接收 URL 参数渲染页面整页 HTML 同步加载基础 URL 固定仅 query 参数page/pn变化低拼接 URL 参数循环请求AJAX 异步分页前端 JS 发起接口请求局部刷新JSON/XML 数据异步加载页面不刷新抓包可见独立数据接口中requests 请求接口 json 解析加密参数分页分页参数经 JS 加密 / 编码处理同步 / 异步加载均有页码参数无规律、存在时间戳 / 随机串高逆向 JS 解密参数后请求1.3 分页抓取核心流程无论何种分页类型标准化的抓取流程具备高度通用性完整流程可分为六个步骤该流程贯穿所有分页爬虫项目也是排查分页抓取异常的核心思路目标站点分析手动浏览页面观察分页切换规律、URL 变化、数据加载形式抓包与元素审查使用浏览器开发者工具定位分页参数、数据接口、边界标识规则梳理确定分页起始值、步长、最大页码、参数拼接规则循环逻辑设计基于 while/for 循环实现页码自增批量构造请求地址数据解析根据数据格式HTML/JSON提取目标字段边界终止判断检测是否到达最后一页、数据为空、重复数据终止循环。二、静态链接分页抓取实战2.1 场景特征与规则分析静态链接分页是最早出现、结构最简单的分页形式多见于传统资讯网站、博客站点、论坛等。其核心特征为每一个页码对应一个独立的静态 URL 地址点击页码后整个页面完成刷新浏览器地址栏链接同步改变。此类分页的 URL 无统一参数规则常见形式包括目录式 URL、后缀式 URL示例如下目录形式https://xxx.com/news/1.html第 1 页、https://xxx.com/news/2.html第 2 页后缀形式https://xxx.com/list?page1、https://xxx.com/list?page2识别该类分页的核心方式手动依次点击页码记录 URL 变化规律同时在网页源码中查找a标签包裹的页码链接确认所有分页地址均可在静态 HTML 中直接提取无需依赖 JavaScript 加载。2.2 完整代码实现本案例模拟传统资讯类静态分页站点通过解析页面 HTML 提取所有分页链接再遍历链接完成全页数据抓取代码基于requests与BeautifulSoup实现。python运行# 导入所需库 import requests from bs4 import BeautifulSoup # 配置请求头模拟浏览器访问基础反爬规避 HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 } # 基础域名与首页地址 BASE_URL https://demo-static-page.com HOME_URL https://demo-static-page.com/list/1.html def get_all_page_links(url): 解析首页HTML提取所有分页链接 :param url: 首页地址 :return: 分页链接列表 page_links [] # 发起网络请求 response requests.get(url, headersHEADERS, timeout10) # 设置网页编码避免乱码 response.encoding utf-8 # 实例化解析器使用lxml引擎提升解析效率 soup BeautifulSoup(response.text, lxml) # 定位分页模块根据页面class筛选页码a标签 page_a_list soup.find(div, class_page-box).find_all(a) for a in page_a_list: # 提取a标签中的href属性 href a.get(href) # 拼接完整URL处理相对路径 if href.startswith(/): full_link BASE_URL href else: full_link href # 去重并添加至列表 if full_link not in page_links: page_links.append(full_link) return page_links def parse_page_data(page_url): 解析单页数据提取标题、发布时间、内容摘要 :param page_url: 单页链接 :return: 当前页数据列表 data_list [] response requests.get(page_url, headersHEADERS, timeout10) response.encoding utf-8 soup BeautifulSoup(response.text, lxml) # 遍历页面数据条目 item_list soup.find_all(div, class_news-item) for item in item_list: news_title item.find(h3, class_news-title).get_text(stripTrue) news_time item.find(span, class_publish-time).get_text(stripTrue) news_summary item.find(p, class_summary).get_text(stripTrue) data_dict { 标题: news_title, 发布时间: news_time, 内容摘要: news_summary } data_list.append(data_dict) return data_list if __name__ __main__: # 第一步获取全部分页链接 all_links get_all_page_links(HOME_URL) print(f共识别到 {len(all_links)} 个分页) # 第二步遍历所有分页抓取数据 total_data [] for index, link in enumerate(all_links, 1): print(f正在抓取第 {index} 页{link}) page_data parse_page_data(link) total_data.extend(page_data) # 输出最终采集结果统计 print(f数据抓取完成总计采集 {len(total_data)} 条数据) for data in total_data[:5]: print(data)2.3 代码原理深度解析请求头配置原理User-Agent是 HTTP 请求头的核心字段用于标识客户端类型。网站服务器会校验该字段若检测到请求来自 Python 原生客户端会直接拒绝访问。配置浏览器格式的User-Agent本质是模拟正常用户浏览器行为绕过基础的客户端识别反爬策略。timeout参数用于设置请求超时时间防止目标服务器无响应导致程序卡死。编码设置原理网页的字符编码由站点后端定义常见为utf-8、gbk、gb2312。requests库默认会根据响应头自动识别编码但部分静态站点响应头编码标识缺失直接解析会出现中文乱码。手动指定response.encoding utf-8是强制按照目标站点编码规则解析响应文本保证中文内容正常提取。BeautifulSoup 解析原理BeautifulSoup 是 Python 专用的 HTML/XML 文档解析库其工作逻辑分为两步首先将获取到的字符串格式网页源码转换为结构化文档树其次通过标签名、class、id 等定位规则遍历文档树提取指定节点内容。案例中使用lxml作为解析引擎相较于 Python 内置解析器lxml基于 C 语言开发解析速度更快、容错性更强能够兼容不规范的 HTML 代码。分页链接提取与去重原理静态分页的页码全部渲染在首页 HTML 源码中因此只需解析首页即可获取全部分页地址。部分站点的分页模块会重复展示 “首页”“尾页” 链接因此通过if full_link not in page_links做列表去重避免后续重复请求同一页面减少网络开销与数据冗余。数据解析与整合原理find_all()方法用于批量匹配所有符合规则的 HTML 节点遍历节点后通过get_text(stripTrue)提取标签内纯文本stripTrue会自动去除文本首尾的空格、换行符、制表符等无效空白字符。单页解析完成后使用列表extend()方法将单页数据追加至总数据列表实现多页数据统一汇总。2.4 场景优化与问题解决相对路径与绝对路径适配网页中a标签的href分为相对路径/list/2.html和绝对路径https://xxx.com/list/2.html代码中通过字符串判断href.startswith(/)区分两种格式手动拼接基础域名保证链接有效性该逻辑可适配 90% 以上的静态站点路径规则。分页链接缺失处理部分站点仅在当前页展示 “下一页”无法一次性提取所有分页链接此时需改用逐页遍历 下一页判断逻辑解析当前页面的 “下一页” 链接若链接存在则继续请求若不存在则判定为最后一页终止循环。该逻辑是静态分页的补充方案代码改造示例如下python运行def crawl_by_next_page(start_url): current_url start_url while current_url: print(f正在抓取{current_url}) # 解析当前页数据 page_data parse_page_data(current_url) # 解析下一页链接 res requests.get(current_url, headersHEADERS) soup BeautifulSoup(res.text, lxml) next_a soup.find(a, text下一页) if next_a: next_href next_a.get(href) current_url BASE_URL next_href if next_href.startswith(/) else next_href else: current_url None三、URL 参数分页抓取实战3.1 场景特征与规则分析URL 参数分页是目前 PC 端、移动端网站使用最广泛的分页形式其核心特征为网站基础 URL 固定仅 URL 后的查询参数随页码变化。切换页码时页面整体刷新地址栏中域名、路径保持不变仅?后的参数发生改变。该类分页的参数命名具备行业通用习惯主流分页参数名称及含义如下表表格参数名含义取值规则应用场景page通用页码参数从 1 开始自增步长为 1资讯、博客、综合列表页pn页码参数从 1/0 开始自增电商、搜索引擎类站点offset偏移量每页数据条数 × (页码 - 1)大数据列表、后台管理系统limit单页条数固定值定义每页展示数据量配合 offset 联合分页标准 URL 参数分页格式示例基础页码型https://demo-param.com/list?page1、https://demo-param.com/list?page2偏移量 条数型https://demo-param.com/data?offset0limit20第 1 页20 条、https://demo-param.com/data?offset20limit20第 2 页识别要点手动切换页码记录参数名、初始值、自增步长同时确认站点是否存在最大页码限制部分站点会设置分页上限超过指定页码后不再返回数据。3.2 完整代码实现本案例分为两种主流写法for循环固定页码范围已知最大页码、while循环动态遍历未知最大页码自动终止覆盖不同业务场景。3.2.1 已知最大页码for 循环实现适用于手动查询到站点最大页码页码范围固定的场景代码逻辑简单、运行效率高。python运行import requests from bs4 import BeautifulSoup HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36 } # 基础URL不含分页参数 BASE_URL https://demo-param.com/list # 分页配置起始页、结束页、步长 START_PAGE 1 END_PAGE 20 STEP 1 def parse_param_page(page_num): 根据页码拼接URL并解析数据 # 拼接带分页参数的完整URL url f{BASE_URL}?page{page_num} try: response requests.get(url, headersHEADERS, timeout10) response.encoding utf-8 soup BeautifulSoup(response.text, lxml) item_list soup.find_all(div, class_item) page_data [] for item in item_list: title item.find(a).get_text(stripTrue) author item.find(span, class_author).get_text(stripTrue) page_data.append({标题: title, 作者: author}) return page_data except Exception as e: print(f第{page_num}页请求异常{str(e)}) return [] if __name__ __main__: all_data [] # 遍历指定页码范围 for page in range(START_PAGE, END_PAGE 1, STEP): print(f正在抓取第 {page} 页) data parse_param_page(page) all_data.extend(data) print(f抓取完成共获取 {len(all_data)} 条数据)3.2.2 未知最大页码while 循环动态遍历适用于数据总量不固定、最大页码未知的场景通过页面数据条数判断自动识别最后一页是工程化爬虫的主流写法。python运行import requests from bs4 import BeautifulSoup HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36 } BASE_URL https://demo-param.com/list def crawl_dynamic_page(): all_data [] current_page 1 while True: url f{BASE_URL}?page{current_page} print(f正在抓取第 {current_page} 页) response requests.get(url, headersHEADERS, timeout10) response.encoding utf-8 soup BeautifulSoup(response.text, lxml) item_list soup.find_all(div, class_item) # 核心终止条件页面无数据则退出循环 if not item_list: print(已到达最后一页抓取结束) break # 解析当前页数据 for item in item_list: title item.find(a).get_text(stripTrue) all_data.append({标题: title}) # 页码自增 current_page 1 return all_data if __name__ __main__: result crawl_dynamic_page() print(f总计采集数据{len(result)} 条)3.3 代码原理深度解析URL 拼接原理代码中使用 Python 格式化字符串f-string拼接分页参数本质是按照站点的 URL 规则动态生成请求地址。URL 查询参数遵循键值的标准格式多个参数通过连接这是 HTTP 协议中 GET 请求传递参数的通用规范。相较于字符串拼接、urllib.parse拼接f-string语法简洁、执行效率更高适合简单参数场景。for 循环页码遍历原理range(START_PAGE, END_PAGE 1, STEP)生成连续的页码序列range函数为左闭右开区间因此结束值需要设置为END_PAGE 1才能遍历到最后一页。该方案的优势是逻辑可控、无无效请求缺点是必须提前获取最大页码灵活性较差仅适合静态数据列表。while 循环动态终止原理动态遍历的核心逻辑是数据存在性校验每请求一个页面后判断解析到的数据条目列表是否为空。当页码超过站点最大限制时后端会返回空列表或空 HTML此时if not item_list条件成立触发break语句终止while循环。该逻辑完全自动化无需人工干预页码上限适配数据动态更新的站点。异常捕获基础原理第一段代码中增加了try...except异常捕获结构用于捕获网络超时、连接失败、页面结构变更等运行时异常。网络请求属于 IO 操作受网络环境、服务器状态影响极大主动捕获异常可以保证程序不会因单页请求失败而整体崩溃同时打印异常信息便于后期问题排查。3.4 偏移量分页适配方案针对offset limit组合式分页参数规则与普通页码分页不同偏移量计算公式为offset (当前页码 - 1) × 单页条数基于该公式改造代码实现偏移量分页抓取核心代码片段如下python运行# 单页固定展示条数 LIMIT 20 current_page 1 while True: offset (current_page - 1) * LIMIT url fhttps://demo-param.com/data?offset{offset}limit{LIMIT} # 后续请求、解析、终止逻辑保持不变该类分页常见于大数据量后台接口页码概念被弱化服务器通过偏移量定位数据起始位置在爬虫开发中需要优先梳理偏移量与页码的换算关系。四、AJAX 异步分页抓取实战4.1 场景特征与规则分析随着前端技术发展AJAX 异步分页成为主流交互方式多见于短视频平台、商品列表、信息流网站。其核心特征为切换页码 / 下滑加载更多时页面整体不刷新仅局部区域加载新数据浏览器地址栏 URL 无变化。该类分页的数据并非嵌入在初始 HTML 源码中而是前端 JavaScript 触发异步请求调用后端专属数据接口接口返回 JSON 格式结构化数据前端 JS 再将数据渲染到页面中。识别异步分页的核心步骤浏览器抓包打开浏览器开发者工具切换至「Network网络」面板勾选「XHR/Fetch」过滤异步请求手动点击下一页 / 下滑加载更多观察面板中新增的请求记录选中目标请求查看「General」中的请求地址、请求方式GET/POST、请求参数查看「Response」面板确认返回数据为 JSON 格式并梳理字段对应关系。异步分页接口的传参同样分为页码、偏移量两大类返回数据统一为 JSON 格式无需解析复杂 HTML数据提取效率远高于传统分页。4.2 完整代码实现本案例基于抓包获取的 AJAX 接口实现异步分页数据抓取使用requests请求接口结合 Python 内置json库解析数据。python运行import requests import json HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36, Referer: https://demo-ajax.com/list # 部分接口校验Referer请求头 } # AJAX数据接口地址 API_URL https://demo-ajax.com/api/get_data def crawl_ajax_page(): all_data [] page 1 while True: # 构造接口请求参数 params { page: page, size: 15 # 单页数据条数 } # 发起GET请求携带分页参数 response requests.get(API_URL, headersHEADERS, paramsparams, timeout10) # 将响应文本转为JSON字典 res_json json.loads(response.text) # 根据接口返回格式判断数据状态示例code200表示请求成功 if res_json.get(code) ! 200: print(接口请求失败或已无更多数据抓取终止) break # 提取核心数据列表 data_list res_json.get(data, []) if not data_list: print(当前页无数据抓取结束) break # 遍历解析单条数据 for item in data_list: data_dict { id: item.get(id), 名称: item.get(name), 简介: item.get(desc) } all_data.append(data_dict) print(f成功抓取第 {page} 页当前累计数据{len(all_data)} 条) page 1 return all_data if __name__ __main__: result crawl_ajax_page() print(f异步分页抓取完成总数据量{len(result)} 条)4.3 代码原理深度解析XHR 异步请求原理AJAXAsynchronous JavaScript And XML即异步 JavaScript 和 XML现代站点已普遍使用 JSON 替代 XML 作为数据载体。前端页面加载完成后初始 HTML 仅包含页面框架无实际业务数据当触发分页动作时JS 引擎创建 XHR/Fetch 对象向后端接口发起独立网络请求接口返回 JSON 数据后JS 动态渲染 DOM 节点展示数据。爬虫抓取异步分页本质是直接模拟前端 JS 的接口请求行为绕过前端页面渲染环节直接获取结构化数据这也是异步分页爬虫解析效率更高的核心原因。params 参数传参原理requests.get()方法中的params参数会自动将字典格式的参数拼接为 URL 查询字符串无需开发者手动拼接同时自动完成特殊字符编码规避 URL 编码错误问题。该方法是 GET 请求传递参数的标准写法兼容性与安全性优于手动拼接 URL。Referer 请求头原理Referer是 HTTP 请求头用于标识当前请求的来源页面。部分后端接口开启了来源校验拒绝无合法 Referer 的请求以此防范恶意爬虫。在请求头中补充真实的来源地址是适配接口校验的基础手段。JSON 解析原理json.loads()是 Python 内置 JSON 解析函数作用是将字符串格式的 JSON 数据转换为 Python 字典 / 列表实现字段的键值取值。接口返回的响应体本质是长字符串无法直接通过下标取值必须转换为 Python 内置数据类型后才能使用dict.get()方法提取指定字段。使用get()方法取值而非直接下标取值是为了避免字段缺失引发KeyError异常提升代码健壮性。双重终止判断原理代码中设置了两层终止条件第一层判断接口状态码code识别接口异常、权限不足、接口下线等场景第二层判断数据列表是否为空识别到达最后一页的场景。双重判断机制可以覆盖异步接口的各类异常状态避免程序无效循环。4.4 POST 类型异步接口适配部分异步分页接口采用 POST 请求方式参数不再放在 URL 中而是放在请求体内。requests库通过data或json参数传递请求体数据适配代码改造如下python运行# POST请求传递JSON格式参数 json_data { page: page, size: 15 } response requests.post(API_URL, headersHEADERS, jsonjson_data, timeout10)当接口要求表单格式参数时使用data字典传参要求 JSON 格式参数时使用json字典传参二者不可混用。五、分页抓取高阶问题与解决方案5.1 重复数据问题与过滤方案分页爬虫运行过程中重复数据是高频问题产生原因主要分为三类站点分页参数异常、网络延迟导致重复请求、加载更多接口数据重叠。针对该问题行业内主流两种过滤方案唯一标识去重推荐每条数据都具备唯一标识如 ID、手机号、链接地址等。在采集过程中用列表或集合存储已采集的唯一标识新数据先比对标识重复则跳过。核心代码示例python运行id_set set() # 集合查询效率高于列表 for item in data_list: data_id item.get(id) if data_id in id_set: continue id_set.add(data_id) # 保存有效数据集合set基于哈希表实现成员判断时间复杂度为 O (1)海量数据场景下效率优势明显。内存去重全字段比对无唯一标识时对整条数据字典进行比对适合数据量较小的场景可借助列表判断重复。5.2 分页参数加密处理思路部分高反爬站点会对分页参数、时间戳进行 JS 加密参数呈现无规律字符串无法直接拼接。标准解决流程如下抓包获取加密后的参数、请求接口在浏览器开发者工具中搜索加密参数定位前端加密 JS 函数逆向分析 JS 代码梳理加密算法逻辑使用 Python 复刻加密算法动态生成合法分页参数携带加密参数发起请求完成分页遍历。参数加密属于爬虫进阶逆向范畴基础分页爬虫开发中优先规避此类站点掌握基础分页规则后再逐步学习逆向技术。5.3 分页上限与频率限制应对多数站点设置了分页上限例如仅开放前 50 页数据超过上限后接口返回空数据或错误提示。结合前文动态循环逻辑依靠数据为空的终止条件即可自动适配。同时站点会限制请求频率短时间内高频请求同一分页地址会触发封禁。基础解决方案为添加延时在每次请求后使用time.sleep()设置等待时间降低请求频率该部分内容会在后续《基础延时与请求间隔设置》章节详细讲解。
Python 爬虫项目:分页数据抓取技巧
前言在网络数据采集场景中分页展示是绝大多数网站通用的数据呈现形式无论是资讯平台、商品商城、内容社区还是信息查询站点都会通过分页机制对海量数据进行拆分展示以此优化页面加载速度、提升用户浏览体验。对于爬虫开发而言分页数据抓取是入门到进阶必须掌握的核心技能能否精准识别分页规则、设计稳定的分页遍历逻辑直接决定爬虫的数据采集完整性与运行效率。不同网站的分页实现方式存在明显差异主流分为静态分页链接、动态参数分页、AJAX 异步加载分页三大类部分站点还会结合页码加密、参数动态变化、分页上限限制等反爬手段增加采集难度。本文从实战角度出发系统梳理各类分页场景的识别方法、抓取思路、代码实现与底层原理同时结合实际案例拆解分页遍历逻辑、边界判断、重复数据过滤等实操要点帮助开发者建立标准化的分页爬虫开发流程。本文开发与运行依赖多款 Python 第三方库相关官方下载、文档地址统一整理如下requests网络请求核心库https://pypi.org/project/requests/bs4BeautifulSoup网页解析库https://pypi.org/project/beautifulsoup4/lxml高效网页解析引擎https://pypi.org/project/lxml/json内置库解析异步分页 JSON 数据Python 标准库无需额外安装re内置正则库提取分页参数Python 标准库无需额外安装一、分页基础认知与分类1.1 分页结构通用组成标准网页分页模块通常包含页码、上一页、下一页、首页、尾页、跳转输入框等元素从爬虫视角拆解核心有效信息分为两类一是分页标识参数用于拼接请求地址二是分页边界条件用于判断爬虫停止时机。分页参数是控制页面数据切换的核心也是爬虫构造循环请求的关键。不同站点的分页参数命名、传参位置、取值规则各不相同常见传参位置分为 URL 路径传参、URL 查询参数传参、请求体传参三种。分页边界则主要包含最大页码、数据总条数、无数据标识三类是避免爬虫无效循环、重复请求的判断依据。1.2 主流分页类型划分结合前端实现与数据加载方式行业内将网页分页划分为三大基础类型也是爬虫开发中最常遇到的场景各类分页的特征、识别方式与抓取难度对比如下表所示表格分页类型前端实现方式数据加载形式识别特征抓取难度适用解析方案静态链接分页后端模板渲染页面整体刷新整页 HTML 同步加载页码对应独立 URL切换页码地址发生变化低BeautifulSoup 正则解析 HTMLURL 参数分页后端接收 URL 参数渲染页面整页 HTML 同步加载基础 URL 固定仅 query 参数page/pn变化低拼接 URL 参数循环请求AJAX 异步分页前端 JS 发起接口请求局部刷新JSON/XML 数据异步加载页面不刷新抓包可见独立数据接口中requests 请求接口 json 解析加密参数分页分页参数经 JS 加密 / 编码处理同步 / 异步加载均有页码参数无规律、存在时间戳 / 随机串高逆向 JS 解密参数后请求1.3 分页抓取核心流程无论何种分页类型标准化的抓取流程具备高度通用性完整流程可分为六个步骤该流程贯穿所有分页爬虫项目也是排查分页抓取异常的核心思路目标站点分析手动浏览页面观察分页切换规律、URL 变化、数据加载形式抓包与元素审查使用浏览器开发者工具定位分页参数、数据接口、边界标识规则梳理确定分页起始值、步长、最大页码、参数拼接规则循环逻辑设计基于 while/for 循环实现页码自增批量构造请求地址数据解析根据数据格式HTML/JSON提取目标字段边界终止判断检测是否到达最后一页、数据为空、重复数据终止循环。二、静态链接分页抓取实战2.1 场景特征与规则分析静态链接分页是最早出现、结构最简单的分页形式多见于传统资讯网站、博客站点、论坛等。其核心特征为每一个页码对应一个独立的静态 URL 地址点击页码后整个页面完成刷新浏览器地址栏链接同步改变。此类分页的 URL 无统一参数规则常见形式包括目录式 URL、后缀式 URL示例如下目录形式https://xxx.com/news/1.html第 1 页、https://xxx.com/news/2.html第 2 页后缀形式https://xxx.com/list?page1、https://xxx.com/list?page2识别该类分页的核心方式手动依次点击页码记录 URL 变化规律同时在网页源码中查找a标签包裹的页码链接确认所有分页地址均可在静态 HTML 中直接提取无需依赖 JavaScript 加载。2.2 完整代码实现本案例模拟传统资讯类静态分页站点通过解析页面 HTML 提取所有分页链接再遍历链接完成全页数据抓取代码基于requests与BeautifulSoup实现。python运行# 导入所需库 import requests from bs4 import BeautifulSoup # 配置请求头模拟浏览器访问基础反爬规避 HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 } # 基础域名与首页地址 BASE_URL https://demo-static-page.com HOME_URL https://demo-static-page.com/list/1.html def get_all_page_links(url): 解析首页HTML提取所有分页链接 :param url: 首页地址 :return: 分页链接列表 page_links [] # 发起网络请求 response requests.get(url, headersHEADERS, timeout10) # 设置网页编码避免乱码 response.encoding utf-8 # 实例化解析器使用lxml引擎提升解析效率 soup BeautifulSoup(response.text, lxml) # 定位分页模块根据页面class筛选页码a标签 page_a_list soup.find(div, class_page-box).find_all(a) for a in page_a_list: # 提取a标签中的href属性 href a.get(href) # 拼接完整URL处理相对路径 if href.startswith(/): full_link BASE_URL href else: full_link href # 去重并添加至列表 if full_link not in page_links: page_links.append(full_link) return page_links def parse_page_data(page_url): 解析单页数据提取标题、发布时间、内容摘要 :param page_url: 单页链接 :return: 当前页数据列表 data_list [] response requests.get(page_url, headersHEADERS, timeout10) response.encoding utf-8 soup BeautifulSoup(response.text, lxml) # 遍历页面数据条目 item_list soup.find_all(div, class_news-item) for item in item_list: news_title item.find(h3, class_news-title).get_text(stripTrue) news_time item.find(span, class_publish-time).get_text(stripTrue) news_summary item.find(p, class_summary).get_text(stripTrue) data_dict { 标题: news_title, 发布时间: news_time, 内容摘要: news_summary } data_list.append(data_dict) return data_list if __name__ __main__: # 第一步获取全部分页链接 all_links get_all_page_links(HOME_URL) print(f共识别到 {len(all_links)} 个分页) # 第二步遍历所有分页抓取数据 total_data [] for index, link in enumerate(all_links, 1): print(f正在抓取第 {index} 页{link}) page_data parse_page_data(link) total_data.extend(page_data) # 输出最终采集结果统计 print(f数据抓取完成总计采集 {len(total_data)} 条数据) for data in total_data[:5]: print(data)2.3 代码原理深度解析请求头配置原理User-Agent是 HTTP 请求头的核心字段用于标识客户端类型。网站服务器会校验该字段若检测到请求来自 Python 原生客户端会直接拒绝访问。配置浏览器格式的User-Agent本质是模拟正常用户浏览器行为绕过基础的客户端识别反爬策略。timeout参数用于设置请求超时时间防止目标服务器无响应导致程序卡死。编码设置原理网页的字符编码由站点后端定义常见为utf-8、gbk、gb2312。requests库默认会根据响应头自动识别编码但部分静态站点响应头编码标识缺失直接解析会出现中文乱码。手动指定response.encoding utf-8是强制按照目标站点编码规则解析响应文本保证中文内容正常提取。BeautifulSoup 解析原理BeautifulSoup 是 Python 专用的 HTML/XML 文档解析库其工作逻辑分为两步首先将获取到的字符串格式网页源码转换为结构化文档树其次通过标签名、class、id 等定位规则遍历文档树提取指定节点内容。案例中使用lxml作为解析引擎相较于 Python 内置解析器lxml基于 C 语言开发解析速度更快、容错性更强能够兼容不规范的 HTML 代码。分页链接提取与去重原理静态分页的页码全部渲染在首页 HTML 源码中因此只需解析首页即可获取全部分页地址。部分站点的分页模块会重复展示 “首页”“尾页” 链接因此通过if full_link not in page_links做列表去重避免后续重复请求同一页面减少网络开销与数据冗余。数据解析与整合原理find_all()方法用于批量匹配所有符合规则的 HTML 节点遍历节点后通过get_text(stripTrue)提取标签内纯文本stripTrue会自动去除文本首尾的空格、换行符、制表符等无效空白字符。单页解析完成后使用列表extend()方法将单页数据追加至总数据列表实现多页数据统一汇总。2.4 场景优化与问题解决相对路径与绝对路径适配网页中a标签的href分为相对路径/list/2.html和绝对路径https://xxx.com/list/2.html代码中通过字符串判断href.startswith(/)区分两种格式手动拼接基础域名保证链接有效性该逻辑可适配 90% 以上的静态站点路径规则。分页链接缺失处理部分站点仅在当前页展示 “下一页”无法一次性提取所有分页链接此时需改用逐页遍历 下一页判断逻辑解析当前页面的 “下一页” 链接若链接存在则继续请求若不存在则判定为最后一页终止循环。该逻辑是静态分页的补充方案代码改造示例如下python运行def crawl_by_next_page(start_url): current_url start_url while current_url: print(f正在抓取{current_url}) # 解析当前页数据 page_data parse_page_data(current_url) # 解析下一页链接 res requests.get(current_url, headersHEADERS) soup BeautifulSoup(res.text, lxml) next_a soup.find(a, text下一页) if next_a: next_href next_a.get(href) current_url BASE_URL next_href if next_href.startswith(/) else next_href else: current_url None三、URL 参数分页抓取实战3.1 场景特征与规则分析URL 参数分页是目前 PC 端、移动端网站使用最广泛的分页形式其核心特征为网站基础 URL 固定仅 URL 后的查询参数随页码变化。切换页码时页面整体刷新地址栏中域名、路径保持不变仅?后的参数发生改变。该类分页的参数命名具备行业通用习惯主流分页参数名称及含义如下表表格参数名含义取值规则应用场景page通用页码参数从 1 开始自增步长为 1资讯、博客、综合列表页pn页码参数从 1/0 开始自增电商、搜索引擎类站点offset偏移量每页数据条数 × (页码 - 1)大数据列表、后台管理系统limit单页条数固定值定义每页展示数据量配合 offset 联合分页标准 URL 参数分页格式示例基础页码型https://demo-param.com/list?page1、https://demo-param.com/list?page2偏移量 条数型https://demo-param.com/data?offset0limit20第 1 页20 条、https://demo-param.com/data?offset20limit20第 2 页识别要点手动切换页码记录参数名、初始值、自增步长同时确认站点是否存在最大页码限制部分站点会设置分页上限超过指定页码后不再返回数据。3.2 完整代码实现本案例分为两种主流写法for循环固定页码范围已知最大页码、while循环动态遍历未知最大页码自动终止覆盖不同业务场景。3.2.1 已知最大页码for 循环实现适用于手动查询到站点最大页码页码范围固定的场景代码逻辑简单、运行效率高。python运行import requests from bs4 import BeautifulSoup HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36 } # 基础URL不含分页参数 BASE_URL https://demo-param.com/list # 分页配置起始页、结束页、步长 START_PAGE 1 END_PAGE 20 STEP 1 def parse_param_page(page_num): 根据页码拼接URL并解析数据 # 拼接带分页参数的完整URL url f{BASE_URL}?page{page_num} try: response requests.get(url, headersHEADERS, timeout10) response.encoding utf-8 soup BeautifulSoup(response.text, lxml) item_list soup.find_all(div, class_item) page_data [] for item in item_list: title item.find(a).get_text(stripTrue) author item.find(span, class_author).get_text(stripTrue) page_data.append({标题: title, 作者: author}) return page_data except Exception as e: print(f第{page_num}页请求异常{str(e)}) return [] if __name__ __main__: all_data [] # 遍历指定页码范围 for page in range(START_PAGE, END_PAGE 1, STEP): print(f正在抓取第 {page} 页) data parse_param_page(page) all_data.extend(data) print(f抓取完成共获取 {len(all_data)} 条数据)3.2.2 未知最大页码while 循环动态遍历适用于数据总量不固定、最大页码未知的场景通过页面数据条数判断自动识别最后一页是工程化爬虫的主流写法。python运行import requests from bs4 import BeautifulSoup HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36 } BASE_URL https://demo-param.com/list def crawl_dynamic_page(): all_data [] current_page 1 while True: url f{BASE_URL}?page{current_page} print(f正在抓取第 {current_page} 页) response requests.get(url, headersHEADERS, timeout10) response.encoding utf-8 soup BeautifulSoup(response.text, lxml) item_list soup.find_all(div, class_item) # 核心终止条件页面无数据则退出循环 if not item_list: print(已到达最后一页抓取结束) break # 解析当前页数据 for item in item_list: title item.find(a).get_text(stripTrue) all_data.append({标题: title}) # 页码自增 current_page 1 return all_data if __name__ __main__: result crawl_dynamic_page() print(f总计采集数据{len(result)} 条)3.3 代码原理深度解析URL 拼接原理代码中使用 Python 格式化字符串f-string拼接分页参数本质是按照站点的 URL 规则动态生成请求地址。URL 查询参数遵循键值的标准格式多个参数通过连接这是 HTTP 协议中 GET 请求传递参数的通用规范。相较于字符串拼接、urllib.parse拼接f-string语法简洁、执行效率更高适合简单参数场景。for 循环页码遍历原理range(START_PAGE, END_PAGE 1, STEP)生成连续的页码序列range函数为左闭右开区间因此结束值需要设置为END_PAGE 1才能遍历到最后一页。该方案的优势是逻辑可控、无无效请求缺点是必须提前获取最大页码灵活性较差仅适合静态数据列表。while 循环动态终止原理动态遍历的核心逻辑是数据存在性校验每请求一个页面后判断解析到的数据条目列表是否为空。当页码超过站点最大限制时后端会返回空列表或空 HTML此时if not item_list条件成立触发break语句终止while循环。该逻辑完全自动化无需人工干预页码上限适配数据动态更新的站点。异常捕获基础原理第一段代码中增加了try...except异常捕获结构用于捕获网络超时、连接失败、页面结构变更等运行时异常。网络请求属于 IO 操作受网络环境、服务器状态影响极大主动捕获异常可以保证程序不会因单页请求失败而整体崩溃同时打印异常信息便于后期问题排查。3.4 偏移量分页适配方案针对offset limit组合式分页参数规则与普通页码分页不同偏移量计算公式为offset (当前页码 - 1) × 单页条数基于该公式改造代码实现偏移量分页抓取核心代码片段如下python运行# 单页固定展示条数 LIMIT 20 current_page 1 while True: offset (current_page - 1) * LIMIT url fhttps://demo-param.com/data?offset{offset}limit{LIMIT} # 后续请求、解析、终止逻辑保持不变该类分页常见于大数据量后台接口页码概念被弱化服务器通过偏移量定位数据起始位置在爬虫开发中需要优先梳理偏移量与页码的换算关系。四、AJAX 异步分页抓取实战4.1 场景特征与规则分析随着前端技术发展AJAX 异步分页成为主流交互方式多见于短视频平台、商品列表、信息流网站。其核心特征为切换页码 / 下滑加载更多时页面整体不刷新仅局部区域加载新数据浏览器地址栏 URL 无变化。该类分页的数据并非嵌入在初始 HTML 源码中而是前端 JavaScript 触发异步请求调用后端专属数据接口接口返回 JSON 格式结构化数据前端 JS 再将数据渲染到页面中。识别异步分页的核心步骤浏览器抓包打开浏览器开发者工具切换至「Network网络」面板勾选「XHR/Fetch」过滤异步请求手动点击下一页 / 下滑加载更多观察面板中新增的请求记录选中目标请求查看「General」中的请求地址、请求方式GET/POST、请求参数查看「Response」面板确认返回数据为 JSON 格式并梳理字段对应关系。异步分页接口的传参同样分为页码、偏移量两大类返回数据统一为 JSON 格式无需解析复杂 HTML数据提取效率远高于传统分页。4.2 完整代码实现本案例基于抓包获取的 AJAX 接口实现异步分页数据抓取使用requests请求接口结合 Python 内置json库解析数据。python运行import requests import json HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36, Referer: https://demo-ajax.com/list # 部分接口校验Referer请求头 } # AJAX数据接口地址 API_URL https://demo-ajax.com/api/get_data def crawl_ajax_page(): all_data [] page 1 while True: # 构造接口请求参数 params { page: page, size: 15 # 单页数据条数 } # 发起GET请求携带分页参数 response requests.get(API_URL, headersHEADERS, paramsparams, timeout10) # 将响应文本转为JSON字典 res_json json.loads(response.text) # 根据接口返回格式判断数据状态示例code200表示请求成功 if res_json.get(code) ! 200: print(接口请求失败或已无更多数据抓取终止) break # 提取核心数据列表 data_list res_json.get(data, []) if not data_list: print(当前页无数据抓取结束) break # 遍历解析单条数据 for item in data_list: data_dict { id: item.get(id), 名称: item.get(name), 简介: item.get(desc) } all_data.append(data_dict) print(f成功抓取第 {page} 页当前累计数据{len(all_data)} 条) page 1 return all_data if __name__ __main__: result crawl_ajax_page() print(f异步分页抓取完成总数据量{len(result)} 条)4.3 代码原理深度解析XHR 异步请求原理AJAXAsynchronous JavaScript And XML即异步 JavaScript 和 XML现代站点已普遍使用 JSON 替代 XML 作为数据载体。前端页面加载完成后初始 HTML 仅包含页面框架无实际业务数据当触发分页动作时JS 引擎创建 XHR/Fetch 对象向后端接口发起独立网络请求接口返回 JSON 数据后JS 动态渲染 DOM 节点展示数据。爬虫抓取异步分页本质是直接模拟前端 JS 的接口请求行为绕过前端页面渲染环节直接获取结构化数据这也是异步分页爬虫解析效率更高的核心原因。params 参数传参原理requests.get()方法中的params参数会自动将字典格式的参数拼接为 URL 查询字符串无需开发者手动拼接同时自动完成特殊字符编码规避 URL 编码错误问题。该方法是 GET 请求传递参数的标准写法兼容性与安全性优于手动拼接 URL。Referer 请求头原理Referer是 HTTP 请求头用于标识当前请求的来源页面。部分后端接口开启了来源校验拒绝无合法 Referer 的请求以此防范恶意爬虫。在请求头中补充真实的来源地址是适配接口校验的基础手段。JSON 解析原理json.loads()是 Python 内置 JSON 解析函数作用是将字符串格式的 JSON 数据转换为 Python 字典 / 列表实现字段的键值取值。接口返回的响应体本质是长字符串无法直接通过下标取值必须转换为 Python 内置数据类型后才能使用dict.get()方法提取指定字段。使用get()方法取值而非直接下标取值是为了避免字段缺失引发KeyError异常提升代码健壮性。双重终止判断原理代码中设置了两层终止条件第一层判断接口状态码code识别接口异常、权限不足、接口下线等场景第二层判断数据列表是否为空识别到达最后一页的场景。双重判断机制可以覆盖异步接口的各类异常状态避免程序无效循环。4.4 POST 类型异步接口适配部分异步分页接口采用 POST 请求方式参数不再放在 URL 中而是放在请求体内。requests库通过data或json参数传递请求体数据适配代码改造如下python运行# POST请求传递JSON格式参数 json_data { page: page, size: 15 } response requests.post(API_URL, headersHEADERS, jsonjson_data, timeout10)当接口要求表单格式参数时使用data字典传参要求 JSON 格式参数时使用json字典传参二者不可混用。五、分页抓取高阶问题与解决方案5.1 重复数据问题与过滤方案分页爬虫运行过程中重复数据是高频问题产生原因主要分为三类站点分页参数异常、网络延迟导致重复请求、加载更多接口数据重叠。针对该问题行业内主流两种过滤方案唯一标识去重推荐每条数据都具备唯一标识如 ID、手机号、链接地址等。在采集过程中用列表或集合存储已采集的唯一标识新数据先比对标识重复则跳过。核心代码示例python运行id_set set() # 集合查询效率高于列表 for item in data_list: data_id item.get(id) if data_id in id_set: continue id_set.add(data_id) # 保存有效数据集合set基于哈希表实现成员判断时间复杂度为 O (1)海量数据场景下效率优势明显。内存去重全字段比对无唯一标识时对整条数据字典进行比对适合数据量较小的场景可借助列表判断重复。5.2 分页参数加密处理思路部分高反爬站点会对分页参数、时间戳进行 JS 加密参数呈现无规律字符串无法直接拼接。标准解决流程如下抓包获取加密后的参数、请求接口在浏览器开发者工具中搜索加密参数定位前端加密 JS 函数逆向分析 JS 代码梳理加密算法逻辑使用 Python 复刻加密算法动态生成合法分页参数携带加密参数发起请求完成分页遍历。参数加密属于爬虫进阶逆向范畴基础分页爬虫开发中优先规避此类站点掌握基础分页规则后再逐步学习逆向技术。5.3 分页上限与频率限制应对多数站点设置了分页上限例如仅开放前 50 页数据超过上限后接口返回空数据或错误提示。结合前文动态循环逻辑依靠数据为空的终止条件即可自动适配。同时站点会限制请求频率短时间内高频请求同一分页地址会触发封禁。基础解决方案为添加延时在每次请求后使用time.sleep()设置等待时间降低请求频率该部分内容会在后续《基础延时与请求间隔设置》章节详细讲解。