第二章:金融文本数据获取实战

做量化交易的朋友都知道,数据是策略的命根子。但金融数据这东西,结构化数据好拿,非结构化的文本数据就有点麻烦了。今天我就带大家走一遍,怎么把A股的新闻数据、财经网站的公告信息,实实在在地抓下来。

嗯,先别急着写代码。咱们得先搞清楚,到底要拿什么数据。

2.1 数据源的选择与对比

我个人习惯把数据源分成三类:专业金融接口、财经网站、以及公开API。这三类各有各的脾气。

数据源类型 代表工具 优点 缺点
专业金融接口 Tushare, AkShare 数据规范、字段完整 部分需要积分/权限
财经网站 东方财富、新浪财经 实时性强、内容丰富 反爬严格、结构不固定
公开API 新浪财经API、腾讯财经API 免费、调用简单 接口不稳定、文档不全

你想想看,如果只是做回测验证,Tushare和AkShare就够用了。但要是做实时舆情监控,那还得靠爬虫。

核心观点:不要迷信单一数据源。我在项目中吃过亏,某次策略回测表现很好,实盘却崩了。后来发现是数据源在某个时间段有缺失。所以,至少准备两个数据源做交叉验证。

2.2 使用Tushare获取新闻数据

Tushare这个库,做量化的应该都不陌生。它提供了比较干净的金融数据接口。获取新闻数据,主要用 news 这个接口。

import tushare as ts

# 初始化,需要你的token
pro = ts.pro_api('你的token')

# 获取最近100条新闻
df = pro.news(src='sina', start_date='2024-01-01', end_date='2024-01-31')
print(df.head())

这里有个坑,我一开始也踩过。Tushare的新闻数据,默认只返回最近几条。如果你要历史数据,必须指定 start_dateend_date。而且,src 参数可以指定来源,比如 sinaeastmoney 等。

小技巧:我建议把获取到的数据直接存到本地CSV或者数据库里。别每次都重新拉,Tushare有调用次数限制的。我曾经一天拉了5000次,直接被封了24小时。

2.3 使用AkShare获取财经新闻

AkShare是另一个好用的库,它更像一个数据聚合器。获取新闻的方式更直接。

import akshare as ak

# 获取东方财富的新闻
df = ak.news_eastmoney(symbol="SH600519", date="2024-01-31")
print(df.columns)
print(df.head())

AkShare的好处是,它把很多网站的接口封装好了。你不需要自己去分析网页结构。但坏处是,如果网站改版,AkShare的接口可能会失效。我遇到过两次,都是等作者更新版本才恢复。

注意:AkShare的接口名经常变。比如 news_eastmoney 可能某天就改成 eastmoney_news 了。建议每次使用前,先看看官方文档。

2.4 使用Requests爬取东方财富

有时候,现成的接口满足不了需求。比如我想抓取某只股票的所有公告,那就得自己写爬虫了。

东方财富的公告页面,其实是一个动态加载的页面。直接请求HTML是拿不到数据的。得分析它的XHR请求。

import requests
import json

url = "https://np-anotice-stock.eastmoney.com/api/security/ann"
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
    "Referer": "https://data.eastmoney.com/",
}

params = {
    "sr": -1,
    "page_size": 50,
    "page_index": 1,
    "ann_type": "A",
    "stock_list": "600519",
    "f_node": 0,
    "s_node": 0,
}

resp = requests.get(url, headers=headers, params=params)
data = resp.json()
print(data['data']['list'][0]['title'])

为什么会这样?因为东方财富的前端是Vue写的,数据都是通过API异步加载的。直接爬HTML只能拿到一个空壳子。

我记得有一次,我为了抓一个特定格式的公告,花了整整一下午分析接口参数。最后发现是 ann_type 这个参数没传对。嗯,这种坑踩多了,就长记性了。

2.5 使用Scrapy爬取新浪财经

如果数据量比较大,比如要爬取几千只股票的历史新闻,那Requests就有点吃力了。这时候我推荐用Scrapy。

Scrapy是一个异步爬虫框架,速度比Requests快很多。而且它自带数据管道,可以很方便地把数据存到数据库。

import scrapy

class SinaNewsSpider(scrapy.Spider):
    name = 'sina_news'
    start_urls = ['https://finance.sina.com.cn/stock/']
    
    def parse(self, response):
        # 提取新闻列表
        for news in response.css('.news-item'):
            yield {
                'title': news.css('a::text').get(),
                'url': news.css('a::attr(href)').get(),
                'time': news.css('.time::text').get(),
            }
        
        # 翻页
        next_page = response.css('.next::attr(href)').get()
        if next_page:
            yield scrapy.Request(next_page, callback=self.parse)

Scrapy的学习曲线稍微陡峭一点,但一旦上手,效率提升很明显。我现在的生产环境里,大部分爬虫都是用Scrapy写的。

避坑指南:我曾经写过一个爬虫,没加下载延迟,结果把新浪财经的服务器搞崩了。后来对方直接封了我的IP。所以,爬虫一定要加 DOWNLOAD_DELAY 设置,建议至少1秒。

2.6 API调用与数据清洗基础

数据拿到手了,但还不能直接用。金融文本数据通常很脏,需要清洗。

常见的清洗步骤包括:

  • 去重:同一篇新闻可能被多个源转载
  • 去噪:去掉广告、无关字符、HTML标签
  • 标准化:统一日期格式、编码格式
  • 字段提取:从文本中提取股票代码、公司名称等关键信息
import re
import pandas as pd

def clean_news_text(text):
    # 去掉HTML标签
    text = re.sub(r'<.*?>', '', text)
    # 去掉特殊字符
    text = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9,。!?、]', '', text)
    # 去掉多余空格
    text = re.sub(r'\s+', '', text)
    return text

# 示例
df = pd.read_csv('news_data.csv')
df['clean_content'] = df['content'].apply(clean_news_text)
print(df[['title', 'clean_content']].head())

数据清洗这一步,看似简单,其实最花时间。我有个项目,80%的时间都花在清洗数据上。但这一步做不好,后面的NLP模型再牛也没用。

我的习惯:清洗完的数据,我会做一个简单的质量检查。比如统计一下空值比例、文本长度分布。如果发现异常,就回溯到爬虫阶段看看是不是哪里出了问题。

2.7 本章知识体系

下面这张图,是我自己总结的数据获取流程。你可以照着这个思路来搭建自己的数据管道。

金融文本数据获取流程 Tushare / AkShare Requests / Scrapy 公开API 数据清洗:去重 → 去噪 → 标准化 → 字段提取 本地存储 (CSV / 数据库) NLP分析 (后续章节) 建议:数据源至少准备两个,清洗步骤不可跳过

这张图把整个流程串起来了。从数据源到清洗,再到存储和分析,每一步都有讲究。我个人建议,刚开始做的时候,先用Tushare或AkShare跑通流程,再考虑自己写爬虫。

好了,数据获取这块就聊到这儿。数据拿到手,清洗干净,下一步就是做NLP分析了。不过那是后面章节的事,咱们先把基础打牢。


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