Python 爬虫项目:单页面完整爬取流程
2026/6/11 16:36:53 网站建设 项目流程

前言

单页面爬取是 Python 爬虫体系中最基础、应用最广泛的核心单元,也是构建多页循环爬取、全站采集、分布式爬虫的底层基石。所谓单页面爬取,指针对单个目标 URL 完成请求发送、源码获取、内容解析、数据抽取、清洗规整、结果存储的全链路操作,覆盖爬虫从网络交互到数据落地的完整业务闭环。在实际开发场景中,资讯文章、商品详情、帖子内容、个人主页等单一载体的数据采集,均依赖标准单页爬取流程实现。

未经规范化设计的爬取代码往往存在请求不稳定、解析容错差、数据格式混乱、异常无法捕获等问题,不仅单次采集成功率低,也无法直接复用至复杂爬虫项目。本文结合工业级开发标准,拆解单页面爬虫每一个执行环节的底层逻辑、技术选型、编码规范与异常处理机制,搭配可直接运行的代码案例、原理剖析、规则说明,同时融合前文讲解的数据清洗、链接处理技术,形成一体化的单页采集解决方案。

本文所使用的开发库及官方资源链接如下: Python 官方下载地址、requests 库官方文档、BeautifulSoup4 官方文档、re 正则表达式模块文档、urllib 标准库文档、pandas 数据处理库、json 标准库文档。

全文按照流程拆解→环境准备→分环节实现→全流程整合→多场景适配→问题优化的结构展开,兼顾入门实操与工程化规范,同时区分静态 HTML 页面、JSON 接口页面两类主流数据源,讲解不同解析方案的选型依据,帮助开发者搭建标准化、高可用、可复用的单页面爬虫模板。

一、单页面爬虫整体架构与核心流程

1.1 应用场景划分

根据网页数据渲染方式与数据载体,将单页面爬取划分为两大主流场景,二者技术方案、解析逻辑存在明显差异,也是开发前首要区分的内容:

表格

页面类型数据特征渲染方式主流解析方案典型应用场景
静态 HTML 页面数据直接嵌入 HTML 标签,查看网页源码即可看到完整内容服务端渲染,一次性返回完整页面BeautifulSoup 标签解析、正则表达式新闻资讯、博客文章、传统论坛、企业官网
接口渲染页面页面骨架为空 HTML,核心数据通过异步 AJAX 请求加载 JSON 数据前端动态渲染,浏览器二次请求接口json 库解析、正则截取 JSON 字符串电商详情、短视频页面、现代资讯站点、移动端网页

两种场景覆盖当前互联网绝大多数网页形态,本文会分别给出对应实现方案,同时统一通用流程规范。

1.2 标准执行流程

一套健壮的工业级单页面爬虫,严格遵循串行执行流程,每个环节各司其职,且配套异常捕获与容错机制,完整流程共分为八大步骤,执行顺序不可随意调整:

  1. 参数初始化:定义目标 URL、请求头、超时时间、代理、Cookie 等请求配置信息,模拟正常客户端访问环境;
  2. 网络请求发送:基于 HTTP 协议向目标服务器发起请求,接收服务端返回的响应数据与状态码;
  3. 响应状态校验:判断请求结果,根据 HTTP 状态码区分正常响应、页面不存在、访问受限、服务器错误等情况;
  4. 编码统一处理:识别网页编码格式,完成字节流转码,彻底解决中文乱码问题;
  5. 数据解析抽取:根据页面类型,使用标签解析或 JSON 解析,精准提取标题、正文、时间、作者等目标字段;
  6. 数据清洗规整:调用前文数据清洗逻辑,剔除 HTML 标签、空白字符、特殊符号、冗余文本,标准化数据格式;
  7. 数据持久化存储:将清洗后的结构化数据保存至文本、CSV、JSON、数据库等载体;
  8. 收尾与日志记录:记录爬取结果、异常信息、耗时等内容,方便后期排查与统计。

1.3 核心设计原则

