前言
爬虫开发中,拿到 http 返回的 html 原始源码并不是终点。原始页面经常夹杂 bom 标记、多余空白、无效注释、转义字符、不规范标签,直接拿去解析、提取文本、做相似度对比或者文件类型判断,很容易踩各种隐形大坑。
html 预处理,本质是在正式解析之前,对原始网页做清洗、规整。它不等同于 html 压缩,压缩更多是为减小体积;预处理目标更宽泛:修复脏数据、消除干扰项,为后续 xpath / css选择器 / 文本提取提供干净可靠的输入。
本文结合爬虫实战,梳理一套完整的 html 预处理流水线,覆盖从原始 bytes 到干净文本的全流程。
html预处理一般要做哪些工作
一套标准预处理流程顺序(非常关键,顺序不能乱):
- 二进制预处理:剔除 utf-8 bom、截断超长响应、过滤无效二进制头部
- 编码自动识别 & 解码:把 bytes 转为字符串,解决网页乱码
- 清洗干扰内容:移除注释、多余换行/空格,按需压缩空白
- 标签规范化:补全残缺标签、处理转义字符
- 可选过滤:移除无用标签(script/style/noscript),减少解析压力
重点提醒:操作顺序建议:bytes处理 → 解码 → 文本清洗。不要先解码再处理bom,会引入不必要的
\ufeff字符。
第一步:原始二进制处理,清除utf-8 bom
网页由windows服务器或记事本生成时,很容易在最前面带上b'\xef\xbb\xbf'。
它不属于html正文,但是会干扰魔数判断、前缀匹配,lstrip()无法自动移除。
def strip_bom_and_prefix_whitespace(raw_bytes: bytes) -> bytes:
# 移除utf-8 bom
if raw_bytes.startswith(b"\xef\xbb\xbf"):
raw_bytes = raw_bytes[3:]
# 剔除文档开头空白(空格、换行、tab、回车)
raw_bytes = raw_bytes.lstrip(b" \r\n\t")
return raw_bytes
这一步对应我们之前写的detect_file_type函数,在解码之前执行,性能更高,规避解码异常。
第二步:编码识别,bytes转文本
很多网页content-type里写的编码和页面meta声明不一致,直接用resp.content.decode("utf-8")大概率乱码。
推荐使用cchardet/chardet自动探测编码。
pip install cchardet
import cchardet
def decode_html(raw_bytes: bytes) -> str:
detect_result = cchardet.detect(raw_bytes)
encoding = detect_result["encoding"] or "utf-8"
try:
return raw_bytes.decode(encoding)
except exception:
# 探测编码失败时兜底
return raw_bytes.decode("utf-8", errors="replace")
小技巧:可以优先从response headers、html meta标签提取charset,优先级高于自动探测,准确率更高。
第三步:文本清洗,去除注释与多余空白
这里要区分两种场景:
- 保留页面结构,只删除注释、合并多余空白(推荐,用于解析)
- 强压缩,删除标签间空白(适合存储、文本比对,不建议用于解析)
❌ 禁止简单正则全局删除空白,<pre> <textarea> <script> <style>内部的空白不能随意清除。
import re
import htmlmin
def clean_html_text(html: str, remove_comments=true) -> str:
if remove_comments:
# 删除html注释,注意:不会处理script内部注释
html = re.sub(r"<!--[\s\s]*?-->", "", html)
# 轻量清洗,保留pre/script/style内部格式
html = htmlmin.minify(html, remove_comments=false, remove_all_empty_space=false)
return html
按需移除无用标签(爬虫常用)
如果我们只需要提取正文文本,不需要js、css,可以直接删除script、style标签,减少解析开销:
from bs4 import beautifulsoup
def remove_useless_tags(html: str) -> str:
soup = beautifulsoup(html, "html.parser")
for tag in soup(["script", "style", "noscript"]):
tag.decompose()
return str(soup)
注意:移除script/style会改变页面dom结构,如果你的业务需要执行js、提取内嵌css,不能使用这一步。
完整预处理流水线示例(爬虫实战)
组合上面所有步骤,搭配requests,形成可直接复用的预处理函数:
import requests
import cchardet
import htmlmin
from bs4 import beautifulsoup
def preprocess_html(resp: requests.response) -> str:
# 1. 获取原始二进制,清理bom和前置空白
raw_bytes = resp.content
if raw_bytes.startswith(b"\xef\xbb\xbf"):
raw_bytes = raw_bytes[3:]
raw_bytes = raw_bytes.lstrip(b" \r\n\t")
# 2. 编码探测并解码
detect_result = cchardet.detect(raw_bytes)
encoding = detect_result["encoding"] or "utf-8"
try:
html_str = raw_bytes.decode(encoding)
except exception:
html_str = raw_bytes.decode("utf-8", errors="replace")
# 3. 删除注释
html_str = re.sub(r"<!--[\s\s]*?-->", "", html_str)
# 4. 移除script/style标签(按需开启)
soup = beautifulsoup(html_str, "html.parser")
for tag in soup(["script", "style"]):
tag.decompose()
html_str = str(soup)
# 5. 轻量清洗多余空白
html_str = htmlmin.minify(html_str, remove_comments=false, remove_all_empty_space=false)
return html_str
if __name__ == "__main__":
r = requests.get("[https://example.com](https://example.com)")
clean_html = preprocess_html(r)
print(clean_html)
常见踩坑点
- bom处理时机:bom必须在bytes阶段处理;解码后会变成
\ufeff,容易被忽略,干扰文本匹配。 - 编码冲突:http header的charset、meta标签charset、页面实际编码三者可能不一致,自动探测只能做兜底,优先解析meta标签。
- 正则不能万能:不要手写正则去删除标签,html不是规则文本,正则极易破坏dom;标签删除交给beautifulsoup。
- 预处理≠压缩:做xpath/css选择器解析时,不要使用强压缩模式,虽然体积更小,但调试困难,极少提升解析速度。
- 转义字符:
<&这类实体,一般保留原样;只有提取纯文本展示时才调用html.unescape()。 - 异常页面:部分网页存在不闭合标签,bs4会自动修复,这也是预处理的一部分。
不同业务场景预处理策略
- 网页文件类型判断:只做二进制bom清洗,不解码,直接用原始bytes做魔数判断(就是前面detect_file_type方案)
- dom解析、字段提取:bom清洗 → 解码 → 删除注释 → 移除script/style → 轻量空白规整
- 网页归档存储:在上面基础上,开启htmlmin强压缩,减小存储体积
- 网页正文相似度比对:全部预处理完成后,再提取纯文本,消除换行空格带来的干扰
小结
html预处理不是简单的“删空格”,而是一套分层清洗流水线:二进制预处理优先,然后处理编码,再做dom清洗。
根据业务选择清洗力度:类型检测尽量不解码;dom解析用bs4安全移除无用标签;归档场景再开启压缩。
在爬虫项目中,把bom清洗、编码探测、注释清理、无用标签移除封装成独立预处理函数,可以极大减少后续解析环节的各种诡异bug。
以上就是python对html进行预处理的全流程的详细内容,更多关于python对html预处理的资料请关注代码网其它相关文章!
发表评论