ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

数据获取与清洗实战:从接口到文件的全流程处理思路

数据获取与清洗实战:从接口到文件的全流程处理思路

做了这么多年数据相关工作,我有个特别深的感受:真正消耗时间的往往不是分析模型,也不是图表怎么做才好看,而是最不起眼的"数据处理与获取"环节。数据拿不到、格式不统一、字段对不上、值看起来不对劲,这些问题如果没在前期解决,后面所有分析结论都等于在沙滩上盖楼。这篇内容不打算讲高深算法,而是把日常数据工作中最常用的获取和整理思路、实操步骤、踩过的坑一次说清楚。适合运营、产品、市场、开发以及刚入门数据分析的同学参考,不管你是要整理一份Excel报表,还是要对接某个平台的开放接口,基本的处理思路都是相通的。我习惯把整个流程拆成四件事:数据从哪来、怎么洗、怎么加工、怎么验证。

1. 数据从哪里来:获取渠道与选型思路

1.1 先确定数据形态,再选获取方式

很多人一上来就问"用什么工具抓数据",这其实是把顺序搞反了。工具永远排在业务场景和数据结构之后。你需要的是一张汇总表、一批明细还是持续更新的实时指标,对应的获取方式完全不同。

从我接触过的实际场景看,数据来源大致能分成五种形态:

  • 接口数据:某个平台或业务系统对外开放的API,返回JSON或XML,适合需要实时更新、按条件查询的数据。
  • 数据库:公司内部的业务库、数据仓库,通常要写SQL,适合处理大规模的明细数据。
  • 文件数据:导出好的CSV、Excel、TXT日志,适合离线分析和一次性处理。
  • 网页内容:公开页面上展示的表格、列表信息,适合少量、结构简单的数据。
  • 手工记录:团队成员填写的表格、文档,这类数据往往最"脏",但也是现实中不可回避的来源。

选择获取方式时,我的建议是按优先级来。先看有没有正规接口或数据库权限,这是最稳定、口径也最清晰的渠道。其次才是文件导出。网页内容属于最后手段,因为页面结构一变,代码就得跟着改,维护成本相当高。还有一些外部公开数据,比如政务开放数据、行业机构发布的统计资料,适合做行业对标和背景补充。

这里有个容易被忽视的问题:渠道的可持续性。你写了一段脚本,每天自动从某个页面拿数据,觉得效率很高。但页面的HTML结构只要改一次,你的脚本就全废。接口反而通常有版本管理,三五个月不一定变。所以只要条件允许,我会优先走接口,并且把获取逻辑和后续的数据处理逻辑分开,页面变了只改获取层,不牵连下游。

1.2 接口调用中的关键参数与容错处理

调用接口是数据获取里最值得花时间研究的部分。别看就是个HTTP请求,实际跑下来你会发现,大部分问题都出在参数和容错上。

先看一个最基础的请求例子。假设要从某个开放平台拿订单明细:

import requests import time url = "https://api.example.com/v1/orders" params = { "page": 1, "page_size": 100, "sort": "created_at", "start_time": "2025-01-01 00:00:00" } headers = { "Authorization": "Bearer YOUR_TOKEN", "Content-Type": "application/json" } resp = requests.get(url, params=params, headers=headers, timeout=10) print(resp.status_code) print(resp.json())

这套代码虽然简单,至少踩过三个坑。

第一,认证方式。有些平台用固定密钥,有些用动态签名。密钥不能直接硬编码在脚本里,万一代码外传、密钥泄露,整个账号都可能被波及。我一般放在环境变量或者单独的配置文件里,并且定期轮换。

第二,分页策略。接口不会把所有数据一次性返回,常见分页方式有page/page_size、offset/limit、还有基于游标的cursor。用page翻页最直观,但如果数据在不断新增,你在翻页过程中可能漏掉新数据。这时候用时间游标更稳妥——每次按时间增量去拿,下一批从上一条记录的末尾时间开始。这个思路几乎适用于所有日志类数据源。

第三,限流和失败重试。接口方为了保护服务,会限制单位时间内的请求次数,超了直接返回429。你可以做两层防御:请求之间加短暂延时,避免触发限流;真被限流了,用退避策略重试,等几秒、几十秒再试,而不是立刻暴力重连。还有一个经验是,请求必须设置超时时间。不设置timeout的话,网络一抖动,请求可能挂半小时不返回,整个批处理就卡死了。

注意:拿到任何接口文档,先看"错误码"和"频率限制"两个章节。很多你以为的奇怪现象,其实文档里早就写明白了。

1.3 文件与网页类数据的常见细节

