3. 数据采集基础:新闻API介绍与实战抓取

做新闻情绪分析,第一步就是搞到数据。说白了,你得先有新闻,才能分析它的情绪。

我个人习惯用API来抓取新闻数据。为什么?因为省事。你想想看,手动去各大新闻网站爬页面,又要处理反爬,又要解析HTML,太折腾了。API直接给你结构化的数据,多香。

这一章,我们就来聊聊两个主流的新闻API——NewsAPI和GDELT,以及怎么用Python把它们的数据拿到手。

3.1 两大新闻API:NewsAPI vs GDELT

我在项目中用过不少新闻数据源,但最常用的就这两个。它们各有千秋,我分别说说。

3.1.1 NewsAPI

NewsAPI是一个聚合新闻API,覆盖了全球上千家新闻源。它的特点是:

  • 数据新鲜:主要提供最近30天内的新闻
  • 结构清晰:返回的JSON格式非常规整
  • 有免费额度:开发者账号每天100次请求,够你练手了
  • 支持关键词搜索:可以按主题、来源、语言等过滤

重要提醒:NewsAPI的免费版不支持历史新闻搜索,只能查最近30天的。如果你需要历史数据,得考虑GDELT或者付费方案。

3.1.2 GDELT

GDELT(Global Database of Events, Language, and Tone)是个庞然大物。它监控全球100多种语言的新闻媒体,数据量巨大。

它的特点:

  • 历史数据丰富:从1979年至今的新闻都有
  • 自带情绪分析:每条新闻都有情绪评分(Tone)
  • 更新频繁:每15分钟更新一次
  • 免费开放:完全免费,没有请求次数限制

我的经验:如果你做的是短期事件分析(比如某公司最近一周的舆情),用NewsAPI就够了。但如果你要研究长期趋势(比如某行业过去十年的情绪变化),GDELT是更好的选择。

3.2 用requests库抓取新闻数据

好,理论说完了,咱们直接上代码。Python的requests库是抓取API数据的标配工具。

3.2.1 安装与导入

pip install requests
import requests
import json
from datetime import datetime, timedelta

3.2.2 抓取NewsAPI数据

首先,你需要去 newsapi.org 注册一个账号,拿到API Key。免费的。

# 配置你的API Key
API_KEY = "你的API_KEY"

# 构建请求参数
url = "https://newsapi.org/v2/everything"
params = {
    "q": "人工智能",           # 搜索关键词
    "language": "zh",         # 中文新闻
    "sortBy": "publishedAt",  # 按发布时间排序
    "pageSize": 20,           # 每页20条
    "from": (datetime.now() - timedelta(days=7)).strftime("%Y-%m-%d")
}

# 发送请求
headers = {"X-Api-Key": API_KEY}
response = requests.get(url, params=params, headers=headers)

# 检查状态码
if response.status_code == 200:
    data = response.json()
    print(f"成功获取 {data['totalResults']} 条新闻")
else:
    print(f"请求失败,状态码:{response.status_code}")

避坑指南:我曾经犯过一个低级错误——把API Key直接写在代码里,然后上传到了GitHub。结果第二天就收到邮件说我的Key被滥用,被冻结了。嗯,从那以后我都是用环境变量来管理API Key。

3.2.3 抓取GDELT数据

GDELT的API稍微有点不一样。它用的是GKG(Global Knowledge Graph)接口,返回的是CSV格式的数据。

# GDELT GKG API
url = "https://api.gdeltproject.org/api/v2/doc/doc"

params = {
    "query": "人工智能",       # 搜索关键词
    "mode": "artlist",        # 返回文章列表
    "format": "json",         # 返回JSON格式
    "maxrecords": 20,         # 最多20条
    "timespan": "7d"          # 最近7天
}

response = requests.get(url, params=params)

if response.status_code == 200:
    data = response.json()
    articles = data.get("articles", [])
    print(f"成功获取 {len(articles)} 条新闻")
else:
    print(f"请求失败,状态码:{response.status_code}")

小技巧:GDELT的查询语法很灵活。你可以用sourcecountry:CN来限定中国来源,用tone>5来筛选正面情绪较高的新闻。具体语法可以参考GDELT官方文档。

