跳转至

[ISSUE-0067] dogfood v0.10.87 — 4 项小修

v0.10.87 dogfood(advocatehealth.com 医生页)截图反馈: 1. 邮箱抓到 your@email.com(占位符污染) 2. 网页 2 个号码(847-382-6579 + 224-848-4453)只显示 1 个 3. 想开 multi-stage flag 但只能 DevTools console,体验差 4. v0.10.87 probe 把 403 当 ok 路径,但 403 一般是反爬墙

Bug 1:your@email.com 占位符污染

病灶

BUILTIN_EMAIL_BLACKLISTscraper.ts:2-8)只覆盖 example@example.com / yourname@domain.com 等,your@email.com 不在内。它是国外订阅表单 / footer 模板的极常见 placeholder。

扩充 BLACKLIST + NOISE 两层:

BLACKLIST 精确新增 14 个(your@email.com / you@example.com / me@example.com / info@example.com / contact@example.com / noreply@example.com / johndoe@example.com 等)

NOISE 模糊新增 9 个:@example.com / @example.org / @example.net / @yoursite.com / @yourdomain.com / @your-domain.com / @mysite.com / @mywebsite.com

Bug 2:网页 regex 提取的 phone 从未写入 DB(长期 bug)

病灶 ⚠️

scraper-executor.ts:processWebsiteScrape tab 路径写库时:

// v0.10.87 及之前
await updateByQuery('MapTaskData', { id: { in: [id] } }, {
  emails: uniqueEmails,
  facebook, linkedin, instagram, twitter, youtube,
  whatsapp: finalWhatsapp,
  scrape_status: 2,
  // ❌ phone 字段完全缺失!
});

extractedPhonesscraper-executor.ts:185)确实抽到了,但只塞进 page-log 做日志展示,从未写到 MapTaskData。

UI 看到的 phone 字段值一直是 Google Maps 商家本身的电话(地图采集阶段填的)— 不是网站抓的。截图里看到 +1 847-382-6579 是 Google Maps 给的;第二个 224-848-4453 网页 regex 抽到了但被丢了。

这是个 极长期 bug(自有 extractedPhones 那天起就漏写 DB)。

加 helper mergePhonesWithMapsLabel(mapsPhone, websitePhones)

export function mergePhonesWithMapsLabel(
  mapsPhone: string,
  websitePhones: string[]
): string {
  const normalize = (p: string) => p.replace(/\D/g, ''); // 纯数字 key 去重
  const mapsList = (mapsPhone || '').split(/[,;|]/).map(s => s.trim()).filter(Boolean);

  const seen = new Map<string, { value: string; source: 'maps' | 'web' }>();
  for (const p of mapsList) {
    const k = normalize(p);
    if (k.length >= 7 && !seen.has(k)) seen.set(k, { value: p, source: 'maps' });
  }
  for (const p of websitePhones || []) {
    const k = normalize(p);
    if (k.length >= 7 && !seen.has(k)) seen.set(k, { value: p, source: 'web' });
  }

  const all = Array.from(seen.values()).slice(0, 5);
  if (all.length === 0) return '';
  // 单号码 + 地图来源 → 不加标记(兼容旧数据)
  if (all.length === 1 && all[0].source === 'maps') return all[0].value;
  // 多号码 / 仅网站 → 地图来源加 (地图) 标记
  return all.map(({ value, source }) =>
    source === 'maps' ? `${value} (地图)` : value
  ).join(', ');
}

写库前先 select 现有 phone(Google Maps 给的),合并去重,回写:

const existing = await selectByQuery('MapTaskData', { where: { id: { in: [id] } }, limit: 1 });
const existingPhone = existing?.[0]?.phone || '';
const mergedPhone = mergePhonesWithMapsLabel(existingPhone, extractedPhones);

await updateByQuery('MapTaskData', { id: { in: [id] } }, {
  ...,
  phone: mergedPhone, // ← v0.10.88 写回
  ...
});

两处都改了:tab 路径(line 211)+ pipeline success 路径(applyPipelineOutcome)。

UX 选择(用户决策)

B 方案 + 地图来源加 (地图) 标记。 - 单号码场景(90%+):不加标记,UI 与 v0.10.87 完全一致 - 多号码场景:地图来源 +1 847-382-6579 (地图),网站抓的 224-848-4453,逗号分隔

Bug 3:缺 settings UI toggle

病灶

v0.10.87 默认关 enableMultiStageScrape,启用方式只有 chrome DevTools 改 storage。用户截图被 Chrome "allow pasting" 拦住 + 真跑发现 WXT lazy 写入(settings 没保存过时 chrome.storage.local 里没有 local:settingParams),导致 s = undefined,code 直接 TypeError。

settings-view.tsx 在 "🔗 内页跟随" 段之前加新段 "⚡ 多阶段抓取(实验性 · 默认关)" + 一个 RHFSwitch:

<RHFSwitch
  name="enableMultiStageScrape"
  label="启用多阶段抓取(HEAD → GET → 正则 → tab 兜底)"
  helperText="开启后,先用 fetch 轻量探测..."
/>

加 Yup schema:enableMultiStageScrape: Yup.boolean()

Bug 4:probe 403/451 未归 antibot

病灶

website-probe.ts pickStatusReason 只把 404/410 归 dead、429/5xx 归 retry-later,403/451 走默认分支返回 ok — 然后 fetch GET 拿 body,浪费一次请求。

SPEC-004 决策树原意:403/503 + cf-ray 是 antibot。但 403 没 cf-ray 怎么办? Spec 没明确。

按"宁可错杀不放过"原则,403/451 全归 antibot(让 tab fallback 用真实 UA/cookie 试):

if (status === 403 || status === 451) return { kind: 'antibot', reason: String(status) };

截图里 auroradvancedurgentcare.com 那条 403 在 v0.10.88 会被识别为 antibot → 直接 tab fallback,不浪费 GET。

元教训

  1. dogfood ≠ 完美:v0.10.87 写完自信"骨架可用",第一次截图就翻出 2 个真实 bug + 1 个长期 bug + 1 个体验问题。没装到真浏览器看的话,永远查不出 phone 字段从未写库
  2. WXT lazy storage 陷阱storage.defineItem getValue 在 storage 没该 key 时返回 defaultValue 但不写。chrome devtools 改 storage 不安全,应优先做 UI。
  3. 长期 bug 的发现需要新功能驱动:phone 字段从未写库这个事,没人会主动查 — 必须等用户截图对比,才能发现"咦,我抓到的电话去哪了"。
  4. 占位符黑名单永远漏:placeholders 五花八门,要持续补。

相关