文件类的数据获取看起来门槛最低,但细节问题最多。比如CSV文件的编码,绝大多数场景中文要用UTF-8,可有些系统导出的是GBK或GB18030,直接读出来全是乱码。Excel文件则要小心sheet名、合并单元格、表格里藏着公式或小计行。我的习惯是,拿到任何文件先别急着全量处理,先用文本编辑器或命令行工具看一眼原始结构,确认编码、分隔符、表头是不是和预期一致。

日志文件是另一个高频场景。这类文件通常没有严格的列式结构,而是半结构化的文本,经常需要按行读取,再用正则表达式抽关键字段。比如nginx访问日志、服务运行日志,一行就是一条记录。逐行处理不会漏数据,但速度慢;如果日志量很大,可以先用grep过滤出需要的行,再进入后续程序。

网页内容获取我放在最后说。这里要明确一个原则:只处理公开且允许访问的页面,遵守目标站的robots协议和授权条款,不做突破访问限制的事。如果页面上有数据导出按钮,优先用导出功能;没有时才考虑写脚本解析HTML。用Python的解析库固然方便,但更简单的方式是先用浏览器开发者工具,找到数据到底在HTML里,还是通过异步请求加载的。很多"网页数据"实际上是隐藏在XHR请求里的JSON,这时候与其解析HTML,不如直接找那个请求地址,处理起来效率翻倍。

2. 数据清洗:先处理脏乱差,再谈分析

2.1 缺失值:先问"为什么缺",再决定怎么办

缺失值是数据清洗里最绕不开的问题。很多人一看到空值就想着填充,这其实是个危险动作。处理缺失值之前,必须先搞清楚一个更根本的问题:这些值到底是"没有"还是"不应该有"。

举个例子。一份订单表里,退款时间字段是空的。这大概率不是数据缺失,而是这个订单根本没退款,空值本身就是一种业务状态。这时候用平均值去填充,反而制造了错误信息。另一种情况是,用户在表单里没填手机号,那是真的缺失,需要按缺失处理。

在实操层面,我通常分三步走:

  1. 算缺失率:用代码统计每列空值数量和占比。数据量大时可以把阈值设到5%或10%,超过阈值的字段要重点讨论,而不是直接默认删除。
  2. 看缺失分布:空值到底是随机出现,还是集中在某个时间段、某个分类下。如果只在某一天大规模缺失,那可能是上游系统故障,这时候应该去修复数据源,而不是在清洗层面硬补。
  3. 选择处理策略:
    • 删除:缺失比例极低,或者该字段对当前分析目标没有影响。
    • 填充:用均值、中位数、众数填充,适合数值型且缺失机制近似随机的场景。
    • 前向填充:时间序列数据里,某个值一直保持到下一次变化,用前向填充很自然。
    • 算法插值:线性插值、样条插值,适合有上升或下降趋势的数据。

我有一个很土但很有效的办法:在处理缺失值之前,先把数据复制一份放到"原始区"。因为填充策略一旦错了,后面还可能回头重来。留一份原始数据,是给自己留一条退路。

2.2 重复值和异常值:别让一条脏数据带偏整体结果

重复数据比缺失值更隐蔽。完全重复的行好处理,难的是部分字段重复、大部分字段不同的情况。比如同一张订单在不同统计周期里被导出了多次,除了导出时间不一样其他都一样。这时候如果直接去重,你根本不知道该保留哪一个版本。

我的处理逻辑是先把"重复"的定义写清楚。什么字段组合能唯一标识一条记录?通常是一到三个业务主键的组合。没有主键,就要靠多个字段拼起来判断。比如设备记录用日期+设备编号+事件类型组合判断。确定唯一标识后,再决定保留规则:最新的一条、字段最完整的一条,或者干脆把重复记录合并成一条汇总。

异常值和重复值经常一起讨论,但本质不同。异常值不是"错",而是"超出合理范围"。识别异常值有两个思路。一个是业务经验法,比如订单金额正常在0到10000之间,突然出现一条-5000,那不用什么统计方法,直接在业务逻辑上就判死刑。另一个是统计方法,用四分位距,把小于第一四分位数减1.5倍IQR或大于第三四分位数加1.5倍IQR的值标记为潜在异常。

实操心得:异常值不要直接删。先标记、先排查原因,确认是录入错误、单位错误还是发生了真实的极端情况。做欺诈分析或风险分析时,异常值恰恰是最需要关注的对象。

2.3 类型、格式与编码:最容易被忽略的坑

我做过的数据清洗里,最难缠的往往不是空值,而是"看起来正常但一算就错"的数据。最典型的就是字段类型错乱。

场景一,数字列里混进了文本。比如金额列显示"1,200元",统计的时候它被当成字符串而不是数字。这时候必须先做格式清理:去掉货币符号、千分位逗号、单位后缀,再转成数值类型。用SQL或者数据处理工具,都要先做一次正则替换。