为保证爬虫的稳定性、可移植性与可维护性,单页面爬虫开发需要遵循四项基本原则: 第一,分层解耦。将请求、解析、清洗、存储拆分为独立函数,单一函数只负责一项功能,便于单独调试、修改与复用; 第二,全链路异常捕获。网络波动、服务器宕机、页面结构变更、数据缺失等问题均会导致程序报错,必须对每一个风险点添加异常捕获; 第三,模拟真实访问。配置标准请求头、合理设置请求间隔,规避基础反爬策略,降低被封禁 IP 的概率; 第四,字段兼容设计。考虑页面字段缺失、内容为空的场景,设置默认值,保证程序不会因局部数据异常终止运行。

二、运行环境与依赖配置

2.1 库功能与使用场景汇总

本文所有代码基于 Python 3.8 及以上版本开发,兼容全主流操作系统,所依赖库分为 Python 内置标准库与第三方拓展库,具体功能及在单页爬虫中的应用环节如下表:

表格

库名称库类型核心功能对应执行环节
requests第三方库发送 HTTP/HTTPS 请求,获取网页响应网络请求、状态校验
bs4(BeautifulSoup4)第三方库HTML 文档 DOM 树构建、节点查找、字段提取静态页面数据解析
re内置库正则匹配、字符替换、内容截取数据清洗、非常规内容提取
urllib.parse内置库URL 拼接、参数解析、链接格式化请求参数处理、链接校验
json内置库JSON 字符串与字典相互转换、接口数据解析异步接口页面数据抽取
pandas第三方库结构化数据批量存储、文件读写结果持久化、多数据汇总存储

2.2 依赖安装命令

若本地未安装第三方库,打开终端使用国内清华镜像源执行安装指令,提升下载速度与稳定性:

shell

pip install requests beautifulsoup4 pandas -i https://pypi.tuna.tsinghua.edu.cn/simple

安装完成后,在 Python 交互环境中依次导入上述库,无报错则代表环境配置完成。

三、基础模块实现:通用请求封装

网络请求是单页面爬虫的入口,也是最容易出现异常的环节。本节封装通用请求函数,统一实现请求头配置、超时设置、编码处理、异常捕获,该函数可复用于所有单页爬取场景。

3.1 HTTP 请求基础原理

客户端与网页服务器基于 HTTP 协议完成数据交互,整体流程分为三步:客户端组装请求报文(包含请求方式、请求头、请求参数)→ 服务器接收请求并处理 → 服务器返回响应报文(状态码、响应头、网页源码 / 数据)。

requests 库对原生 socket 网络请求进行了高度封装,简化了报文组装、连接管理、编码转换等底层操作,支持 GET、POST 等主流请求方式,是 Python 爬虫领域的首选请求工具。网页访问以 GET 请求为主,本文核心围绕 GET 请求展开。

3.2 通用请求函数代码实现

python

运行

import requests def get_page_response(url: str, timeout: int = 10): """ 通用网页请求函数,返回响应对象与状态信息 :param url: 目标网页地址 :param timeout: 请求超时时间,单位秒 :return: 元组(响应对象, 状态描述) """ # 模拟浏览器请求头,规避基础反爬 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;q=0.9", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8" } try: # 发起GET请求 resp = requests.get(url, headers=headers, timeout=timeout) # 手动触发异常:状态码非200则抛出HTTP错误 resp.raise_for_status() return resp, "请求成功" except requests.exceptions.Timeout: return None, "请求超时" except requests.exceptions.HTTPError: return None, f"HTTP错误,状态码:{resp.status_code if 'resp' in locals() else '未知'}" except requests.exceptions.ConnectionError: return None, "网络连接失败,域名无法访问或IP被封禁" except Exception as e: return None, f"未知异常:{str(e)}"

3.3 代码原理与关键细节解析

  1. 请求头配置逻辑:除核心的 User-Agent 外,补充 Accept、Accept-Language 等字段,完整模拟主流浏览器的请求特征,大幅降低被服务器拦截的概率,是工程爬虫的标准配置。
  2. 超时机制:timeout 参数限制单次请求的最大等待时长,避免目标服务器响应缓慢导致程序卡死,常规场景设置为 10 秒即可。
  3. 状态码校验resp.raise_for_status()方法会主动检测 HTTP 状态码,当状态码为 4xx(客户端错误)、5xx(服务端错误)时,主动抛出异常,便于分类处理错误场景。
  4. 分类异常捕获:针对超时、连接失败、HTTP 错误、未知异常做分层捕获,返回明确的状态描述,方便日志记录与问题定位。

