Python 爬虫项目:多页面循环爬取实现

发布时间:2026/6/12 22:06:07

Python 爬虫项目:多页面循环爬取实现 前言在爬虫工程实践中单页面采集仅能完成孤立数据获取而资讯站点、博客专栏、商品列表、论坛帖子等业务场景均需要对多个独立页面进行连续采集多页面循环爬取由此成为爬虫体系中承上启下的核心能力。多页爬取以单页面爬取逻辑为最小执行单元结合循环控制、链接队列、状态管理实现批量页面自动化采集有效规避人工逐页发起请求的低效问题是中小型全站爬虫、栏目数据采集的主流实现方案。未做规范化设计的循环爬虫易出现重复请求、页面漏爬、程序卡死、IP 封禁、数据错乱等问题同时缺乏日志记录与异常重试机制难以落地至正式项目。本文基于前文单页面爬取、链接提取与去重、数据清洗等已有技术栈系统讲解多页面循环爬取的架构设计、循环逻辑、队列管理、异常容错、延时控制、数据批量存储等内容区分固定页面集、连续页面、自定义页面组三大应用场景配套底层原理、完整代码、规则解析与性能优化方案。本文所使用的开发工具及第三方库官方资源链接汇总如下 Python 官方下载地址、requests 库官方文档、BeautifulSoup4 官方文档、re 正则表达式模块文档、urllib 标准库文档、pandas 数据处理库、time 标准库文档。全文遵循工程化开发思路从基础循环模型逐步过渡到工业级稳健爬虫兼顾代码可读性、复用性与稳定性同时梳理不同场景下的技术选型依据与避坑要点帮助开发者快速掌握多页爬虫的设计思想与落地方法。一、多页面循环爬取整体架构与场景分类1.1 核心应用场景划分根据页面 URL 特征、页面分布形式将多页面爬取划分为三大主流场景不同场景的循环逻辑、链接生成方式存在本质区别是代码设计的首要依据表格场景类型URL 特征页面分布特点循环实现方式典型应用场景固定链接集合页面地址无统一规则为离散独立 URL提前整理好所有待爬链接列表数量固定遍历列表循环精选文章合集、指定商品列表、自定义专题页面连续数字分页URL 包含连续数字参数页码规则统一页面地址由基础域名 递增页码组成页码线性变化数值自增循环简单列表页、个人博客分页、基础资讯栏目动态链接分页页码无明显数字规则需从列表页提取下一页链接必须解析当前页面获取后续页面地址无法拼接 URL递归 / 条件循环主流新闻网站、论坛、电商分类页、复杂资讯站点三种场景覆盖绝大多数多页采集需求其中离散链接遍历为基础模型数字分页为常用简化模型动态链接分页为工业项目主流模型。1.2 标准执行流程一套具备高可用性的多页面循环爬虫在单页面爬取流程基础上增加队列管理、循环控制、全局状态监控模块完整执行流程分为十个环节各环节串行执行形成闭环初始化配置定义基础 URL、待爬链接列表、请求延时、重试次数、存储路径、全局计数器等参数链接预处理对原始链接执行去重、过滤无效地址、格式标准化构建待爬队列循环入口判断根据场景选择列表遍历、数值递增、条件判断三种循环启动方式单页面请求解析调用已封装的单页爬取函数完成当前页面数据抽取、清洗数据临时缓存将单页结果存入内存列表避免频繁 IO 操作影响运行效率延时休眠设置固定间隔时间模拟人工浏览节奏规避高频请求触发反爬异常捕获与重试针对请求超时、连接失败等问题执行有限次数重试提升采集成功率循环终止判断判断是否达到终止条件包括队列清空、页码到达最大值、无下一页链接等批量数据持久化循环结束后将内存中所有缓存数据统一写入文件或数据库日志与统计输出输出总页面数、成功采集数、失败页面清单、运行时长等统计信息。1.3 核心设计原则结合实战经验多页面循环爬虫开发需遵守五大原则保障程序长期稳定运行 第一解耦复用单页爬取逻辑独立封装循环逻辑、队列逻辑、存储逻辑分层拆分模块间低耦合便于单独调试与修改 第二流量控制强制添加请求延时禁止无间隔高频请求控制单 IP 访问频率 第三容错重试网络波动属于常态对临时失败的页面设置重试机制单次失败不直接终止整体循环 第四状态可追溯记录失败链接、采集进度支持断点续爬避免中途终止后全量重爬 第五资源可控控制内存缓存数据量超大批量页面采用分批次存储防止内存溢出。二、运行环境与依赖说明2.1 依赖库功能汇总本文代码基于 Python 3.8 及以上版本开发全平台兼容所依赖库分为内置标准库与第三方库各库在多页爬虫中的作用如下表所示表格库名称库类型核心功能应用环节requests第三方库发送 HTTP 请求获取页面响应单页面数据请求、异常重试bs4(BeautifulSoup4)第三方库HTML 解析、字段提取、下一页链接抓取页面内容解析、动态分页链接获取re内置库正则匹配、文本清洗、规则校验数据清洗、URL 规则匹配urllib.parse内置库链接拼接、格式校正相对链接转绝对链接time内置库延时休眠、运行时间统计请求间隔控制、耗时统计json内置库JSON 文件读写、结构化数据存储单条 / 批量数据持久化pandas第三方库批量数据处理、CSV 文件读写大规模数据汇总存储2.2 依赖安装指令若未安装对应第三方库打开终端使用国内镜像源执行安装命令shellpip install requests beautifulsoup4 pandas -i https://pypi.tuna.tsinghua.edu.cn/simple导入库无报错即代表环境配置正常。三、基础前置模块复用多页面爬虫依赖前文封装的通用请求、编码转换、文本清洗、单页数据解析函数本节统一整合基础公共模块后续所有循环场景均直接复用不再重复编写底层逻辑。3.1 通用请求与异常处理函数python运行import requests import chardet import re import html import time from bs4 import BeautifulSoup from urllib.parse import urljoin import pandas as pd import json # 通用请求函数 def get_page_response(url: str, timeout: int 10): headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36, Accept-Language: zh-CN,zh;q0.9, Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8 } try: resp requests.get(url, headersheaders, timeouttimeout) resp.raise_for_status() return resp, 请求成功 except requests.exceptions.Timeout: return None, 请求超时 except requests.exceptions.HTTPError: return None, HTTP访问异常 except requests.exceptions.ConnectionError: return None, 网络连接失败 except Exception as e: return None, f未知异常{str(e)} # 编码转换函数 def decode_response(resp) - str: raw_bytes resp.content detect_res chardet.detect(raw_bytes) encode detect_res.get(encoding, utf-8) try: return raw_bytes.decode(encode) except UnicodeDecodeError: return raw_bytes.decode(utf-8, errorsignore) # 文本清洗函数 def clean_text(text: str) - str: text html.unescape(text) text re.sub(r[\n\r\t], , text) text re.sub(r[ ], , text) text text.strip() return text3.2 单页面数据解析函数以资讯文章页面为例提取标题、发布时间、作者、正文四大核心字段可根据业务自行调整解析规则python运行def parse_article(html_str: str) - dict: data { url: , title: , publish_time: , author: , content: } soup BeautifulSoup(html_str, html.parser) # 提取标题 title_tag soup.find(h1, class_article-title) if title_tag: data[title] clean_text(title_tag.get_text()) # 提取发布时间 time_tag soup.find(span, class_publish-time) if time_tag: data[publish_time] clean_text(time_tag.get_text()) # 提取作者 author_tag soup.find(span, class_author) if author_tag: data[author] clean_text(author_tag.get_text()) # 提取正文 content_tag soup.find(div, class_article-content) if content_tag: data[content] clean_text(content_tag.get_text()) return data # 单页面整合爬取函数 def single_page_crawl(url: str) - tuple[dict, bool]: 单页爬取入口 :return: (数据字典, 是否采集成功) resp, status get_page_response(url) if not resp: return {}, False html_content decode_response(resp) article_data parse_article(html_content) article_data[url] url return article_data, True四、场景一固定链接列表循环爬取该场景适用于页面地址离散、无统一 URL 规则的情况核心逻辑为提前定义链接列表使用 for 循环逐一遍历执行单页爬取是最简单、最基础的多页爬虫模型上手难度低、稳定性强。4.1 实现原理将所有待爬取的页面 URL 预先存入列表利用 Python 遍历循环依次取出每个链接调用单页爬取函数完成数据采集。循环过程中添加延时休眠控制访问频率同时记录采集失败的链接便于后续排查与补爬。整体逻辑线性执行无复杂跳转与链接生成逻辑。4.2 完整代码实现python运行def crawl_fixed_url_list(url_list: list, sleep_sec: float 1.5): 固定链接列表循环爬取 :param url_list: 待爬链接列表 :param sleep_sec: 页面之间请求延时单位秒 :return: 成功数据列表、失败链接列表 all_data [] fail_urls [] total_count len(url_list) success_count 0 print(f任务开始待爬页面总数{total_count}) for index, url in enumerate(url_list, 1): print(f正在采集第 {index}/{total_count} 页{url}) page_data, is_success single_page_crawl(url) if is_success: all_data.append(page_data) success_count 1 else: fail_urls.append(url) print(f第 {index} 页采集失败) # 请求延时控制访问频率 time.sleep(sleep_sec) # 输出统计信息 print( * 50) print(f采集完成 | 总页面{total_count} | 成功{success_count} | 失败{len(fail_urls)}) if fail_urls: print(采集失败的链接) for fail_url in fail_urls: print(fail_url) return all_data, fail_urls # 数据批量存储函数 def save_batch_data(data_list: list, csv_path: str fixed_page_data.csv, json_path: str fixed_page_data.json): # 保存为CSV df pd.DataFrame(data_list) df.to_csv(csv_path, indexFalse, encodingutf-8-sig) # 保存为JSON with open(json_path, w, encodingutf-8) as f: json.dump(data_list, f, ensure_asciiFalse, indent2) print(f批量数据已保存至 {csv_path} 和 {json_path}) # 测试运行 if __name__ __main__: # 自定义待爬链接列表 target_urls [ https://www.example.com/article/1.html, https://www.example.com/article/2.html, https://www.example.com/article/3.html, https://www.example.com/article/4.html ] result_data, fail_list crawl_fixed_url_list(target_urls, sleep_sec2) if result_data: save_batch_data(result_data)4.3 代码核心细节解析遍历计数使用enumerate获取当前页码与总页数实时打印采集进度提升可视化效果延时控制time.sleep()设置页面间隔常规站点建议 1~3 秒高防护站点可延长至 3~5 秒结果分类存储分别记录成功数据与失败链接区分正常数据和异常页面方便后续补爬批量存储循环全部结束后统一写入文件相比单页即时 IO大幅减少磁盘读写次数提升运行效率。4.4 适用范围与优缺点优点逻辑简单、代码易维护、运行稳定、无链接拼接错误风险适合小规模定向采集 缺点需要手动整理所有链接页面数量庞大时人工成本高无法适配动态分页场景 适用场景指定专题页面、精选内容、数量少于 100 页的小规模采集任务。五、场景二连续数字分页循环爬取多数简易列表页、博客栏目会采用基础 URL 数字页码的格式例如https://www.example.com/list?page1、https://www.example.com/list?page2页码从 1 开始连续递增。该场景无需手动整理链接通过数值自增循环自动生成每页 URL自动化程度高于固定列表模式。5.1 实现原理定义基础 URL 模板、起始页码、最大页码使用 while 循环或 for 循环让页码持续自增动态拼接出每一页的完整 URL。循环终止条件为页码达到预设最大值过程中同样添加延时、失败记录逻辑。根据 URL 格式分为参数拼接型与路径拼接型两种拼接方式。5.2 方式一参数拼接分页主流URL 以请求参数携带页码格式base_url?page页码python运行def crawl_number_page(base_url: str, start_page: int, end_page: int, sleep_sec: float 2): 数字参数分页爬取url?page1 格式 all_data [] fail_urls [] print(f分页任务开始页码范围{start_page} - {end_page}) for page in range(start_page, end_page 1): # 拼接完整URL current_url f{base_url}?page{page} print(f正在采集第 {page} 页{current_url}) page_data, is_success single_page_crawl(current_url) if is_success: all_data.append(page_data) else: fail_urls.append(current_url) print(f第 {page} 页采集失败) time.sleep(sleep_sec) # 统计输出 total end_page - start_page 1 print( * 50) print(f分页采集完成 | 总页数{total} | 成功{len(all_data)} | 失败{len(fail_urls)}) if fail_urls: print(失败链接列表) for url in fail_urls: print(url) return all_data, fail_urls # 测试运行 if __name__ __main__: # 基础URL不含页码参数 base https://www.example.com/list data, fails crawl_number_page(base, start_page1, end_page10, sleep_sec2) if data: save_batch_data(data, csv_pathnumber_page_data.csv)5.3 方式二路径拼接分页页码作为 URL 路径的一部分格式base_url/1、base_url/2仅需调整拼接规则python运行# 核心拼接代码替换为以下内容 current_url f{base_url}/{page}5.4 进阶优化循环重试机制针对临时网络故障导致的采集失败增加单次页面重试功能设置最大重试次数提升采集成功率python运行def single_page_crawl_with_retry(url: str, retry_times: int 2) - tuple[dict, bool]: 带重试机制的单页爬取 for i in range(retry_times 1): page_data, is_success single_page_crawl(url) if is_success: return page_data, True # 重试间隔 time.sleep(1) return {}, False将原代码中的single_page_crawl替换为该函数即可实现失败页面自动重试。5.5 适用范围与优缺点优点全自动生成 URL无需手动整理链接代码简洁适合大批量连续分页 缺点依赖固定数字页码规则若站点修改 URL 格式、页码不连续则程序失效无法识别真实最后一页必须手动设置最大页码 适用场景个人博客、小型资讯站、结构简单的列表页。六、场景三动态下一页链接循环爬取主流大型网站、论坛、电商平台不会对外暴露完整页码规则页面跳转依靠下一页按钮实现必须解析当前页面 HTML提取下一页对应的链接再进入下一轮循环。该方案不依赖 URL 规则适配性最强是工业级多页爬虫的首选方案。6.1 实现原理传入初始列表页 URL采集当前页面数据解析页面中的「下一页」标签提取对应的href链接将相对链接转换为绝对链接作为下一轮循环的目标地址循环执行上述步骤当页面中无下一页链接时判定为全部页面采集完成终止循环全程使用while条件循环以 “是否存在下一页” 作为循环终止条件。6.2 提取下一页链接函数实现python运行def get_next_page_link(current_url: str) - str: 从当前页面提取下一页链接 :param current_url: 当前页面地址 :return: 下一页绝对链接无则返回空字符串 resp, _ get_page_response(current_url) if not resp: return html_str decode_response(resp) soup BeautifulSoup(html_str, html.parser) # 根据站点实际class/text定位下一页标签按需修改规则 next_tag soup.find(a, text下一页) if not next_tag: next_tag soup.find(a, class_next-page) if next_tag and next_tag.get(href): # 相对链接转绝对链接 next_href next_tag.get(href).strip() next_url urljoin(current_url, next_href) return next_url return 6.3 完整动态分页循环代码python运行def crawl_dynamic_page(start_url: str, sleep_sec: float 2): 动态下一页循环爬取自动识别最后一页 all_data [] fail_urls [] current_url start_url page_num 1 print(动态分页爬虫启动...) while current_url: print(f正在采集第 {page_num} 页{current_url}) page_data, is_success single_page_crawl(current_url) if is_success: all_data.append(page_data) else: fail_urls.append(current_url) print(f第 {page_num} 页采集失败) # 延时控制 time.sleep(sleep_sec) # 获取下一页链接 current_url get_next_page_link(current_url) page_num 1 # 统计信息输出 print( * 50) print(f动态分页采集完成 | 总页数{page_num - 1} | 成功{len(all_data)} | 失败{len(fail_urls)}) if fail_urls: print(失败链接) for url in fail_urls: print(url) return all_data, fail_urls # 测试运行 if __name__ __main__: # 传入第一页地址 first_page_url https://www.example.com/news/list result, fails crawl_dynamic_page(first_page_url, sleep_sec2) if result: save_batch_data(result, csv_pathdynamic_page_data.csv)6.4 关键规则适配说明不同网站的 “下一页” 标签特征不同需根据实际页面修改定位规则常见匹配方式根据文本匹配soup.find(a, text下一页)适用于文字型按钮根据类名匹配soup.find(a, class_next)适用于样式区分的按钮根据链接特征正则匹配使用正则匹配href中包含next、page等关键词的链接。6.5 适用范围与优缺点优点不依赖 URL 页码规则自动识别首尾页面适配绝大多数商业网站通用性最强 缺点每轮循环需要额外请求页面提取下一页链接增加一次网络请求页面结构改版会导致链接提取失效 适用场景新闻门户、论坛、电商分类、大型资讯站点是项目开发首选方案。七、综合拓展队列式多页爬虫支持断点续爬当页面数量达到数百页、上千页时基础循环模型无法支持断点续爬一旦程序中断需要从头重爬。本节基于列表队列实现带断点续爬的高级多页爬虫将待爬链接存入队列区分已爬、待爬、失败三类状态支持中断后继续执行。7.1 队列模型原理使用列表模拟爬虫队列队列分为待爬队列、已爬集合、失败队列三大容器。每次从待爬队列取出链接执行采集完成后移入已爬集合采集失败则移入失败队列。运行过程中将队列状态保存至本地文件程序重启时读取状态文件继续处理剩余链接实现断点续爬。7.2 核心代码实现python运行import pickle # 保存队列状态至本地文件 def save_queue_state(wait_queue: list, done_set: set, fail_queue: list, file_path: str queue_state.pkl): state { wait_queue: wait_queue, done_set: list(done_set), fail_queue: fail_queue } with open(file_path, wb) as f: pickle.dump(state, f) # 读取队列状态 def load_queue_state(file_path: str queue_state.pkl) - tuple[list, set, list]: try: with open(file_path, rb) as f: state pickle.load(f) return state[wait_queue], set(state[done_set]), state[fail_queue] except FileNotFoundError: return [], set(), [] # 队列式断点续爬爬虫 def crawl_with_queue(init_url_list: list, sleep_sec: float 2): # 加载历史状态无文件则初始化 wait_queue, done_set, fail_queue load_queue_state() # 新增链接加入待爬队列去重 for url in init_url_list: if url not in done_set and url not in wait_queue: wait_queue.append(url) print(f断点续爬启动 | 待爬数量{len(wait_queue)} | 已爬数量{len(done_set)}) all_data [] while wait_queue: current_url wait_queue.pop(0) print(f正在采集{current_url}) page_data, is_success single_page_crawl(current_url) if is_success: all_data.append(page_data) done_set.add(current_url) else: fail_queue.append(current_url) print(f采集失败{current_url}) # 保存当前队列状态实现断点续爬 save_queue_state(wait_queue, done_set, fail_queue) time.sleep(sleep_sec) print(队列任务全部执行完毕) return all_data, fail_queue # 测试运行 if __name__ __main__: init_urls [ https://www.example.com/1.html, https://www.example.com/2.html, https://www.example.com/3.html ] data, fails crawl_with_queue(init_urls, sleep_sec2) if data: save_batch_data(data, csv_pathqueue_crawl_data.csv)7.3 功能说明pickle 序列化将队列、集合等复杂数据结构保存至本地文件程序重启后恢复运行状态实时状态保存每处理完一条链接立即保存状态断电、手动终止后均可从断点继续自动去重判断链接是否已爬避免重复采集 该模型适用于超大规模页面采集、长时间运行的爬虫任务是分布式爬虫的基础雏形。八、全场景问题排查与综合优化方案8.1 高频问题及解决方案请求被封禁 IP成因请求间隔过短、请求头不完善、并发过高。 优化延长sleep延时至 2~5 秒完善请求头必要时使用代理 IP 轮换。页码循环超出真实页面成因数字分页手动设置最大页码过大。 优化优先使用动态下一页模式增加页面内容校验空白页面自动终止循环。相对链接拼接错误成因未使用urljoin手动拼接路径。 优化统一使用urljoin完成相对链接转绝对链接。内存占用持续升高成因大批量数据全部缓存至内存。 优化分批次写入文件每采集 50~100 页执行一次持久化操作。循环死锁 / 无限循环成因下一页链接提取错误重复跳转同一页面。 优化增加已爬链接集合重复链接直接终止循环。8.2 通用性能优化建议IO 优化循环内减少频繁文件读写采用内存缓存 批量落地的模式异常防护所有网络请求、节点解析位置增加异常捕获单页失败不中断整体循环日志优化正式项目中将 print 输出替换为 logging 日志模块分级记录运行信息资源释放长时间运行的爬虫定期释放无用变量降低内存占用。

相关新闻