レシピ · 6手 · 1プロンプト実装

Web から見込み顧客を収集する(閉域ローカル・ニーズ精査つき)

企業サイトが公開するコーポレート窓口メールを、robots 尊重・レート制限・法令遵守のうえ収集し、ルールスコアでニーズ精査 → 人手レビュー → 承認分だけを CSV 書き出し。収集ツールと DB は commit / deploy しない閉域ローカル運用にする

前提: run-newsletter-ops解説 ch.31解説 ch.33module: マッチング & ディレクトリmodule: リード獲得

outbound の見込み顧客収集は、inbound フォーム(add-leads)とは 別系統の閉域ローカル専用ツールとして作る。このツールは絶対に本番アプリに載せない(scraping コード・ DB・収集前の候補を公開リポに含めない)。集めた候補は人手で精査し、承認分だけを run-newsletter-ops の取込器へ渡す。このツール自体は 一切メールを送らない。

0. 法令と設計の非交渉条件

特定電子メール法では、Web で公表された「営業用メールアドレス」への送信は一定条件下でオプトイン例外。 その趣旨に沿い、収集器に以下を機械的に強制する(変更禁止):

条件実装
robots.txt 尊重ドメインごとに取得・解釈、Disallow は取得しない。root Disallow は丸ごとスキップ
礼儀あるレート制限同一ドメイン ≥3s / 同時 ≤2 ドメイン / 15s タイムアウト / ≤6 ページ・ドメイン / 身元入り UA
企業窓口のみメールの登録可能ドメイン == サイト自身のドメインのみ採用。フリーメール・キャリアメール除外
個人アドレス除外firstname.lastname@ / 単名 は personal フラグで書き出し対象外。役職 (info/contact/sales/pr/recruit…) のみ
収集元の記録メール 1 件ごとに source_url + fetched_at を保存
do-not-contactdonotcontact.txt のドメイン/メールはハード除外
送信経路なし収集器にメール送信コードを持たせない。送信は別系統・人手承認後のみ

1. 閉域ディレクトリを用意

app リポ直下に _prospect/ を作り、.gitignore/_prospect/ を追加する(ツールも DB も候補も追跡しない):

echo "/_prospect/" >> .gitignore
mkdir -p _prospect/lib _prospect/data

保存は Node 22.5+ 内蔵の node:sqlite(外部依存ゼロ)。実行は node --experimental-sqlite

2. スコアリング設定(ニーズ精査の真実源)

_prospect/config.json に、収集の全ノブと重み付きキーワードを置く(ハードコード禁止・ここだけ編集で調整):

{
  "rateLimitMs": 3000, "maxPagesPerDomain": 6, "concurrency": 2, "timeoutMs": 15000,
  "userAgent": "YOURBOT-research-bot (+https://your.site; contact info@your.site)",
  "contactPathHints": ["/contact","/company","/about","/inquiry","/お問い合わせ","/recruit"],
  "freemailDomains": ["gmail.com","yahoo.co.jp","outlook.com","icloud.com", "..."],
  "roleLocalParts": ["info","contact","inquiry","otoiawase","sales","pr","press","recruit"],
  "scoring": {
    "keywords": { "ブランディング": 15, "資金調達": 14, "採用": 12, "新商品": 10, "プロダクト": 7, "EC": 7 },
    "sizeSignals": {}, "industryWeights": {}
  }
}

スコアは clamp(Σ 一致キーワード重み + シグナル, 0, 100)透明なルールベース。LLM 判定を足す場合も一次はこのルールで絞る。

3. 収集する

collect.mjs(種 CSV: url[必須] / industry / note)を実行:

node --experimental-sqlite _prospect/collect.mjs _prospect/seeds.csv

各ドメインで: robots 確認 → homepage 取得(レート制限)→ 会社名・メール・シグナル抽出 → robots が許す contact 系パスを最大 N ページ → スコア → SQLite に upsert。 7 日以内クロール済みはスキップ(--refresh で再取得)、1 ドメインの失敗で全体を止めない。

4. ニーズ精査(人手レビュー)

review.mjs127.0.0.1 のみにバインド(非ループバックは 403)のローカル UI を立てる:

node --experimental-sqlite _prospect/review.mjs   # → http://127.0.0.1:4599

スコア降順の一覧で、企業 / ドメイン / 役職メール / シグナル / status を見て 承認 / 却下。 これが「見込みあり」を人が最終判断する関門。localhost 専用なので外部に露出しない。

5. 承認分を書き出す → 取込器へ

node --experimental-sqlite _prospect/export-approved.mjs
# → _prospect/data/approved-export.csv (email,name,source_url)  ※役職メールのみ・個人除外

書き出した CSV を run-newsletter-ops の取込器へ:

node scripts/import-inquiries.mjs _prospect/data/approved-export.csv --source generic --apply

取込器側で PII は AES-256-GCM 暗号化・source 記録され、以後は unsubscribe / suppression の管轄下に入る。

6. 送信は別工程(無差別送信を構造的に不可能にする)

承認 → 取込 で連絡先化しても、送信は必ず run-newsletter-ops のキャンペーン作成を経る。 初回コンタクトは送信者表示+配信解除リンクを必須にし、セグメントは cold-prospect として分離運用する。

検証(acid test)

crawl_log ok=1・行が書かれることを確認(メール 0 でも form 運用なら正常)。