3.4 编码统一处理逻辑

请求成功后,需要对响应内容进行转码,解决中文乱码问题。结合 chardet 库与 requests 自带编码识别能力,封装编码转换函数:

python

运行

import chardet def decode_response(resp) -> str: """ 自动识别编码并完成转码,返回标准字符串 :param resp: requests响应对象 :return: 转码后的网页源码 """ # 获取原始字节流 raw_bytes = resp.content # 检测字节流编码 detect_res = chardet.detect(raw_bytes) encode = detect_res.get("encoding", "utf-8") # 容错解码 try: html_str = raw_bytes.decode(encode) except UnicodeDecodeError: html_str = raw_bytes.decode("utf-8", errors="ignore") return html_str

该函数沿用前文编码修复思路,优先通过字符频率检测真实编码,解码失败时忽略异常字节,保证主体内容可用。

四、场景一:静态 HTML 单页面爬取实现

静态 HTML 页面是传统网页的主要形态,数据全部内嵌在 HTML 标签中,使用 BeautifulSoup 解析 DOM 树提取目标字段,搭配数据清洗逻辑完成全流程开发。本节以资讯文章页面为例,提取标题、发布时间、作者、正文、来源五大核心字段。

4.1 页面解析底层原理

BeautifulSoup 将 HTML 字符串解析为 DOM 节点树,整个页面由根节点、父节点、子节点、文本节点、属性节点构成。数据提取的本质是通过标签名、属性(class/id)、层级关系定位目标节点,再读取节点内的文本内容或属性值。

常用定位方式分为三类:根据标签属性精准定位、根据标签层级遍历、根据文本内容模糊匹配,开发中优先使用 class、id 属性定位,该方式稳定性最高,受页面小幅改版影响最小。

4.2 字段解析函数开发

结合 DOM 节点定位规则,编写独立的解析函数,从 HTML 源码中抽取结构化字段:

python

运行

from bs4 import BeautifulSoup def parse_static_html(html_str: str) -> dict: """ 解析静态HTML页面,提取文章核心字段 :param html_str: 转码完成的HTML源码 :return: 包含各字段的字典数据 """ # 初始化空字典,设置默认值,避免字段缺失报错 article_data = { "title": "", "publish_time": "", "author": "", "source": "", "content": "" } # 构建DOM树 soup = BeautifulSoup(html_str, "html.parser") # 1. 提取标题:根据class属性定位标题标签 title_tag = soup.find("h1", class_="article-title") if title_tag: article_data["title"] = title_tag.get_text(strip=True) # 2. 提取发布时间 time_tag = soup.find("span", class_="publish-time") if time_tag: article_data["publish_time"] = time_tag.get_text(strip=True) # 3. 提取作者 author_tag = soup.find("span", class_="author") if author_tag: article_data["author"] = author_tag.get_text(strip=True) # 4. 提取来源 source_tag = soup.find("span", class_="source") if source_tag: article_data["source"] = source_tag.get_text(strip=True) # 5. 提取正文内容 content_tag = soup.find("div", class_="article-content") if content_tag: # 提取标签内所有文本,保留段落结构 article_data["content"] = content_tag.get_text() return article_data

4.3 数据清洗函数复用

调用前文开发的文本清洗逻辑,对提取到的正文、标题等内容做标准化处理,剔除空白字符、HTML 残留标签、特殊符号:

python

运行

import re import html def clean_text_data(text: str) -> str: """ 文本综合清洗函数 """ # 还原HTML转义字符 text = html.unescape(text) # 清除换行、回车、制表符 text = re.sub(r"[\n\r\t]", "", text) # 合并全角、半角空格 text = re.sub(r"[ ]+", " ", text) # 清除首尾空白 text = text.strip() # 过滤特殊符号 symbol_pat = re.compile(r"[★☆※§@#$%^&*]") text = symbol_pat.sub("", text) return text # 批量清洗所有字段 def clean_article_dict(data: dict) -> dict: for key in data.keys(): data[key] = clean_text_data(data[key]) return data

4.4 数据持久化实现

提供两种主流存储方案:单条数据保存为 JSON 文件、多条数据汇总保存为 CSV 文件,适配不同使用场景。

