在做网页爬虫、**文档解析(PDF/Word)或者清洗用户生成内容(UGC)**时,我们经常面临一个尴尬的问题:拿到了一堆文本,怎么知道哪句是标题,哪句是正文?
比如这段数据:
“2023年全球AI市场规模达到1000亿美元。根据最新报告,增长主要来自生成式AI。”
哪一句是标题?人类一眼就能看出来,但机器怎么判断?今天我们就从规则匹配到深度学习,层层递进,带你搞定这个问题。
方案一:基于启发式规则(Heuristics)—— 简单粗暴,无需训练
对于格式比较规范的文本(如新闻、Markdown、简单的爬虫数据),我们可以利用标题的统计学特征来写规则。
标题的常见特征:
- 长度较短:通常在 10-30 个字符之间。
- 首字母大写:英文标题通常首词大写(Title Case)。
- 结尾无标点:很多标题结尾没有句号(。或.),或者只有感叹号/问号。
- 包含数字/年份:如 “2023年…”、“Top 10…”。
- 词汇特征:包含 “揭秘”、“指南”、“报告”、“分析” 等高频标题词。
代码实现
importredefis_title_heuristic(text,threshold=30):""" 基于启发式规则判断是否为标题 """# 1. 长度检测:太长肯定不是标题,太短可能是废话iflen(text)>thresholdorlen(text)<4:returnFalse# 2. 结尾标点检测:如果以句号、逗号结尾,大概率是正文iftext.endswith(('。','.',',','.',';',';')):returnFalse# 3. 数字/年份特征:包含年份或列表数字(如 1. 2. 3.)ifre.search(r'\d{4}年|第[\d一二三四五六七八九十]+章|Top \d+',text):returnTrue# 4. 英文 Title Case 检测 (简单版)# 检查首字母是否大写,且长度大于1iftext[0].isupper()andlen(text)>1:# 简单的概率判断:如果大写字母占比过高(全大写标题除外),可能是标题upper_ratio=sum(1forcintextifc.isupper())/len(text)if0.2<upper_ratio<0.8:returnTrue# 5. 关键词匹配title_keywords=['报告','指南','揭秘','分析','研究','新闻','Review','Guide','Analysis']ifany(keywordintextforkeywordintitle_keywords):returnTruereturnFalse# 测试test_cases=["2023年中国经济发展报告",# True"这是一个普通的句子。",# False"How to Learn Python in 30 Days",# True"今天天气不错",# False (太短且无特征)"揭秘:DeepSeek 的核心技术"# True]fortintest_cases:print(f"{t:30}->{'是标题'ifis_title_heuristic(t)else'是正文'}")优点:速度极快,无需数据,逻辑可解释。
缺点:误报率高(比如英文句子首字母大写会被误判),对口语化标题无效。
方案二:基于传统机器学习(TF-IDF + 分类器)—— 中等精度
如果我们有一批已经标注好的数据(哪些是标题,哪些是正文),就可以用机器学习来找规律。标题和正文的词频分布是不同的:
- 标题:名词、动词多,虚词少,信息密度大。
- 正文:连词、介词、代词多,句子结构完整。
我们可以用TF-IDF提取特征,用逻辑回归(Logistic Regression)或SVM分类。
代码实现 (使用 scikit-learn)
fromsklearn.feature_extraction.textimportTfidfVectorizerfromsklearn.linear_modelimportLogisticRegressionfromsklearn.model_selectionimporttrain_test_splitfromsklearn.metricsimportaccuracy_scoreimportjoblib# 1. 模拟数据集 (实际应用中需要几千条真实数据)data=[("深度学习入门教程",1),("根据最新的统计数据显示",0),("2024年投资策略分析",1),("他昨天去了公园,玩得很开心。",0),("Python 编程最佳实践",1),("这是一段用于测试的正文内容。",0),("如何优雅地删除 Emoji",1),("在自然语言处理中,数据清洗非常重要。",0),]texts=[d[0]fordindata]labels=[d[1]fordindata]# 1=标题, 0=正文# 2. 特征工程:TF-IDF (注意:中文需要先分词,这里为了演示用 char level)vectorizer=TfidfVectorizer(analyzer='char',ngram_range=(2,3))# 使用字符级N-gram捕捉结构X=vectorizer.fit_transform(texts)# 3. 训练模型X_train,X_test,y_train,y_test=train_test_split(X,labels,test_size=0.2,random_state=42)clf=LogisticRegression()clf.fit(X_train,y_train)# 4. 预测defpredict_is_title(text):vec=vectorizer.transform([text])returnclf.predict(vec)[0]==1# 测试new_text="2024年宏观经济展望"print(f"'{new_text}' 是标题吗?{predict_is_title(new_text)}")new_text_2="我们需要进一步观察市场的反应。"print(f"'{new_text_2}' 是标题吗?{predict_is_title(new_text_2)}")优点:比纯规则准确,能学习到隐含模式。
缺点:需要标注数据,特征工程(尤其是中文分词)比较麻烦。
方案三:基于深度学习(BERT/Transformers)—— 工业级方案
如果你追求最高准确率,或者需要理解语义(比如区分“这是一个标题”这句话本身和真正的标题),必须上预训练模型。
我们可以使用 Hugging Face 的transformers库,加载一个中文文本分类模型(或者自己微调一个)。
核心思路
将问题转化为二分类任务(Binary Classification)。输入文本,输出[标题, 正文]的概率。
代码实现 (使用 Transformers Pipeline)
首先安装库:
pipinstalltransformers torch使用现成的情感分析模型改造成“标题检测”比较麻烦,最好是 fine-tune 一个。但如果只是做 Demo,我们可以用零样本分类(Zero-shot classification)或者直接用一个通用的文本匹配模型。
这里展示一个更实用的思路:利用句子向量相似度。
fromsentence_transformersimportSentenceTransformer,util# 加载预训练模型 (中文)model=SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')# 定义一些“典型标题”的模板title_templates=["2023年行业研究报告","Python 入门指南","如何高效学习","深度学习技术分析","新闻早知道"]# 计算模板的嵌入向量template_embeddings=model.encode(title_templates)defis_title_bert(text,threshold=0.6):""" 通过计算文本与标题模板的语义相似度来判断 """text_embedding=model.encode(text)# 计算与所有模板的最大相似度max_score=0fortpl_embintemplate_embeddings:score=util.cos_sim(text_embedding,tpl_emb).item()ifscore>max_score:max_score=scorereturnmax_score>threshold# 测试print(is_title_bert("2024年AI发展趋势"))# True (高相似度)print(is_title_bert("今天中午吃了面条"))# False (低相似度)print(is_title_bert("Python 编程教程"))# True进阶玩法:如果你有数据,可以使用 BERT 微调。
- 准备正负样本各 5000+ 条。
- 使用
bert-base-chinese。 - 在最后一层加一个 Linear Layer 做二分类。
- 训练 2-3 个 Epoch,准确率通常能达到 95%+。
优点:准确率极高,能理解语义,不需要复杂的特征工程。
缺点:计算资源消耗大(需要 GPU 训练,CPU 推理也较慢),模型体积大。
方案四:基于上下文结构(HTML/Markdown 特有)
如果你处理的是网页或 Markdown 文件,不要只看文本内容,要看标签!这是最准确的方法。
1. HTML 解析 (BeautifulSoup)
frombs4importBeautifulSoup html=""" <h1>这是主标题</h1> <p>这是正文段落。</p> <h2>这是副标题</h2> <div class="content">这里也是正文</div> """soup=BeautifulSoup(html,'html.parser')fortaginsoup.find_all(['h1','h2','h3','h4','h5','h6']):print(f"发现标题:{tag.get_text()}(层级:{tag.name})")2. Markdown 解析
检查行首是否有#、##等符号,或者是否有下划线===。
importredefis_markdown_title(line):# 匹配 # 标题ifre.match(r'^#{1,6}\s+',line):returnTrue# 匹配 Setext 风格标题 (下划线)ifre.match(r'^=+$|^--+$',line):returnTruereturnFalse总结与选型建议
| 场景 | 推荐方案 | 准确度 | 性能 | 难度 |
|---|---|---|---|---|
| 简单爬虫/日志清洗 | 方案一:启发式规则 | ⭐⭐⭐ | 极快 | ⭐ |
| 聊天记录/短文本分类 | 方案二:TF-IDF + LR | ⭐⭐⭐⭐ | 快 | ⭐⭐ |
| 新闻/文章/专业文档 | 方案三:BERT/Deep Learning | ⭐⭐⭐⭐⭐ | 慢 | ⭐⭐⭐⭐ |
| 网页/Markdown文件 | 方案四:标签解析 | ⭐⭐⭐⭐⭐ | 快 | ⭐⭐ |
我的建议:
- 先看来源:如果是 HTML,直接用 BeautifulSoup 抓
<h1>-<h6>,别用 NLP 模型杀鸡用牛刀。 - 混合使用:先用规则过滤掉明显的正文(如以句号结尾、长度超过 50),再用轻量级模型(如 FastText 或 Logistic Regression)对疑似标题进行二次确认。
- 不要迷信 AI:对于“今天天气真好”这种短句,AI 也很难判断它是标题还是正文,必须结合上下文(比如它是不是独立成行、字体是否加粗)。
希望这篇文章能帮你在 Python 中精准识别标题!如果你有更好的特征工程技巧,欢迎留言讨论。
博客标签:Python, NLP, 文本分类, 标题识别, 机器学习