python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python判断文本是否为标题

从正则到 BERT详解Python如何判断文本是否为标题

作者:detayun

在做文档解析(PDF/Word)或者清洗用户生成内容(UGC)时,我们经常面临一个尴尬的问题,怎么知道哪句是标题,哪句是正文,本文将从规则匹配到深度学习,层层递进,带你搞定这个问题

在做文档解析(PDF/Word)或者清洗用户生成内容(UGC)时,我们经常面临一个尴尬的问题:拿到了一堆文本,怎么知道哪句是标题,哪句是正文?

比如这段数据:

“2023年全球AI市场规模达到1000亿美元。根据最新报告,增长主要来自生成式AI。”

哪一句是标题?人类一眼就能看出来,但机器怎么判断?今天我们就从规则匹配深度学习,层层递进,带你搞定这个问题。

方案一:基于启发式规则(Heuristics)—— 简单粗暴,无需训练

对于格式比较规范的文本(如新闻、Markdown、简单的爬虫数据),我们可以利用标题的统计学特征来写规则。

标题的常见特征

  1. 长度较短:通常在 10-30 个字符之间。
  2. 首字母大写:英文标题通常首词大写(Title Case)。
  3. 结尾无标点:很多标题结尾没有句号(。或.),或者只有感叹号/问号。
  4. 包含数字/年份:如 “2023年…”、“Top 10…”。
  5. 词汇特征:包含 “揭秘”、“指南”、“报告”、“分析” 等高频标题词。

代码实现

import re

def is_title_heuristic(text, threshold=30):
    """
    基于启发式规则判断是否为标题
    """
    # 1. 长度检测:太长肯定不是标题,太短可能是废话
    if len(text) > threshold or len(text) < 4:
        return False
    
    # 2. 结尾标点检测:如果以句号、逗号结尾,大概率是正文
    if text.endswith(('。', '.', ',', '.', ';', ';')):
        return False
    
    # 3. 数字/年份特征:包含年份或列表数字(如 1. 2. 3.)
    if re.search(r'\d{4}年|第[\d一二三四五六七八九十]+章|Top \d+', text):
        return True
    
    # 4. 英文 Title Case 检测 (简单版)
    # 检查首字母是否大写,且长度大于1
    if text[0].isupper() and len(text) > 1:
        # 简单的概率判断:如果大写字母占比过高(全大写标题除外),可能是标题
        upper_ratio = sum(1 for c in text if c.isupper()) / len(text)
        if 0.2 < upper_ratio < 0.8: 
             return True
            
    # 5. 关键词匹配
    title_keywords = ['报告', '指南', '揭秘', '分析', '研究', '新闻', 'Review', 'Guide', 'Analysis']
    if any(keyword in text for keyword in title_keywords):
        return True

    return False

# 测试
test_cases = [
    "2023年中国经济发展报告",      # True
    "这是一个普通的句子。",          # False
    "How to Learn Python in 30 Days", # True
    "今天天气不错",                  # False (太短且无特征)
    "揭秘:DeepSeek 的核心技术"       # True
]

for t in test_cases:
    print(f"{t:30} -> {'是标题' if is_title_heuristic(t) else '是正文'}")

优点:速度极快,无需数据,逻辑可解释。

缺点:误报率高(比如英文句子首字母大写会被误判),对口语化标题无效。

方案二:基于传统机器学习(TF-IDF + 分类器)—— 中等精度

如果我们有一批已经标注好的数据(哪些是标题,哪些是正文),就可以用机器学习来找规律。标题和正文的词频分布是不同的:

我们可以用 TF-IDF 提取特征,用 逻辑回归(Logistic Regression)SVM 分类。

代码实现 (使用 scikit-learn)

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
import joblib

# 1. 模拟数据集 (实际应用中需要几千条真实数据)
data = [
    ("深度学习入门教程", 1),
    ("根据最新的统计数据显示", 0),
    ("2024年投资策略分析", 1),
    ("他昨天去了公园,玩得很开心。", 0),
    ("Python 编程最佳实践", 1),
    ("这是一段用于测试的正文内容。", 0),
    ("如何优雅地删除 Emoji", 1),
    ("在自然语言处理中,数据清洗非常重要。", 0),
]