4.4.1 JSON 文件存储(适合单条独立数据)

python

运行

import json def save_to_json(data: dict, file_path: str): """保存字典数据至JSON文件""" with open(file_path, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) print(f"数据已保存至:{file_path}")
4.4.2 CSV 文件存储(适合批量汇总数据)

python

运行

import pandas as pd def save_to_csv(data: dict, file_path: str, append: bool = True): """ 保存数据至CSV文件,支持追加写入 :param append: True为追加,False为覆盖 """ df = pd.DataFrame([data]) if append: # 追加模式,文件不存在则新建 df.to_csv(file_path, mode="a", header=not os.path.exists(file_path), index=False, encoding="utf-8-sig") else: df.to_csv(file_path, index=False, encoding="utf-8-sig") print(f"数据已写入CSV文件:{file_path}")

4.5 静态页面全流程整合主函数

将请求、解码、解析、清洗、存储所有模块整合,形成可直接调用的单页爬取主函数:

python

运行

import os def crawl_static_page(url: str, json_path: str = "article.json", csv_path: str = "article.csv"): """ 静态HTML页面一站式爬取主函数 """ # 步骤1:发起网络请求 resp, status = get_page_response(url) if not resp: print(f"爬取失败:{status}") return # 步骤2:编码转换,获取标准HTML源码 html_content = decode_response(resp) # 步骤3:解析HTML,抽取结构化数据 raw_data = parse_static_html(html_content) # 步骤4:数据清洗规整 clean_data = clean_article_dict(raw_data) # 步骤5:持久化存储 save_to_json(clean_data, json_path) save_to_csv(clean_data, csv_path) print("单页面爬取完成,所有数据处理完毕") return clean_data # 测试调用 if __name__ == "__main__": target_url = "https://www.example.com/article.html" result = crawl_static_page(target_url) print("提取结果:", result)

五、场景二:JSON 接口页面爬取实现

现代前端页面普遍采用前后端分离架构,HTML 仅作为页面骨架,核心数据通过 AJAX 异步请求后端接口获取,接口返回标准 JSON 格式字符串。此类页面无法直接从 HTML 源码中提取有效数据,需要抓取接口地址,解析 JSON 数据。

5.1 接口页面爬取原理

浏览器加载页面分为两个阶段:第一阶段请求 HTML 骨架页面,第二阶段自动发起多个异步请求调用后端接口,获取标题、内容、图片等数据并渲染至页面。爬虫开发思路为:抓包找到数据接口 URL → 直接请求接口 → 将返回的 JSON 字符串转为 Python 字典 → 按键值抽取目标字段。

接口请求同样使用 GET/POST 方式,大部分公开接口可直接请求,部分接口需要携带 Token、Cookie、请求参数等验证信息。

5.2 JSON 接口解析代码实现

python

运行

import json def parse_json_interface(resp) -> dict: """ 解析接口返回的JSON数据,抽取目标字段 :param resp: 接口请求响应对象 :return: 结构化数据字典 """ api_data = { "title": "", "content": "", "create_time": "", "views": 0 } try: # 将JSON字符串转为Python字典 json_str = resp.text data_dict = json.loads(json_str) # 根据接口字段层级提取数据,需按照实际接口结构调整 api_data["title"] = data_dict.get("data", {}).get("title", "") api_data["content"] = data_dict.get("data", {}).get("content", "") api_data["create_time"] = data_dict.get("data", {}).get("create_time", "") api_data["views"] = data_dict.get("data", {}).get("views", 0) except json.JSONDecodeError: print("接口返回内容非标准JSON格式") except Exception as e: print(f"JSON解析异常:{str(e)}") return api_data

5.3 接口页面全流程整合函数

复用通用请求、数据清洗、存储模块,编写接口类页面专属爬取函数:

python

运行

def crawl_json_api_page(api_url: str): """ 异步JSON接口页面爬取主函数 """ # 发起接口请求 resp, status = get_page_response(api_url) if not resp: print(f"接口请求失败:{status}") return # 解析JSON数据 raw_data = parse_json_interface(resp) # 文本字段清洗 clean_data = clean_article_dict(raw_data) # 数据存储 save_to_json(clean_data, "api_data.json") save_to_csv(clean_data, "api_data.csv") print("接口页面爬取完成") return clean_data # 测试调用 if __name__ == "__main__": api_url = "https://www.example.com/api/content?id=1001" crawl_json_api_page(api_url)

