beautifulsoup 直接解析正文会排版混乱,因其无语义识别能力,仅语法解析,保留广告、导航栏等噪音节点,导致断行错乱、段落粘连;且依赖固定选择器或正则,网站改版即失效。

为什么直接用 BeautifulSoup 解析正文会排版混乱
因为网页 HTML 结构里混着大量广告、导航栏、侧边栏、JS 注入块、冗余 和空段落,BeautifulSoup 按 DOM 树原样提取时,这些噪音节点全被保留,导致文本断行错乱、段落粘连、标题和正文穿插。
更麻烦的是:不同网站的模板结构千差万别,靠写死 soup.select('.content') 或正则匹配 <p>.*?</p> 极不可靠——今天能用,明天改版就失效。
- 常见现象:一段文字被拆成 5 行,每行末尾带
\n+ 多个\xa0;标题和正文挤在同一行;图片 caption 跟正文混在一起;评论区内容误入正文 - 根本原因:没有语义识别能力,只做语法解析
- 性能影响:手动清洗需反复调试 CSS 选择器或正则,维护成本高;对动态渲染页(如 React SSR)基本无效
用 readability-lxml 提取干净正文的实操步骤
它复现了 Firefox 的 Readability 算法,通过文本密度、标签权重、节点嵌套深度等特征自动识别「主内容区」,输出结构清晰的 HTML 或纯文本,排版自然恢复段落与标题层级。
注意:不是所有叫 readability 的包都靠谱。推荐用 readability-lxml(非已停更的 readability),安装命令:
pip install readability-lxml
基础用法:
- 输入必须是
bytes(即response.content),不能传response.text—— 否则编码错位会导致解析失败 - 解析后调用
.summary()得 HTML 片段,.content是原始lxml.html.HtmlElement对象,可继续用.xpath()或.text_content() - 若页面含大量 JS 渲染内容,先用
requests-html或playwright渲染再喂给readability-lxml
示例代码:
import requests from readability import Document <p>url = "<a href="https://www.php.cn/link/0a70b4e9e53d81c726eb245123e2b384">https://www.php.cn/link/0a70b4e9e53d81c726eb245123e2b384</a>" response = requests.get(url) doc = Document(response.content) # ← 必须用 .content clean_html = doc.summary() # ← 返回精简后的 HTML 字符串 print(clean_html)</p>
readability-lxml 的局限与绕过技巧
它不是万能的:遇到极简 HTML(如无 <p></p> 只有 <div> 堆砌)、表格型新闻(如财经数据页)、多栏 PDF 转 HTML 页面,准确率会明显下降。<ul>
<li>当 <code>.summary() 返回空或过短时,先检查 doc.title() 是否能正确提取标题——如果标题都错了,说明 DOM 解析本身已失败(常见于 gzip 压缩未解或 charset 错误)
min_text_length 参数:Document(content, min_text_length=25)(默认是 25,可降至 15 保召回)response.content.replace(b'\xa0', b' '),避免不间断空格干扰文本密度计算<img>),此时需在 .summary() 输出后用 BeautifulSoup 二次修补比 readability-lxml 更轻量的替代方案:用 trafilatura
如果你只需要纯文本且讨厌 HTML 清洗,trafilatura 是更现代的选择:内置支持 XML/Atom/RSS、自动去广告、可选保留或丢弃列表/表格/引用块,而且对中文支持更稳(readability-lxml 在部分 GBK 页面会崩)。
安装与基础用法:
pip install trafilatura # 一行提取纯文本 from trafilatura import extract text = extract(response.content, no_fallback=False)
- 关键参数:
include_comments=False(默认为True,论坛页容易误抓评论);include_tables=False(表格常破坏阅读流) - 它内部也调用
lxml,但做了更多中文适配,比如对<p>你好<br>世界</p>会合并为“你好世界”,而 readability 可能切两行 - 性能略低(启动加载模型),但单次解析耗时差异可忽略;适合批量处理场景
真正容易被忽略的点:无论用哪个库,**永远先确认 response.content 的实际编码是否与 <meta charset> 或 Content-Type header 一致**——否则算法再强,喂进去的就是一团乱码字节流。