texts = [d[0] for d in data]
labels = [d[1] for d in data] # 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. 预测
def predict_is_title(text):
    vec = vectorizer.transform([text])
    return clf.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)

首先安装库:

pip install transformers torch

使用现成的情感分析模型改造成“标题检测”比较麻烦,最好是 fine-tune 一个。但如果只是做 Demo,我们可以用零样本分类(Zero-shot classification)或者直接用一个通用的文本匹配模型。

这里展示一个更实用的思路:利用句子向量相似度

from sentence_transformers import SentenceTransformer, util

# 加载预训练模型 (中文)
model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')

# 定义一些“典型标题”的模板
title_templates = [
    "2023年行业研究报告",
    "Python 入门指南",
    "如何高效学习",
    "深度学习技术分析",
    "新闻早知道"
]

# 计算模板的嵌入向量
template_embeddings = model.encode(title_templates)

def is_title_bert(text, threshold=0.6):
    """
    通过计算文本与标题模板的语义相似度来判断
    """
    text_embedding = model.encode(text)
    
    # 计算与所有模板的最大相似度
    max_score = 0
    for tpl_emb in template_embeddings:
        score = util.cos_sim(text_embedding, tpl_emb).item()
        if score > max_score:
            max_score = score
            
    return max_score > threshold

# 测试
print(is_title_bert("2024年AI发展趋势"))  # True (高相似度)
print(is_title_bert("今天中午吃了面条"))  # False (低相似度)
print(is_title_bert("Python 编程教程"))   # True

进阶玩法:如果你有数据,可以使用 BERT 微调。

  1. 准备正负样本各 5000+ 条。
  2. 使用 bert-base-chinese
  3. 在最后一层加一个 Linear Layer 做二分类。
  4. 训练 2-3 个 Epoch,准确率通常能达到 95%+。

优点:准确率极高,能理解语义,不需要复杂的特征工程。

缺点:计算资源消耗大(需要 GPU 训练,CPU 推理也较慢),模型体积大。

方案四:基于上下文结构(HTML/Markdown 特有)

如果你处理的是网页或 Markdown 文件,不要只看文本内容,要看标签!这是最准确的方法。

1. HTML 解析 (BeautifulSoup)

from bs4 import BeautifulSoup

html = """
<h1>这是主标题</h1>
<p>这是正文段落。</p>
<h2>这是副标题</h2>
<div class="content">这里也是正文</div>
"""

soup = BeautifulSoup(html, 'html.parser')

for tag in soup.find_all(['h1', 'h2', 'h3', 'h4', 'h5', 'h6']):
    print(f"发现标题: {tag.get_text()} (层级: {tag.name})")

2. Markdown 解析

检查行首是否有 ### 等符号,或者是否有下划线 ===

import re

def is_markdown_title(line):
    # 匹配 # 标题
    if re.match(r'^#{1,6}\s+', line):
        return True
    # 匹配 Setext 风格标题 (下划线)
    if re.match(r'^=+$|^--+$', line):
        return True
    return False

总结与选型建议

场景推荐方案准确度性能难度
简单爬虫/日志清洗方案一:启发式规则⭐⭐⭐极快
聊天记录/短文本分类方案二:TF-IDF + LR⭐⭐⭐⭐⭐⭐
新闻/文章/专业文档方案三:BERT/Deep Learning⭐⭐⭐⭐⭐⭐⭐⭐⭐
网页/Markdown文件方案四:标签解析⭐⭐⭐⭐⭐⭐⭐

我的建议

  1. 先看来源:如果是 HTML,直接用 BeautifulSoup 抓 <h1>-<h6>,别用 NLP 模型杀鸡用牛刀。
  2. 混合使用:先用规则过滤掉明显的正文(如以句号结尾、长度超过 50),再用轻量级模型(如 FastText 或 Logistic Regression)对疑似标题进行二次确认。
  3. 不要迷信 AI:对于“今天天气真好”这种短句,AI 也很难判断它是标题还是正文,必须结合上下文(比如它是不是独立成行、字体是否加粗)。

到此这篇关于从正则到 BERT详解Python如何判断文本是否为标题的文章就介绍到这了,更多相关Python判断文本是否为标题内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文