六、进阶拓展:带参数与 Cookie 的单页爬取

部分页面需要携带请求参数、登录 Cookie 才能正常访问,本节补充对应实现方案,完善复杂场景下的单页爬虫能力。

6.1 携带 URL 参数请求

URL 参数拼接在地址后方,格式为url?key1=value1&key2=value2,使用urllib.parse自动拼接参数,避免手动拼接出错:

python

运行

from urllib.parse import urlencode def request_with_params(base_url: str, params: dict): """携带参数发起GET请求""" # 自动拼接参数 param_str = urlencode(params) full_url = f"{base_url}?{param_str}" resp, status = get_page_response(full_url) return resp, status

6.2 携带 Cookie 请求

登录态页面依赖 Cookie 验证身份,直接将 Cookie 添加至请求头即可实现模拟登录访问:

python

运行

def get_page_with_cookie(url: str, cookie_str: str): headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/122.0.0.0 Safari/537.36", "Cookie": cookie_str } try: resp = requests.get(url, headers=headers, timeout=10) resp.raise_for_status() return resp, "请求成功" except Exception as e: return None, f"请求失败:{str(e)}"

七、全链路异常汇总与优化方案

结合实战经验,整理单页面爬虫开发与运行过程中的高频问题、成因及优化手段,提升程序稳定性:

7.1 中文乱码问题

成因:编码识别错误、页面使用 GBK/GB2312 编码。 优化:保留 chardet 编码检测逻辑,增加备用编码列表,依次尝试解码。

7.2 节点提取为空

成因:标签 class/id 名称变更、节点层级变化、页面动态渲染。 优化:多写几套节点定位规则做兼容;静态页面改用多特征组合定位;动态页面切换为接口抓取。

7.3 请求频繁被封禁

成因:请求头不完善、访问速度过快、无请求间隔。 优化:完善请求头,单次爬取完成后添加延时time.sleep(1~3),模拟人工浏览节奏。

7.4 JSON 解析失败

成因:接口返回非标准 JSON、存在多余字符、跨域限制。 优化:使用正则截取 JSON 主体部分,补充异常捕获;对受限接口配置代理。

7.5 数据重复存储

成因:重复执行爬取代码,多次写入文件。 优化:爬取前判断文件是否存在,或使用唯一字段做去重校验。

八、通用爬虫模板封装

将所有功能整合为一套通用模板,可直接修改节点定位规则、URL、字段名称,快速适配任意单页面采集需求,作为项目通用工具类使用:

python

运行

import requests import re import html import json import chardet from bs4 import BeautifulSoup import os # 1. 通用请求函数 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;q=0.9" } try: resp = requests.get(url, headers=headers, timeout=timeout) 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"其他异常:{e}" # 2. 编码转换 def decode_response(resp) -> str: raw_bytes = resp.content detect = chardet.detect(raw_bytes) encode = detect.get("encoding", "utf-8") try: return raw_bytes.decode(encode) except: return raw_bytes.decode("utf-8", errors="ignore") # 3. 文本清洗 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 text # 4. 存储函数 def save_json(data, path): with open(path, "w", encoding="utf-8") as f: json.dump(data, f, ensure_ascii=False, indent=2) def save_csv(data, path): import pandas as pd df = pd.DataFrame([data]) df.to_csv(path, mode="a", header=not os.path.exists(path), index=False, encoding="utf-8-sig") # 5. 通用单页爬取入口 def general_single_crawl(url: str): resp, status = get_page_response(url) if not resp: print(status) return html_str = decode_response(resp) soup = BeautifulSoup(html_str, "html.parser") # ========== 此处根据目标页面修改节点定位规则 ========== res_data = { "title": clean_text(soup.find("h1").get_text() if soup.find("h1") else ""), "content": clean_text(soup.find("div", class_="content").get_text() if soup.find("div", class_="content") else "") } # ===================================================== save_json(res_data, "crawl_result.json") save_csv(res_data, "crawl_result.csv") print("通用模板爬取完成") return res_data if __name__ == "__main__": general_single_crawl("https://www.example.com/test.html")

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询