场景二,日期格式五花八门。同一个项目里,有的导出来是"2025/01/01",有的是"2025-01-01",还有的是"1月1日"。不统一成ISO标准格式,后面做时间排序、区间统计全都会出错。我的做法是进入分析流程前,把一切日期都统一成"YYYY-MM-DD HH:MM:SS"或时间戳,用格式参数识别再转换。

场景三,编码问题。中文乱码是最折磨人的。一般处理方法是:先确认源文件编码,读入时指定编码,输出时再次统一编码。千万别用"猜测编码"的方式赌运气,大概率会猜错。用支持编码检测的工具看原始字节,才能确定。

这里还有一个隐藏坑:隐性空格和不可见字符。很多从系统导出的字段,表面看着是"张三",实际字符里可能带了全角空格或不可见的换行符。用编辑器看不出来,但一统计就发现"张三"和"张三 "被分成了两个组。清洗阶段要顺手把所有常见不可见字符替换掉,我一般用统一的正则表来处理。

3. 数据加工与整合:把数据变成能直接用的样子

3.1 结构拆解与合并:从非结构化到结构化

数据清洗是"把脏数据弄干净",数据加工是"把干净的数据变成好用的结构"。这两步容易混在一起,但我的建议是分开处理。清洗阶段只解决值的问题,加工阶段解决结构的问题。

结构层面最常遇到的需求是把一个字段拆成多个字段。比如接口返回的原始数据是一个嵌套JSON,订单信息、用户信息、商品信息全挤在同一个字段里。这时候就需要把嵌套层级展开,拆成不同的表或列。拆之前先看清层级关系,别把一对多和多对一搞混。订单和订单明细就是一对多,明细不能直接塞进订单表里,否则一行变成多行,数量全都被放大了。

还有一种常见情况是地址拆分。原始数据只有一个完整地址字符串,分析时需要省、市、区三个维度。如果数据量不大,可以用规则匹配去拆;数据量大或者地址不规整,可能依赖地理编码服务。我的建议是,拆地址一定要保留原始字段,因为规则匹配总有失败的时候,原始字段留着你还能补救。

数据合并则是另一个高频操作。多表关联时,关联键的选取决定成败。同一个用户在A表里用手机号识别,在B表里用用户ID识别,如果你直接拿手机号和用户ID关联,大概率一张表都匹配不上。关联前先检查两边的键类型、格式、是否有重复。两个表关联后,行数急剧膨胀,通常就是关联键有重复,产生了笛卡尔积。排查方法是分别统计两个表的关联键重复次数,看是哪一边出了问题。

3.2 字段派生与指标计算:新增比原始更"好用"的列

加工阶段除了拆和并,还要做字段派生。所谓派生,就是基于已有字段计算新字段。这一步的作用是让后续分析更省事,很多业务指标可以提前算好。

最基础的是区间分桶,比如根据订单金额把订单分成"低客单、中客单、高客单",或者根据用户注册时长分成"新用户、老用户"。分桶规则要写在代码里,不要靠肉眼去手工标。否则数据更新一次,你就要手工改一次。

时间差计算也很常见。比如从下单时间到发货时间,算出一个"发货耗时"字段。这里要注意,直接用一个时间段减去另一个时间段,得到的是时间间隔对象,得转成小时或天数才能用。跨天的场景更要小心,比如晚上下单、凌晨发货,漏掉日期部分就会算出负数。

文本字段的清理也属于派生工作。英文统一大小写、中文去掉特殊符号、长文本做关键词抽取,这些操作看似机械,却在后续的分析和匹配里发挥作用。我自己在做用户反馈分析时,会先把评论文本全部做一次小写转换和标点规范化,否则后面一统计,光是"Good"和"good"就能分成两个类别。

3.3 数据量太大时的处理策略

数据量一旦上来,之前的很多"蛮力办法"就不管用了。比如直接用Excel打开上百万行的数据,卡到怀疑人生。这时候有几种策略,按我推荐的顺序排:

  • 采样验证:先用随机抽样跑通整个流程,确认逻辑正确,再处理全量。
  • 分批处理:按日期、按ID范围切分,一批一批跑,跑完合并。这个思路简单,但稳定。
  • 数据库预聚合:如果明细数据量上千万了,别把所有数据拉到本地,在数据库端先用SQL做汇总,只把聚合结果取回来。数据库干这事比本地内存快得多。
  • 并行处理:多进程或多线程分片处理。注意,并行不是所有场景都能提速,如果瓶颈在磁盘IO或接口限流,并行反而变慢。

我自己处理超过5000万行数据的时候,首选并不是换成多厉害的分布式框架,而是先问一句:我真的需要这么细颗粒度吗?很多时候,分析目标只需要天级别汇总。提前在加工阶段做聚合,后面所有流程都轻快起来。这算是一条非常实战的经验:能用查询引擎解决的,不用本地内存硬扛;能聚合的,不拖明细。

