news 2026/10/29 16:09:37

python实现精准搜索并提取网页核心内容

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
python实现精准搜索并提取网页核心内容

前言


标题里的「精准」二字需要先打个折扣。网页正文提取(content extraction)从原理上就是启发式的,不可能 100% 准确。原因很简单:HTML 只是排版语言,它没有字段告诉机器「这段是正文、那段是广告」。一个div可以是文章主体,也可以是侧边栏的推荐位,语法上完全一样。所有号称「精准」的方案,本质都是靠一组特征去猜,猜得比其他方案准而已。


理解了这一点,后面的技术选择就顺了:我们要的不是一个「绝对正确」的算法,而是一个特征抓得准、阈值可调、失败时能降级的启发式流程。


本文讲三类主流特征——语义标签、文本长度、链接密度——以及怎么把它们组合成一个可调参的提取器。需要说明:本机没有 Python 解释器,示例代码为逐行推演,未经运行。「以官方文档为准」的地方会明确标注。


另外提一句版本:Python 2.7 已于 2020 年 1 月 1 日停止维护。Python 2 里str是字节串、unicode才是文本,抓回来的 HTML 在老代码里常是字节串,字符相关的判断(比如标点计数)会完全不一样。本文一律按 Python 3 写:str就是文本。


一、正文提取为什么必然不完美


先给一个反例,感受一下问题有多难。假设页面结构是:


<div id="main">
<div class="article-body">
<p>第一段正文……</p>
<p>第二段正文……</p>
</div>
<div class="comment">
<p>用户评论一……</p>
<p>用户评论二……</p>
</div>
</div>

如果只按「文本长度最大的块」,评论多的时候,评论区的文本长度会超过正文;如果只按语义标签,老站点根本没有article标签。没有单一特征能通吃,这就是为什么成熟的提取库(如可读性算法一类)都采用多特征加权。


因此本文的目标定成:用一个打分函数把候选节点排序,取分最高的当正文,并允许调参。分数给得对不对,决定了这套流程在实际站点上的表现。


二、候选块从哪来


提取的第一步不是选正文,而是生成候选块。常见做法是遍历所有块级元素,把它们当作候选。


# 适用于 Python 3.8+
BLOCK_TAGS = {"article", "section", "div", "main", "td", "body"}

def collect_candidates(root):
"""收集块级元素作为候选正文容器。"""
out = []
# iter() 递归遍历整棵树,tag 为字符串的是普通元素
for el in root.iter():
if not isinstance(el.tag, str):
continue # 跳过注释、处理指令等
if el.tag in BLOCK_TAGS:
out.append(el)
return out

这里用的是标准库xml.etree.ElementTree的接口习惯(iter()、tag)。若换成lxml.etree,iter()同样可用。先收集所有候选,再统一打分,比边遍历边判断要好调,也方便事后打印分数排查问题。


三、四类特征与打分


3.1 特征一:语义标签


现代站点常给正文容器加语义标签或约定俗成的 id / class。命中就是强信号:




特征信号强度说明



article标签强HTML5 语义标签,直接表达「独立成篇的内容」

main标签中表达「页面主体」,但可能包含正文之外的区块

id / class 含content、article、post中命名约定,不同站点写法不一

id / class 含nav、sidebar、comment、footer负命中的候选直接扣分



语义信号的好处是准,坏处是覆盖率低——老站点可能一个都不命中。所以它只能当加分项,不能当唯一判据。


3.2 特征二:文本长度与标点密度


正文的典型特征:文本多,而且标点密。导航链接虽然也占地方,但通常没有句号、逗号、问号。可以用标点数量做一个粗略的语言性判断:


# 适用于 Python 3.8+
import re

PUNCT = re.compile(r"[,。!?;:、,.!?;:]")

def punctuation_score(text):
"""标点越多,越像自然语言正文;纯链接列表的标点极少。"""
return len(PUNCT.findall(text))

findall返回匹配到的所有子串组成的列表,取len即为标点个数。这个特征对中文尤其有效,因为中文正文的标点密度很高。


