2. 文本数据采集与清洗:新闻API调用、网页爬虫基础、数据去重与清洗策略
做新闻文本分析,第一步就是搞到数据。我见过不少同学,模型调得飞起,结果数据质量太差,最后全白干。说白了,数据采集和清洗才是真正的「地基工程」。这一章,咱们就聊聊怎么把新闻数据从网上「搬」下来,再把它收拾干净。
2.1 新闻API调用:最优雅的数据获取方式
我个人习惯,能调API就绝不爬网页。为什么?API是官方给的接口,数据结构清晰,还不用担心被封IP。你想想看,爬虫要处理反爬、解析HTML,多累啊。
2.1.1 主流新闻API一览
| API名称 | 特点 | 免费额度 | 适用场景 |
|---|---|---|---|
| NewsAPI | 覆盖全球,支持多语言 | 每天100次请求 | 国际新闻分析 |
| 聚合数据 | 国内新闻,中文友好 | 每月1000次 | 中文新闻挖掘 |
| 新浪新闻API | 实时性强,分类明确 | 有限免费 | 热点追踪 |
我在项目中遇到过,用NewsAPI抓英文新闻做情感分析。结果发现免费额度根本不够用。后来改用聚合数据,配合定时任务,每天凌晨跑一次,完美解决。
2.1.2 实战:用Python调用NewsAPI
先注册个API Key,然后直接上代码。嗯,这里要注意,请求频率别太快,不然会被限流。
import requests
import json
def fetch_news(api_key, query='technology', page_size=20):
url = 'https://newsapi.org/v2/everything'
params = {
'q': query,
'apiKey': api_key,
'pageSize': page_size,
'language': 'en',
'sortBy': 'publishedAt'
}
response = requests.get(url, params=params)
if response.status_code == 200:
data = response.json()
articles = data.get('articles', [])
print(f'成功获取 {len(articles)} 条新闻')
return articles
else:
print(f'请求失败,状态码:{response.status_code}')
return []
# 使用示例
api_key = 'your_api_key_here'
news_data = fetch_news(api_key, query='artificial intelligence')
content字段经常被截断。我一般用description字段做初步分析,或者直接爬取原文链接。
2.2 网页爬虫基础:当API不够用时
API虽好,但很多新闻网站不提供。这时候就得靠爬虫了。我曾经为了爬一个地方新闻站,折腾了整整两天——对方反爬做得太狠了。
2.2.1 爬虫三件套:Requests + BeautifulSoup + lxml
说白了,爬虫就三步:发请求、解析HTML、提取数据。我推荐用requests库发请求,BeautifulSoup配合lxml解析。速度快,上手也简单。
import requests
from bs4 import BeautifulSoup
import time
def crawl_news_list(url):
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
try:
response = requests.get(url, headers=headers, timeout=10)
response.encoding = 'utf-8'
soup = BeautifulSoup(response.text, 'lxml')
news_items = soup.select('div.news-item') # 根据实际页面调整选择器
for item in news_items:
title = item.select_one('h2 a').text.strip()
link = item.select_one('h2 a')['href']
summary = item.select_one('p.summary').text.strip()
print(f'标题:{title}')
print(f'链接:{link}')
print(f'摘要:{summary}')
print('---')
# 礼貌爬虫,别给人家服务器造成压力
time.sleep(1)
except Exception as e:
print(f'爬取失败:{e}')
# 使用示例
crawl_news_list('https://example-news-site.com')
time.sleep(),直接把一个小网站爬挂了。对方站长找到我,场面一度非常尴尬。记住:尊重robots.txt,控制爬取频率。
2.2.2 动态页面怎么办?
有些新闻网站用JavaScript渲染内容。这时候requests就抓瞎了。我一般用Selenium模拟浏览器,或者直接分析XHR请求。
from selenium import webdriver
from selenium.webdriver.common.by import By
import time
def crawl_dynamic_news(url):
driver = webdriver.Chrome() # 需要安装ChromeDriver
driver.get(url)
# 等待页面加载
time.sleep(3)
# 滚动加载更多
for _ in range(3):
driver.execute_script('window.scrollTo(0, document.body.scrollHeight);')
time.sleep(2)
# 提取数据
articles = driver.find_elements(By.CLASS_NAME, 'article-card')
for article in articles:
title = article.find_element(By.TAG_NAME, 'h3').text
print(title)
driver.quit()
2.3 数据去重与清洗策略
数据搞到手了,但你会发现——重复的、乱码的、广告夹杂的,什么妖魔鬼怪都有。我做过一个项目,采集了10万条新闻,去重后只剩6万条。你想想看,40%都是垃圾数据。
2.3.1 文本去重:不只是简单的字符串比较
简单的==比较肯定不行。两篇新闻可能标题不同,但内容几乎一样。我推荐用SimHash或者MinHash做近似去重。
from simhash import Simhash
def deduplicate_articles(articles, threshold=0.85):
"""
使用SimHash进行近似去重
threshold: 相似度阈值,超过则视为重复
"""
unique_articles = []
seen_hashes = []
for article in articles:
content = article.get('title', '') + article.get('content', '')
simhash = Simhash(content)
is_duplicate = False
for seen_hash in seen_hashes:
similarity = simhash.similarity(seen_hash)
if similarity > threshold:
is_duplicate = True
break
if not is_duplicate:
unique_articles.append(article)
seen_hashes.append(simhash)
return unique_articles
# 使用示例
cleaned_news = deduplicate_articles(raw_news)
print(f'去重前:{len(raw_news)}条,去重后:{len(cleaned_news)}条')
2.3.2 清洗策略:把脏数据「洗白」
清洗是个细致活。我总结了一套「三步走」策略:
- 格式清洗:去除HTML标签、特殊字符、多余空格
- 内容过滤:剔除广告、导航栏、版权声明等非正文内容
- 质量筛选:过滤过短文本、乱码文本、纯标点文本
import re
from bs4 import BeautifulSoup
def clean_news_text(raw_text):
# 第一步:去除HTML标签
soup = BeautifulSoup(raw_text, 'lxml')
text = soup.get_text()
# 第二步:去除特殊字符和多余空白
text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9,。!?、;:""''()\s]', '', text)
text = re.sub(r'\s+', ' ', text).strip()
# 第三步:过滤过短文本
if len(text) < 50:
return None
# 第四步:检查乱码(比如全是数字或符号)
chinese_chars = len(re.findall(r'[\u4e00-\u9fa5]', text))
if chinese_chars / len(text) < 0.3:
return None
return text
# 批量清洗
def batch_clean(news_list):
cleaned = []
for news in news_list:
clean_text = clean_news_text(news.get('content', ''))
if clean_text:
news['clean_content'] = clean_text
cleaned.append(news)
return cleaned
2.4 本章知识体系
下面这张图,是我梳理的数据采集与清洗全流程。你可以把它当作操作手册来用。
这张图把整个流程串起来了。从API或爬虫拿到原始数据,经过清洗和去重,最终得到干净可用的新闻文本。我在实际项目中,通常会在清洗环节多花些时间——数据干净了,后面的分析才能站得住脚。
好了,这一章的内容就到这里。数据采集和清洗虽然枯燥,但绝对是值得投入的环节。记住一句话:垃圾进,垃圾出。把基础打牢,后面的路就好走了。