4. 数据质量验证与排查:数据不交底,结果不可信

4.1 数据体检:几项必查指标

很多同学处理完数据就开始建图表、跑模型,少了一个极其关键的环节:验证。我管这个叫"数据体检"。每次拿到处理完的数据,花几分钟跑一遍体检,能省掉后面几小时返工的痛苦。

体检清单我基本固定:

  • 行数与列数:是否在预期范围内。比如从100万条明细聚合后是30万条,结果发现只有3万条,那一定丢了某些分组。
  • 唯一值检查:唯一标识字段是否有重复。身份证、订单号这种字段原则上必须唯一,一旦重复说明去重或关联出了问题。
  • 缺失率复查:处理完不应该还有大片空值,尤其是关键字段。
  • 数值范围:金额、数量、年龄这些字段的最大最小值,跟业务常识对照。出现负数、超过合理上限,都是危险信号。
  • 数据分布抽查:直方图或分组后的Top10,看看是否存在数据倾斜,某个分组占了90%的占比,但这不符合业务预期时要去追查。

体检不是做完一次就结束。处理链条每经过一步转换,都可能引入新的问题。所以我会在关键节点设置检查点,把"体检"也做成流水线的一部分。比如从接口取完数据查一次,清洗完查一次,加工完再查一次。这样你能很快把问题定位到具体环节。

4.2 常见问题速查表

下面这组对应关系,是我在数据获取与处理过程中反复遇到的典型场景,整理成了一张速查表,建议大家收藏备用。

症状可能原因排查方法
行数剧烈膨胀关联键重复导致笛卡尔积,或一对多关系被错误展开分别检查两个表关联键的重复次数,做抽样关联验证
金额出现负数符号口径不一致,或源系统有退款/冲正记录对照业务场景,区分真实负值和数据符号错误
日期相差若干小时时区未统一,或存储的是UTC时间但展示用了本地时间检查读入时的时区参数,统一转为同一时区
中文字符乱码文件编码与读取编码不一致用编码检测工具确认源编码,重新指定编码读取
汇总结果与明细对不上筛选口径不一致,汇总时遗漏过滤条件对比汇总SQL和明细SQL的where条件,核对每一步过滤
去重后数据仍然重复主键定义错误,多条记录本应属于不同业务实体重新梳理唯一键组合,结合业务含义判断
数值范围异常,出现极端大值单位不一致,比如"万元"和"元"混用检查原始数据单位说明,统一换算后再计算

遇到问题别急着堆代码去修,先顺着时间的逆序往回查:最后一步是谁转换的?上一步的输入是什么?通常问两三层,真凶就现身了。

4.3 把数据处理做成可复用的流程

数据处理做多了会发现,很多操作其实是重复劳动。今天处理一份订单数据,明天又要处理一份用户数据,流程极其相似。与其每次都从零开始,不如把处理过程沉淀成可复用的流程工具。

我的习惯分三个层次:

第一层,保留原始数据。不管中间怎么处理,原始下载的文件或接口返回的原始JSON一律保留,单独放一个目录,不做任何修改。所有中间过程都基于原始数据的副本去操作,这样任何时候想追溯、想重跑,都有据可依。

第二层,处理脚本与SQL存档。每个项目的处理流程,我用脚本记录。脚本只接收配置参数,比如文件路径、日期范围、聚合粒度。这样下次拿到类似结构的数据,改改参数就能跑。

第三层,沉淀清洗函数库。把高频操作,比如格式清理、缺失率统计、时间格式统一,封装成函数。同一个函数多个项目中复用,比每次复制粘贴代码稳定得多。这个投入是值得的。

另外,我强烈建议给处理流程加上"测试用例"。不需要多复杂,给一小份构造好的样本数据,校验一个个已知结果。比如我固定一个1的缺失值样本集,每次改完清洗代码都跑一遍测试,确认原有功能没被破坏。这样做确实会让开发时间多花一点,但换来的是后面无数次的安心。

最后分享一点个人习惯

不管是在命令行还是在代码里处理数据,我现在都保持一个习惯:拿到一份数据,先看五分钟,再动手处理。所谓"看",不是用眼睛扫,而是用代码快速统计行数列数、缺失情况、字段类型、唯一值数量。这五分钟的体检,至少能避免一半的返工。还有一个小技巧:处理过程里每输出一份新数据,都顺手输出一个摘要表,记录行数、关键字段的缺失率、最大值最小值。后面出了问题,不用重新跑全量,先看摘要表就能定位大致环节。数据处理这件事,做对一次不难,难的是每次都能稳定做对。它不需要特别高深的数学,靠的是流程、习惯和一点点较真。希望这篇整理,能帮你少走一些我已经走进去过的坑。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进