3.3 特征三:链接密度(关键)


链接密度 = 块内链接文本长度 / 块内总文本长度。正文里链接很少,导航栏和列表页几乎全是链接。这条特征往往是区分正文与导航的决定性因素。


# 适用于 Python 3.8+
def link_density(el):
"""计算元素内的链接密度,取值 0.0 ~ 1.0。"""
total = text_length(el)
if total == 0:
return 0.0
link_len = sum(text_length(a) for a in el.iter("a"))
return link_len / total

def text_length(el):
"""该元素及其后代的可见文本总长度(粗略:把后代文本接起来)。"""
return len("".join(el.itertext()))

itertext()会按文档顺序产出该元素下所有文本节点的内容,把它拼接起来就是元素的纯文本。注意这是粗略的可见文本——它会把script、style里的内容也算进去,生产代码需要先剔除。


3.4 特征四:段落数量


正文由多个段落组成。统计块内p标签的数量,是个简单但有效的正信号:


# 适用于 Python 3.8+
def paragraph_score(el):
return sum(1 for _ in el.iter("p"))

四、加权打分与选择


把上面的特征加权求和,取最高分。权重是需要按站点调的经验值,不存在普适的最优解:


# 适用于 Python 3.8+
NEGATIVE_HINTS = ("nav", "sidebar", "comment", "footer", "ad-", "banner")
POSITIVE_HINTS = ("content", "article", "post", "main", "detail")

def score(el):
text = "".join(el.itertext())
s = 0.0
s += punctuation_score(text) * 1.0 # 标点越多越像正文
s += paragraph_score(el) * 25.0 # 段落是强正信号
s -= link_density(el) * len(text) * 1.5 # 链接密度折算成扣分
ident = " ".join(filter(None, [el.get("id", ""), el.get("class", "")])).lower()
if any(h in ident for h in NEGATIVE_HINTS):
s -= 1000.0 # 命中负面关键词,重扣
if any(h in ident for h in POSITIVE_HINTS):
s += 200.0
return s

def extract_main(root):
candidates = collect_candidates(root)
if not candidates:
return None
return max(candidates, key=score)

el.get("id", "")取属性,第二个参数是默认值(属性不存在时返回它)。max(..., key=score)按分数取最大的候选。注意:这个打分里的权重(1.0、25.0、1.5)是示意值,不是经过实测标定的数字。真实项目里应当拿一批页面人工标注,再回归调参,而不是照抄。


一个额外的健壮性设计:如果最高分明显低于一个阈值,说明整页可能都不是文章(比如是列表页),此时应当返回「提取失败」而不是硬返回一个错的块:


# 适用于 Python 3.8+
def extract_main_or_none(root, min_score=100.0):
best = extract_main(root)
if best is None or score(best) < min_score:
return None
return best

降级路径是正文提取系统的必备品:宁可报告「没提到正文」,也不要输出一段看起来像正文的导航。下游如果拿到垃圾还以为是正文,错误会一路传下去。


常见坑点



  1. 以为存在 100% 精准的算法


❌ 「找一个库,调一次参数,所有站点都能提到正文。」


✅ 承认这是启发式问题:为不同站点准备不同的权重配置,并保留人工兜底。



  1. 只按文本长度选最大块


❌max(candidates, key=lambda e: len("".join(e.itertext())))—— 评论、相关阅读多时必然选错。


✅ 用「长度 + 标点 + 链接密度 + 段落」的组合分,长度只占一项。



  1. 忘了把导航和评论区扣分


❌ 只加正分,侧边栏因为堆了很多文字反而胜出。


✅ 让 id / class 命中负面关键词的候选直接重扣,把导航排除在竞争之外。



  1. 用链接密度时没排除空文本


❌link_len / total在total == 0时抛ZeroDivisionError。


✅ 先判断total == 0返回0.0。



  1. 把script和style的文本也算进长度


❌itertext()会把内联脚本代码计入,导致含大量 JS 的块被误判为正文。


