
采集拿到的是显示的样子,不是能用的值
一个页面上写着 1.234,56 €,另一个写着 €1,234.56。这两个数字完全相等,但直接导进表格,一个会被识别成一千二百三十四点五六,另一个可能被识别成一点二三。
这不是采集的问题——两个页面显示的都没错,只是国家不同。问题出在拿到数据之后:你要的是可以计算、可以排序、可以去重的值,而页面给的是给当地人看的写法。
这一步就是清洗。它不产生新数据,但它决定这份数据能不能直接用。下面五类是海外数据里最常撞上的。
一、公司名后缀
这是海外名单去重最大的坑。
同一家公司,在不同平台上可能写成:
ABC Handels GmbHABC Handels GmbH & Co. KGABC HandelsABC HANDELS GMBH
各国的公司形式后缀完全不同:德国 GmbH / AG / KG,英国 Ltd / Limited / PLC,美国 Inc. / LLC / Corp.,澳大利亚 Pty Ltd,荷兰 B.V. / N.V.,法国 SARL / SAS,意大利 S.r.l. / S.p.A.,西班牙 S.L. / S.A.。日本的 株式会社 还可能出现在名字前面。

去重的做法是生成一个规范化键:转小写、去标点、去后缀、合并连续空格。ABC Handels GmbH 和 ABC HANDELS 都变成 abc handels,就能匹配上。
但这里有个必须说清楚的限制:ABC Ltd 和 ABC LLC 规范化之后也会撞在一起,而它们可能真是两家不同的公司。 规则解决不了这个问题。我们的做法是规范化匹配之后,用地址或网站域名做二次确认,并把匹配的置信度标出来——高置信度的直接合并,边界情况单独列出来由你判断。
二、电话号码
页面上的电话是给当地人拨的,不是给系统存的。
德国柏林的一个号码,本地写法是 030 12345678。转成国际格式时,前导的 0 是国内长途前缀,必须去掉,变成 +49 30 12345678。
但意大利是个例外:罗马的 06 12345678 转成国际格式是 +39 06 12345678,前导 0 要保留。按德国的规则去处理意大利号码,得到的号码打不通。

其他常见写法:法国 01 23 45 67 89 两位一组、美国 (555) 123-4567 带括号和连字符、英国 020 7123 4567 分三段。
统一的目标格式是 E.164:加号、国家码、剩余号码,中间不带任何分隔符和空格。这样存进系统才能做匹配和去重。
但原始写法要保留。 一是号码本身可能就是残缺的(页面只写了本地号,没有区号,而页面所在国家不一定等于号码所属国家),二是出现打不通的情况时,要能回去看采集到的原文是什么。
三、地址
各国地址的字段顺序不一样,而且不是简单的前后调换。
德国:Hauptstraße 12, 10115 Berlin——街道名在前门牌号在后,邮编在城市前面。
英国:12 Main Street, London SW1A 1AA——门牌号在前,邮编在城市后面,而且邮编分两段中间有空格。
美国:12 Main St, Springfield, IL 62701——州用两位缩写,邮编五位。
荷兰邮编是 1234 AB 这种数字加字母的形式。

这里我们的建议和别的字段相反:除非你明确需要,否则不要把地址硬拆成结构化字段。
原因是拆分规则要按国家分别写,而拆错的代价比不拆更高。一个德国地址如果把 10115 当成门牌号、把 Berlin 当成街道,那后面所有按城市做的统计都是错的,而且错得不明显——你要等到用这份数据做决策时才会发现。
如果你只是要按国家或城市筛选,保留完整地址加一个单独的国家列,通常就够用了。真要拆,测试阶段会先按目标国家跑一批样本给你看拆分准确率,确认之后再全量。
四、日期与时区
03/04/2026 在美国是 3 月 4 日,在欧洲是 4 月 3 日。
同一个字符串,两个意思,而且无法从数据本身判断是哪个。 只有知道这条数据来自哪个站点、那个站点用哪种格式,才能确定。这个信息在采集的那一刻是有的,交付之后就没有了。
所以日期字段一律转成 ISO 8601(2026-03-04)再交付,这是唯一没有歧义的写法。

还有两件相关的事:
相对时间必须在采集时换算。 页面上写「2 天前」「上周」的,交付里存这个字符串毫无意义——数据放三个月之后,「2 天前」是相对哪一天的两天前?我们会在采集时刻换算成绝对日期,并保留 collected_at 供核对。
采集时间带时区。 价格、排名、库存这类快照字段,跨时区比较时差几个小时可能就是不同的状态。我们默认用 UTC 并在字段说明里标明,这一点在数据交付应该按什么标准验收里也提过。
五、数字与货币
小数点和千分位的写法,欧洲和英美是反的:
| 写法 | 使用地区 | 实际数值 |
|---|---|---|
1,234.56 |
英美、多数英语国家 | 一千二百三十四点五六 |
1.234,56 |
德国、意大利、西班牙、荷兰 | 一千二百三十四点五六 |
1 234,56 |
法国(千分位是空格) | 一千二百三十四点五六 |
1'234.56 |
瑞士 | 一千二百三十四点五六 |
四种写法同一个数。如果一份数据里混着几个国家站点的价格,直接按字符串排序或者直接求和,结果全是错的,而且不会报错。

货币符号的位置也不统一:€1,234 和 1.234 € 都有,符号本身还可能是 EUR、€,或者干脆不显示(页面靠上下文说明币种)。
所以价格类字段我们默认拆成两列:price 存纯数值,currency 存 ISO 4217 代码(EUR、USD、GBP)。想还原页面上的显示样子,可以再加一列原始字符串。
一条贯穿的原则:原始值不能丢
上面每一类的建议都带着同一句话——保留原始值。
原因是标准化一定会丢信息。ABC GmbH & Co. KG 规范化成 abc 之后,你再也无法从这份数据里知道它原来是哪种公司形式;030 12345678 转成 +493012345678 之后,也看不出它原来是不是带区号的完整号码。
更实际的一点是可追溯。数据用出问题时,第一件事是回去比对来源页面。如果交付里只有处理后的值,这一步就断了——你无法判断是采集错了、处理错了,还是页面本身就是那么写的。
所以我们的默认做法是原始值一列、标准化值一列,字段说明里写明哪一列是处理过的、按什么规则处理的。文件会大一些,但这是来源可追溯的前提。
提需求时建议一并说明
- 涉及哪些国家:决定要按几套规则处理,也决定测试样本怎么抽
- 数据拿去做什么:做去重和做外呼,对电话字段的处理要求完全不同
- 要不要拆地址:拆的话按哪几级拆,以及能接受多少拆分误差
- 能不能接受两列:有些系统导入时字段数有限制,需要提前确认
这几项确定后,测试阶段会按你的国家范围抽一批样本,把处理前后的结果并排给你看。多来源合并时还有额外的口径问题,写在多个来源的数据怎么合并。
