2、金融文本数据源:获取财经新闻、社交媒体数据的API与爬虫方法

做量化投资的朋友都知道,情绪因子这东西,说白了就是「别人贪婪我恐惧,别人恐惧我贪婪」的数字化版本。但问题来了——你上哪儿去搞这些「别人」的言论数据?

我刚开始做情绪因子研究时,第一反应就是去爬财经新闻。结果发现,这事儿远没想象中简单。今天我就把这几年的踩坑经验,一次性倒给你们。

2.1 财经新闻数据源:从哪里抓?怎么抓?

财经新闻是情绪因子的「基本盘」。我个人习惯把新闻源分成三类:

  • 综合门户:新浪财经、东方财富、雪球——覆盖面广,但噪音大
  • 专业媒体:华尔街见闻、财新、Reuters——质量高,但可能有付费墙
  • 官方渠道:上交所/深交所公告、证监会发布——最权威,但格式死板

嗯,这里要注意:不同源头的新闻,情绪权重是不一样的。我在项目中遇到过,某次把微博大V的吐槽和交易所公告混在一起做情绪打分,结果模型直接崩了——因为公告里全是「中性」措辞,而微博全是「极端」情绪,两者方差太大。

2.1.1 用API获取新闻(推荐方式)

如果有条件,优先用API。稳定、合规、省心。常用的几个:

数据源 API名称 免费额度 特点
新浪财经 新浪财经API(非官方) 无限制(但限频) 中文新闻最全,但接口不稳定
东方财富 东方财富Choice API 有限免费 数据规范,适合量化
Bloomberg Bloomberg API 付费 全球最权威,但贵
NewsAPI NewsAPI.org 500次/天 英文新闻为主,中文覆盖一般

举个例子,用Python调新浪财经的非官方API,获取某只股票的新闻标题:

import requests
import json

def get_sina_news(stock_code, page=1):
    url = f"https://vip.stock.finance.sina.com.cn/corp/go.php/vCB_AllNewsStock/symbol/{stock_code}.phtml?page={page}"
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
    }
    resp = requests.get(url, headers=headers)
    # 这里需要解析HTML,实际项目中我一般用BeautifulSoup
    return resp.text

# 调用示例
news_html = get_sina_news('sh600519')  # 贵州茅台
print(news_html[:500])
⚠️ 避坑指南
我曾经因为没加User-Agent,被新浪封了IP整整三天。后来学乖了,每次请求都随机换一个User-Agent,再配合代理池。你想想看,人家服务器也不是傻子,你一秒发100个请求,不封你封谁?

2.1.2 爬虫方法(备用方案)

API拿不到的数据,就得靠爬虫。我建议用Scrapy框架,它比requests+BeautifulSoup的组合更抗造。为什么?因为Scrapy自带异步、去重、中间件,遇到反爬还能自动重试。

核心思路就三步:

  1. 找到目标页面:比如东方财富的个股新闻列表页
  2. 解析HTML:用XPath或CSS选择器提取标题、时间、正文
  3. 存储数据:存到CSV或数据库里,方便后续处理

给你看个简化版的爬虫代码,抓取东方财富的新闻标题:

import scrapy

class EastMoneySpider(scrapy.Spider):
    name = 'eastmoney'
    start_urls = ['http://finance.eastmoney.com/a/czqyw.html']

    def parse(self, response):
        # 提取新闻列表
        for news in response.css('div.list-item'):
            title = news.css('a::text').get()
            link = news.css('a::attr(href)').get()
            time = news.css('span.time::text').get()
            yield {
                'title': title.strip(),
                'link': link,
                'time': time
            }
💡 我的小技巧
爬新闻的时候,别只爬标题。正文里的「关键词密度」和「情感词分布」才是情绪因子的核心。我一般会把正文前200字单独存一列,因为新闻的「导语」通常包含了最强烈的情绪信号。

2.2 社交媒体数据:Twitter/微博的情绪金矿

财经新闻是「官方情绪」,社交媒体才是「民间情绪」。你想想看,一个散户在微博上骂「茅台今天又跌了,垃圾!」——这种情绪,新闻里是看不到的。

2.2.1 Twitter数据获取

Twitter有官方API,但2023年改版后,免费额度砍得很厉害。我个人建议用以下方案:

  • 学术API:如果你有学术邮箱,可以申请Twitter Academic API,免费且额度高
  • 第三方平台:比如SocialData、Brandwatch,付费但省心
  • 爬虫(不推荐):Twitter反爬极严,我试过用Selenium模拟登录,结果账号被封了5个

