3、数据采集与接入:数据源类型(行情、财务、另类)、实时采集(Kafka)与批量采集(Sqoop/DataX)、数据质量初检

数据采集,说白了就是「把数据从源头搬进仓库」的过程。这一步要是没做好,后面所有分析都是空中楼阁。我在因子工厂项目里踩过最大的坑,就是数据源没搞清楚就开始建表,结果返工了整整两周。

今天咱们就聊聊数据采集的三个核心问题:数据从哪来、怎么搬、搬完怎么验

3.1 数据源类型:行情、财务、另类

因子工厂的数据源,我习惯分成三大类。你想想看,量化分析需要的信息,基本都跑不出这个圈。

数据源类型 典型数据 更新频率 数据量级
行情数据 股票Tick、K线、期货逐笔 毫秒~秒级 TB级/天
财务数据 季报、年报、业绩预告 季度/月度 GB级
另类数据 舆情、卫星图、电商销量 分钟~天级 TB级

行情数据

这是因子工厂的「主食」。我记得刚接手项目时,行情数据一天就有200多GB。Tick级别的数据,每秒都在产生。这类数据的特点是:量大、实时性要求高、结构相对固定。比如上交所的Level-2行情,每3秒推送一次快照,包含买卖十档、逐笔成交等。

核心要点:行情数据一定要做「时间分区」+「标的物分区」。我见过有人把所有股票塞进一张表,查询时慢到怀疑人生。

财务数据

财务数据虽然量不大,但坑特别多。我曾经被「会计政策变更」坑过一次——某公司突然改了折旧方法,导致前后数据不可比。财务数据的特点:结构化强、更新频率低、但质量参差不齐

嗯,这里要注意:财务数据往往有「修正值」。比如某公司发布一季报后,过两个月又发了个更正公告。你采集时如果只取第一次,后面分析就会出错。

另类数据

另类数据是因子工厂的「调味料」。比如爬取电商平台的销量数据、新闻舆情的情感分数、甚至卫星拍摄的停车场车辆数。这类数据的特点:格式多样、质量不可控、采集成本高

我个人建议:另类数据先做小规模验证,确认有因子价值后再全量采集。否则很容易「采集一时爽,清洗火葬场」。

3.2 实时采集(Kafka)与批量采集(Sqoop/DataX)

数据源搞清楚了,接下来就是怎么搬。这里分两条路:实时流批量批

我的经验:不要为了「实时」而实时。行情数据必须实时,财务数据用批量就够了。我曾经见过有人把财务数据也用Kafka实时采集,结果每天就几条记录,纯属浪费资源。

实时采集:Kafka

Kafka在因子工厂里,主要扛行情数据。架构大概是这样的:

行情网关 → Kafka Producer → Kafka Cluster → Flink/Spark Streaming → 数据仓库

为什么选Kafka?说白了,它就是个「超级消息队列」。行情数据每秒几万条,传统MQ根本扛不住。Kafka的吞吐量能到百万级/秒,而且数据持久化,不怕丢。

我记得有一次交易所行情延迟,Kafka里积压了2小时的数据。要是用别的中间件,早就OOM了。Kafka靠着磁盘顺序读写,硬是扛了下来。

配置上,我建议注意几个点:

  • 分区数:按标的物哈希,保证同一只股票的数据有序
  • 副本因子:至少2,防止节点挂了丢数据
  • 消息大小:行情数据一般不超过1MB,默认配置够用

批量采集:Sqoop vs DataX

批量采集主要对付财务数据和历史行情。我常用的两个工具:Sqoop和DataX。

对比项 Sqoop DataX
数据源 关系型数据库 → Hadoop 多种数据源互导
性能 MapReduce模式,适合大表 多线程模式,适合中规模
易用性 命令行,配置简单 JSON配置,灵活度高
我的选择 财务数据(MySQL → Hive) 历史行情(CSV → HDFS)

举个例子,用Sqoop拉取财务数据:

sqoop import \
  --connect jdbc:mysql://192.168.1.100:3306/finance \
  --username root \
  --password **** \
  --table financial_report \
  --where "report_date >= '2024-01-01'" \
  --target-dir /user/hive/warehouse/finance \
  --fields-terminated-by '\001' \
  --num-mappers 4

这里有个坑:--num-mappers不要设太大。我曾经设了16个并发,直接把源库的IO打满了,导致业务系统卡顿。后来学乖了,财务数据一般4~8个并发就够了。

DataX的配置更灵活,适合异构数据源。比如从CSV文件导入HDFS:

{
  "job": {
    "content": [{
      "reader": {
        "name": "txtfilereader",
        "parameter": {
          "path": ["/data/history/2024/*.csv"],
          "encoding": "UTF-8",
          "column": ["date", "code", "open", "high", "low", "close", "volume"]
        }
      },
      "writer": {
        "name": "hdfswriter",
        "parameter": {
          "path": "/user/hive/warehouse/history",
          "fileName": "history_2024",
          "writeMode": "append"
        }
      }
    }]
  }
}

3.3 数据质量初检

数据搬进来了,不代表就能直接用。我吃过最大的亏就是:数据采集时没做质量检查,结果下游因子计算全用了脏数据

数据质量初检,我总结为「三板斧」:

  1. 完整性检查:有没有缺失值?比如某只股票某天没有行情数据,是停牌了还是采集漏了?
  2. 一致性检查:字段格式对不对?比如日期字段,有的源是"2024-01-01",有的是"20240101",必须统一。
  3. 合理性检查:数值在不在合理范围?比如股票价格不可能为负数,成交量不可能为0(除非停牌)。

避坑指南:我曾经遇到过行情数据里出现「价格=999999」的异常值,原因是交易所的测试数据没过滤干净。如果没做合理性检查,这个异常值会直接污染所有因子。

我习惯在采集管道里加一个「质量检查层」。比如用Flink做实时检查:

// 伪代码示例
DataStream<Tick> tickStream = env.addSource(kafkaConsumer);

tickStream
  .filter(tick -> tick.getPrice() > 0)  // 价格必须大于0
  .filter(tick -> tick.getVolume() >= 0) // 成交量不能为负
  .filter(tick -> tick.getTimestamp() > 0) // 时间戳合法
  .addSink(hbaseSink);

对于批量数据,我建议在Sqoop/DataX导入后,跑一个简单的SQL做校验:

-- 检查财务数据完整性
SELECT 
  report_date,
  COUNT(*) AS total_records,
  SUM(CASE WHEN revenue IS NULL THEN 1 ELSE 0 END) AS null_revenue,
  SUM(CASE WHEN profit IS NULL THEN 1 ELSE 0 END) AS null_profit
FROM financial_report
WHERE report_date = '2024-03-31'
GROUP BY report_date;

如果发现异常数据,我一般会:

  • 标记异常:在数据表里加一个quality_flag字段,标记为'BAD'
  • 记录日志:把异常数据写入「数据质量日志表」,方便追溯
  • 通知告警:如果异常比例超过阈值(比如1%),触发告警通知

我的习惯:数据质量初检不是一次性的。我会在每天凌晨跑一个「数据质量报告」,统计前一天所有数据源的质量指标。这样能尽早发现问题,而不是等到因子计算报错才去排查。

本章知识体系

下面这张图,概括了数据采集与接入的核心逻辑:

数据采集与接入知识体系 数据源层 行情数据(Tick/K线) 财务数据(季报/年报) 另类数据(舆情/卫星) 采集层 实时采集:Kafka 高吞吐、持久化、分区有序 批量采集:Sqoop / DataX 关系型/文件 → HDFS/Hive 数据质量初检层 完整性检查 一致性检查 合理性检查 数据仓库(Hive / HBase)

数据从源端出发,经过采集层(实时或批量),再到质量检查层,最终落入数据仓库。每一步都有坑,但每一步也都有成熟的解决方案。

好了,数据采集这块就聊到这儿。记住一句话:数据采集不是「搬砖」,而是「质检+搬运」的组合拳。搬得再快,质量不行也是白搭。


专注资料整理