✅ 计算前先删除script/style子树。



  1. 提取失败时硬返回一个块


❌return max(...)无条件执行,列表页也照样返回一个div。


✅ 设最低分阈值,低于阈值返回None,让上层知道「这次没提出来」。



  1. 忽略字符编码导致正文乱码


❌ 用错误的编码解析响应,正文里全是问号或方块,标点检测随之失效。


✅ 按响应头声明的编码解析,缺失时再探测;解析层编码错了,后面所有特征都不准。


总结




特征作用局限



语义标签(article/main)命中即强信号老站点覆盖率低

标点密度判断「像不像自然语言」对短句、列表文案不敏感

链接密度区分正文与导航的利器正文内链接多时会误伤

段落数量简单有效的正信号长段落单段正文会吃亏

负面关键词排除导航、评论、广告依赖命名约定



正文提取的正确心态是「工程」而不是「定理」。先承认它必然不完美,然后用多特征打分把准确率抬到一个可接受的水平,再用最低分阈值给自己留一条失败的退路。把这三件事做好,比追求一个不存在的「完美算法」实在得多。




版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/29 16:08:55

ponytail插件化架构解析:轻量灵活的工程实践指南

1. 从“ponytail”这个词说起&#xff1a;它到底指什么第一次看到“ponytail”这个词&#xff0c;绝大多数人脑子里蹦出来的画面是发型——马尾辫。没错&#xff0c;字面意思确实如此。但如果你是在技术社区、插件市场或者开发者的聊天记录里反复撞见这个词&#xff0c;那它大概…

作者头像 李华
网站建设 2026/10/29 16:08:51

智能交通系统中的深度学习流量预测实战

1. 项目背景与核心价值交通流量预测是智能交通系统&#xff08;ITS&#xff09;的核心模块&#xff0c;直接影响着城市道路规划、信号灯控制和出行路线推荐。传统基于统计模型&#xff08;如ARIMA&#xff09;的方法在应对突发流量变化时表现乏力&#xff0c;这正是深度学习技术…

作者头像 李华
网站建设 2026/10/29 16:08:48

DDR子系统板级调试:初始化、训练与误码排查全链路

1. 先把“内存条”这层壳掀掉&#xff1a;DDR子系统在板级到底由什么拼成写完第一篇之后&#xff0c;收到最多的一类问题几乎都长一个样&#xff1a;速率我算得出来&#xff0c;CL 和 tRCD 我也知道是什么&#xff0c;可板子一上电就是起不来&#xff0c;要么训练直接不过&…

作者头像 李华
网站建设 2026/10/29 16:07:53

Nginx反向代理下Druid监控静态资源404问题解决方案

1. 问题现象与背景分析那天我正在将若依框架部署到生产环境&#xff0c;Nginx作为反向代理服务器配置完成后&#xff0c;发现了一个奇怪的问题&#xff1a;Druid监控页面的CSS和JS文件全部报404错误&#xff0c;而其他静态资源却能正常加载。这直接导致Druid监控页面样式错乱&a…

作者头像 李华
网站建设 2026/10/29 16:07:39

把“无可挑剔”变成方法:功能、审美与体验的三层打磨

我第一次认真掂量“impeccable”这个词&#xff0c;是在一次产品交付前的内部评审会上。团队打磨了三个月的方案&#xff0c;被一位资深前辈看了十分钟&#xff0c;她只说了一句&#xff1a;“整体思路没问题&#xff0c;但几个细节还不够 impeccable。”没人觉得被冒犯&#x…

作者头像 李华
网站建设 2026/10/29 16:06:23

企业级AI治理:从数据合规到模型落地的全流程实践

1. 项目背景与核心价值在AI技术应用日益普及的今天&#xff0c;许多企业和个人开发者都面临着"如何规范使用AI工具"的挑战。我最近参与了一个企业级AI治理项目&#xff0c;深刻体会到&#xff1a;在部署AI解决方案前&#xff0c;系统化的准备工作往往决定了最终的实施…

作者头像 李华