
本文介绍如何在数据入库前统一处理爬取字段中混杂的单位(如“$ 75 thousand”“15 kg”等),将其标准化为统一单位和数值类型,并说明 mongodb 本身不提供内置单位转换能力,需在 etl 环节完成清洗。
本文介绍如何在数据入库前统一处理爬取字段中混杂的单位(如“$ 75 thousand”“15 kg”等),将其标准化为统一单位和数值类型,并说明 mongodb 本身不提供内置单位转换能力,需在 etl 环节完成清洗。
在构建网络爬虫系统时,面对来源多样、格式不一的网页数据,常见挑战之一是同一语义字段(如 amount、weight)使用多种单位和表达形式。例如:
- 金额字段:"$ 75 Thousand"、"$ 1.2 Lakh"、"$ 1500"、"₹ 2.5 Crore"
- 重量字段:"15 Kg"、"25 Kgs"、"600 gm"、"15,000 gm"、"0.015 Ton"
若直接存入 MongoDB,不仅影响查询一致性与聚合准确性,更会阻碍后续分析(如金额求和、重量范围筛选)。MongoDB 作为文档型数据库,不提供字段级单位自动识别或标准化功能——它只存储你写入的数据,不会解析 "Kg" 或 "Thousand" 的语义。因此,标准化必须前置到数据采集或预处理阶段(即 ETL 中的 “T:Transform” 环节)。
推荐实践:在爬虫层完成标准化清洗
以开源爬虫框架 Norconex Web Crawler 为例(支持 Java 生态,可深度定制),提供四级渐进式清洗能力:
✅ 1. 简单替换:ReplaceTagger(适用于固定字符串映射)
适合单位缩写替换,如将 "Thousand" → "000"、"Lakh" → "00000"(注意:需配合数字提取逻辑):
<handler class="ReplaceTagger"><replace><fieldmatcher>amount</fieldmatcher><valuematcher replaceall="true">Thousand</valuematcher><tovalue>000</tovalue></replace><replace><fieldmatcher>amount</fieldmatcher><valuematcher replaceall="true">Lakh</valuematcher><tovalue>00000</tovalue></replace></handler>
⚠️ 注意:此方式仅做文本替换,后续仍需数值解析(如去除 $、逗号,转换浮点数)。
✅ 2. 正则提取:RegexTagger(推荐用于结构化提取)
可精准捕获数字+单位组合,并分组处理。例如从 "15 Kg" 中提取 15 和 Kg:
<handler class="RegexTagger"><pattern fieldgroup="1" valuegroup="2" tofield="weight_clean"></pattern></handler>
该配置将生成新字段 weight_clean,值为匹配的两组内容(如 ["15", "Kg"]),便于后续脚本统一换算。
✅ 3. 灵活计算:ScriptTagger(最常用且推荐)
使用 JavaScript(或 Groovy/Python)编写完整清洗逻辑,支持单位识别、数值转换、异常处理:
<handler class="ScriptTagger"><script>
<![CDATA[
// 标准化 amount 字段(输出单位:USD 美元整数)
let amountStr = metadata.getString('amount', '');
if (amountStr) {
// 移除货币符号、空格、逗号
let clean = amountStr.replace(/[\$\u20B9\u20A8,\s]/g, '');
// 匹配数字 + 单位关键词
let match = clean.match(/^(\d+(?:\.\d+)?)\s*(Thousand|Lakh|Crore|Million|Billion)?$/i);
if (match) {
let num = parseFloat(match[1]);
let unit = (match[2] || '').toLowerCase();
switch(unit) {
case 'thousand': num *= 1000; break;
case 'lakh': num *= 100000; break;
case 'crore': num *= 10000000; break;
case 'million': num *= 1000000; break;
case 'billion': num *= 1000000000; break;
}
metadata.set('amount_usd', Math.round(num)); // 存为整数
}
}
// 标准化 weight 字段(输出单位:克,整数)
let weightStr = metadata.getString('weight', '');
if (weightStr) {
let wMatch = weightStr.match(/^([\d.,]+)\s*(Kg|Kgs|gm|g|Ton|ton|lbs)/i);
if (wMatch) {
let val = parseFloat(wMatch[1].replace(/,/g, ''));
let unit = wMatch[2].toLowerCase();
let grams = 0;
switch(unit) {
case 'kg': case 'kgs': grams = val * 1000; break;
case 'gm': case 'g': grams = val; break;
case 'ton': case 'tons': grams = val * 1000000; break;
case 'lbs': grams = val * 453.592; break;
}
metadata.set('weight_g', Math.round(grams));
}
}
]]>
</script></handler>
✅ 优势:
- 支持四则运算、条件分支、异常容错;
- 可同时处理多字段、多单位体系;
- 输出为纯数字(Number 类型),MongoDB 自动映射为 int32/int64/double;
- 清晰分离清洗逻辑,便于测试与维护。
✅ 4. 全文档重构:ScriptTransformer(适用于需修改原始 HTML/JSON 内容的场景)
当标准化依赖页面上下文(如单位在邻近标签中)时,可用此处理器访问完整文档内容,但日常字段清洗中较少需要。
最终写入 MongoDB 的建议结构
清洗后,推荐在文档中保留原始字段(用于审计)+ 标准化数值字段(用于业务):
{
"url": "https://example.com/product/123",
"amount_raw": "$ 75 Thousand",
"amount_usd": 75000,
"weight_raw": "15 Kg",
"weight_g": 15000,
"currency": "USD",
"weight_unit": "g"
}
这样既保障数据可追溯性,又确保查询高效性(如 db.products.find({ amount_usd: { $gt: 50000 } }))。
总结
- ❌ MongoDB 无内置单位标准化能力,不可依赖数据库层解决;
- ✅ 必须在爬虫或中间 ETL 层完成清洗,优先选用支持脚本扩展的爬虫(如 Norconex、Scrapy + 自定义 pipeline);
- ✅ ScriptTagger 是平衡灵活性与可维护性的首选方案,可封装为通用函数复用;
- ✅ 始终保留原始字段 + 标准化数值字段,兼顾可解释性与实用性;
- ? 对于高频更新源,建议将单位映射规则外置为 JSON 配置,实现热更新。











