基于Scrapy和BERT的新闻数据爬取与智能分析实践

发布时间:2026/10/31 11:50:31
基于Scrapy和BERT的新闻数据爬取与智能分析实践

1. 项目背景与目标

去年冬天,我突发奇想:能否用技术手段分析新闻网站的报道规律?于是选择了中新网作为研究对象,计划爬取10万条新闻数据,用机器学习进行分类,并尝试构建一个爆款标题生成器。这个项目涉及网络爬虫、自然语言处理和深度学习等多个技术领域。

2. 数据采集方案设计

2.1 爬虫架构设计

采用Scrapy框架构建分布式爬虫系统,主要包含以下组件:

  • 调度器:使用Redis实现分布式任务队列
  • 下载器:配置自动重试和代理中间件
  • 解析器:基于XPath和CSS选择器提取结构化数据
class ChinaNewsSpider(scrapy.Spider): name = 'china_news' allowed_domains = ['chinanews.com'] def start_requests(self): urls = [f'http://www.chinanews.com/scroll-news/{date}/news.shtml' for date in date_range('20200101', '20231231')] for url in urls: yield scrapy.Request(url=url, callback=self.parse_list)

2.2 反爬策略应对

中新网的反爬机制主要包括:

  1. 频率限制:单个IP请求间隔需大于2秒
  2. User-Agent验证:需要轮换常见浏览器UA
  3. Cookie验证:首次访问需要获取初始Cookie

解决方案:

  • 使用RotatingProxyMiddleware实现IP轮换
  • 配置DOWNLOAD_DELAY=3秒
  • 启用AutoThrottle扩展自动调整请求频率

重要提示:严格遵守robots.txt规则,设置合理的爬取间隔,避免对服务器造成过大压力。

3. 数据预处理流程

3.1 原始数据清洗

采集到的原始数据存在以下问题:

  • 正文中夹杂记者信息、免责声明等噪声
  • 部分字段缺失(如作者、来源)
  • HTML标签未完全清除

清洗步骤:

  1. 使用BeautifulSoup清理HTML标签
  2. 正则表达式提取纯文本内容
  3. 基于规则过滤非正文内容
def clean_content(html): soup = BeautifulSoup(html, 'lxml') # 移除特定标签 for tag in ['script', 'style', 'footer']: [s.extract() for s in soup(tag)] # 提取正文 text = '\n'.join(p.get_text() for p in soup.find_all('p')) return re.sub(r'\s+', ' ', text).strip()

3.2 特征工程构建

构建的特征矩阵包含:

  1. 文本特征:
    • 词频统计(TF-IDF)
    • 主题分布(LDA)
    • 情感极性(TextBlob)
  2. 元数据特征:
    • 发布时间(小时、星期)
    • 新闻字数
    • 标题长度
  3. 结构特征:
    • 段落数量
    • 图片数量
    • 引用数量

4. 新闻分类模型实现

4.1 类别体系设计

通过分析数据,建立三级分类体系:

一级分类(8类): 时政、经济、社会、国际、文化、体育、科技、军事 二级分类(32类): 如经济→金融、产业、贸易等 三级分类(128类): 如金融→银行、证券、保险等

4.2 模型选型对比

测试了多种分类算法效果:

模型准确率训练时间内存占用
Logistic Regression82.3%15min2GB
Random Forest85.7%45min8GB
XGBoost86.2%30min6GB
BERT89.5%4h16GB

最终选择BERT+BiLSTM的混合模型,在保证精度的同时控制推理时间在200ms以内。

4.3 模型优化技巧

  1. 类别不平衡处理:
    • 过采样少数类
    • 使用Focal Loss
  2. 文本长度优化:
    • 动态padding
    • 截断策略组合(头+尾)
  3. 迁移学习:
    • 基于Chinese-BERT预训练
    • 领域自适应训练
class NewsClassifier(nn.Module): def __init__(self, bert_path, num_classes): super().__init__() self.bert = BertModel.from_pretrained(bert_path) self.lstm = nn.LSTM(768, 256, bidirectional=True) self.classifier = nn.Linear(512, num_classes) def forward(self, input_ids, attention_mask): outputs = self.bert(input_ids, attention_mask) lstm_out, _ = self.lstm(outputs.last_hidden_state) logits = self.classifier(lstm_out[:, -1, :]) return logits

5. 标题生成器开发

5.1 爆款标题特征分析

统计发现高点击量标题具有以下特点:

  1. 长度:15-25字(手机屏幕最佳显示)
  2. 情感:积极或负面情感(中立标题点击率低)
  3. 结构:
    • 悬念式("竟然...")
    • 数字式("3个关键...")
    • 疑问式("为何...?")

5.2 生成模型架构

采用GPT-2+Copy机制的双通道模型:

  1. 编码器:BERT提取新闻正文特征
  2. 解码器:GPT-2生成文本
  3. 复制机制:允许直接从原文复制关键词

训练参数:

  • Batch size: 16
  • Learning rate: 3e-5
  • Max length: 512 (正文), 30 (标题)
  • Beam size: 5

5.3 生成效果优化

  1. 重复惩罚(repetition_penalty=1.5)
  2. 温度调节(temperature=0.7)
  3. 关键词引导:
    def generate_title(text, keywords): inputs = tokenizer(text, return_tensors='pt') keyword_ids = [tokenizer.encode(k)[1:-1] for k in keywords] outputs = model.generate( **inputs, max_length=30, bad_words_ids=[[x] for x in banned_words], force_words_ids=keyword_ids ) return tokenizer.decode(outputs[0], skip_special_tokens=True)

6. 实际应用中的发现

6.1 意料之外的结果

  1. 时效性规律:
    • 工作日上午10点发布的新闻平均点击量高23%
    • 周末发布的科技类新闻分享率更高
  2. 标题特征:
    • 含"重磅""独家"等词的标题打开率下降(用户审美疲劳)
    • 使用"?"的标题CTR提升但分享率降低

6.2 模型局限性

  1. 领域迁移问题:
    • 在娱乐新闻上准确率下降15%
    • 对新兴领域(如元宇宙)识别不准
  2. 生成标题的多样性不足:
    • 容易陷入固定模板
    • 长尾关键词覆盖不够

7. 工程实践建议

7.1 爬虫注意事项

  1. 遵守道德规范:
    • 设置合理的爬取间隔(>3秒)
    • 避开服务器高峰时段
  2. 数据存储优化:
    • 原始HTML与解析数据分离存储
    • 建立增量更新机制

7.2 模型部署经验

  1. 性能优化:
    • 使用ONNX Runtime加速推理
    • 实现动态批处理
  2. 监控指标:
    • 分类准确率日报
    • 标题生成多样性指数
# 模型转换示例 python -m onnxruntime.tools.convert_onnx_models -i bert_model -o onnx_model

这个项目让我深刻认识到:数据质量决定模型上限,而业务理解决定价值下限。在实际应用中,我们最终将分类准确率提升到91.2%,标题生成器的CTR比人工标题高15%。但更宝贵的是发现了许多人工难以察觉的内容传播规律,这些insight对内容运营具有重要参考价值。