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)}条')
核心思路:SimHash把文本转成64位指纹,通过比较汉明距离判断相似度。速度快,适合大规模去重。我在处理百万级新闻数据时,就用这个方案,效果不错。

2.3.2 清洗策略:把脏数据「洗白」

清洗是个细致活。我总结了一套「三步走」策略:

  1. 格式清洗:去除HTML标签、特殊字符、多余空格
  2. 内容过滤:剔除广告、导航栏、版权声明等非正文内容
  3. 质量筛选:过滤过短文本、乱码文本、纯标点文本
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 本章知识体系

下面这张图,是我梳理的数据采集与清洗全流程。你可以把它当作操作手册来用。

新闻文本数据采集与清洗流程 数据采集 1. API调用 - NewsAPI / 聚合数据 - 请求参数设置 - 频率控制 2. 网页爬虫 - Requests + BeautifulSoup - Selenium(动态页面) 数据清洗 1. 格式清洗 - 去除HTML标签 - 去除特殊字符 2. 内容过滤 - 剔除广告/导航 - 过滤乱码文本 数据去重 1. 精确去重 - 字符串完全匹配 2. 近似去重 - SimHash / MinHash - 相似度阈值控制 高质量新闻数据集

这张图把整个流程串起来了。从API或爬虫拿到原始数据,经过清洗和去重,最终得到干净可用的新闻文本。我在实际项目中,通常会在清洗环节多花些时间——数据干净了,后面的分析才能站得住脚。

好了,这一章的内容就到这里。数据采集和清洗虽然枯燥,但绝对是值得投入的环节。记住一句话:垃圾进,垃圾出。把基础打牢,后面的路就好走了。

专注资料整理