3.3 数据格式解析:JSON

抓回来的数据,大部分是JSON格式。你得学会怎么解析它。

3.3.1 JSON结构长什么样?

NewsAPI返回的JSON结构大概是这样的:

{
  "status": "ok",
  "totalResults": 100,
  "articles": [
    {
      "source": {
        "id": "techcrunch",
        "name": "TechCrunch"
      },
      "author": "John Doe",
      "title": "人工智能的最新突破",
      "description": "这是一篇关于AI突破的文章...",
      "url": "https://...",
      "publishedAt": "2024-01-15T08:00:00Z",
      "content": "全文内容..."
    },
    // ...更多文章
  ]
}

3.3.2 解析JSON数据

拿到JSON后,怎么提取我们需要的信息?看代码:

def parse_newsapi_response(data):
    """解析NewsAPI返回的JSON数据"""
    articles = []
    
    for item in data.get("articles", []):
        article = {
            "title": item.get("title", ""),
            "description": item.get("description", ""),
            "content": item.get("content", ""),
            "source": item.get("source", {}).get("name", ""),
            "author": item.get("author", ""),
            "published_at": item.get("publishedAt", ""),
            "url": item.get("url", "")
        }
        articles.append(article)
    
    return articles

# 使用示例
parsed_data = parse_newsapi_response(data)
for article in parsed_data[:3]:  # 打印前3条
    print(f"标题:{article['title']}")
    print(f"来源:{article['source']}")
    print(f"时间:{article['published_at']}")
    print("-" * 50)

注意:GDELT返回的JSON结构略有不同。它的articles里每个字段名是titleurlseendate等。而且GDELT多了一个tone字段,直接给出了情绪评分。这个我们后面做情绪分析时会重点用到。

3.4 本章知识体系

为了让你更直观地理解这一章的内容,我画了一张流程图:

新闻数据采集流程 新闻数据源 NewsAPI GDELT requests库 JSON解析 结构化数据 数据源选择 NewsAPI:近30天 GDELT:1979年至今 API调用 GET请求 参数配置 输出 标题、内容、来源、时间、情绪分

3.5 完整示例:抓取并保存新闻

最后,我给你一个完整的示例。把抓取和解析整合到一起,并保存到本地文件:

import requests
import json
from datetime import datetime, timedelta

def fetch_news(api_key, query="人工智能", days=7, max_results=20):
    """抓取新闻并返回结构化数据"""
    
    url = "https://newsapi.org/v2/everything"
    params = {
        "q": query,
        "language": "zh",
        "sortBy": "publishedAt",
        "pageSize": max_results,
        "from": (datetime.now() - timedelta(days=days)).strftime("%Y-%m-%d")
    }
    
    headers = {"X-Api-Key": api_key}
    response = requests.get(url, params=params, headers=headers)
    
    if response.status_code != 200:
        print(f"请求失败:{response.status_code}")
        return []
    
    data = response.json()
    articles = []
    
    for item in data.get("articles", []):
        article = {
            "title": item.get("title", ""),
            "description": item.get("description", ""),
            "content": item.get("content", ""),
            "source": item.get("source", {}).get("name", ""),
            "published_at": item.get("publishedAt", ""),
            "url": item.get("url", "")
        }
        articles.append(article)
    
    return articles

# 使用示例
API_KEY = "你的API_KEY"
news_data = fetch_news(API_KEY, query="人工智能", days=7, max_results=10)

# 保存到文件
with open("news_data.json", "w", encoding="utf-8") as f:
    json.dump(news_data, f, ensure_ascii=False, indent=2)

print(f"成功保存 {len(news_data)} 条新闻到 news_data.json")

我的建议:刚开始做的时候,别贪多。先抓个10条20条数据,把整个流程跑通。确认数据格式没问题了,再慢慢扩大规模。我曾经一上来就抓了5000条,结果发现数据里有大量重复和无关内容,处理起来特别痛苦。

好了,这一章的内容就到这里。数据采集是基础中的基础,但也是最重要的一步。数据质量决定了你后续分析的上限。下一章,我们会聊聊怎么清洗和预处理这些新闻数据——嗯,那才是真正考验耐心的时候。


公众号:蓝海资料掘金营,微信deep3321