用Tweepy库调Twitter API的示例:

import tweepy

# 配置API密钥(实际项目中别硬编码)
bearer_token = "你的Bearer Token"

client = tweepy.Client(bearer_token=bearer_token)

# 搜索最近7天关于"AAPL"的推文
query = "AAPL lang:en -is:retweet"
tweets = client.search_recent_tweets(
    query=query,
    max_results=10,
    tweet_fields=['created_at', 'public_metrics']
)

for tweet in tweets.data:
    print(f"{tweet.created_at}: {tweet.text[:100]}")
🔑 关键点
Twitter数据的关键不是「量」,而是「质」。我建议只抓取粉丝数>1000的用户的推文,因为他们的情绪更有「传染性」。普通小号的噪音太大,反而会稀释情绪信号。

2.2.2 微博数据获取

微博的API比Twitter更坑。2018年之后,微博大幅收紧了开放接口。现在想拿微博数据,基本只有两条路:

  1. 微博开放平台:申请开发者权限,但审核极严,个人基本过不了
  2. 爬虫+模拟登录:用Selenium或Playwright模拟手机端登录,然后抓取

我目前在用的方案是Playwright模拟微博移动端(m.weibo.cn),因为移动端的反爬相对弱一些。给你看个核心片段:

from playwright.sync_api import sync_playwright

def fetch_weibo(keyword, max_pages=3):
    with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        page = browser.new_page()
        # 先登录(需要手动扫码一次,后续用cookie)
        page.goto('https://m.weibo.cn/search?type=60&q=' + keyword)
        page.wait_for_timeout(3000)
        
        for i in range(max_pages):
            # 提取推文内容
            cards = page.query_selector_all('div.card')
            for card in cards:
                text = card.query_selector('p.txt').inner_text()
                print(text)
            # 点击加载更多
            page.click('a.m-page-next')
            page.wait_for_timeout(2000)
        
        browser.close()
⚠️ 法律风险提示
爬取社交媒体数据,尤其是需要登录后才能看到的内容,存在法律风险。我曾经有个朋友,因为爬了某平台的用户私信数据,被起诉了。所以我的原则是:只爬公开数据,不碰用户隐私。做量化研究,合规永远是第一位的。

2.3 数据清洗与存储:别让脏数据毁了你的模型

数据拿到手,只是第一步。真正磨人的是清洗。我总结了一个「三筛法」:

  • 第一筛:去重——同一篇新闻被多个网站转载,只保留最早的那篇
  • 第二筛:去噪——去掉广告、营销软文、纯数据表格
  • 第三筛:对齐——把新闻时间统一成UTC+8,把股票代码统一成6位数字

存储方面,我推荐用MongoDB。为什么?因为新闻和推文都是非结构化数据,字段不固定。MongoDB的文档型存储,比MySQL灵活得多。当然,如果你只是做小规模实验,CSV也够用。

# 清洗示例:过滤掉长度小于50字的新闻
def clean_news(news_list):
    cleaned = []
    for news in news_list:
        if len(news['content']) < 50:
            continue  # 太短,可能是标题党
        if '广告' in news['title'] or '推广' in news['title']:
            continue
        # 统一时间格式
        news['time'] = pd.to_datetime(news['time']).tz_localize('Asia/Shanghai')
        cleaned.append(news)
    return cleaned

2.4 本章知识体系总览

说了这么多,我画了张图帮你理清思路。这张图展示了从数据源到情绪因子的完整链路:

金融文本数据源获取流程 财经新闻 社交媒体 官方公告 API / 爬虫 API / 模拟登录 官方API / PDF解析 数据清洗(三筛法) MongoDB / CSV 存储 → 后续用于情绪因子计算

这张图你看懂了吗?从数据源到最终存储,每一步都有坑。我当年在第一层「数据源」就卡了两个月——因为没搞清楚不同新闻源的更新频率差异,导致时间序列对不齐。后来我学乖了,每个数据源都单独建一个时间戳字段,再统一做对齐。

💡 最后说一句
数据源的选择,决定了你情绪因子的「天花板」。如果你只爬新浪财经,那你的情绪只能反映「中国散户」;如果你加上Twitter,就能看到「全球投资者」的情绪。我个人建议,至少覆盖2个新闻源+1个社交媒体源,这样因子才够稳健。

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