返回主页

数据爬取与线索生成

BBQ / Catering Lead Generation System

一个面向 B2B 商家线索生成的全自动数据管道。组合 Serper 搜索引擎 API、Google Maps 地点解析、Facebook 页面解析, 实现关键词生成、搜索结果采集、结构化数据提取、去重过滤与 Supabase 持久化存储的完整闭环。

Python 3.11 FastAPI Supabase / PostgreSQL Serper API APScheduler BeautifulSoup Docker httpx

系统架构

关键词生成
keyword_generator
Serper 搜索
search + places
来源分类
Facebook / Maps
页面解析
HTML 数据提取
去重过滤
phone / website
Supabase
Postgres 存储

每周日 02:00 (UTC) 由 APScheduler 自动触发,也可通过 POST /pipeline/run 手动执行

Pipeline 执行流程

1

关键词生成

基于配置的基础关键词(bbq catering, bbq restaurant, smokehouse restaurant 等)与目标地区(Texas, California, UK, Australia 等),组合生成搜索关键词列表,自动去重。

2

Serper API 搜索

对每个关键词调用 Serper Google Search API 获取自然搜索结果,同时调用 Serper Places API 获取 Google Maps 地点数据,过滤出 Facebook 和 Google Maps 链接。

3

Google Maps 页面解析

访问 Google Maps 链接,使用 BeautifulSoup 解析 HTML,提取商家名称、评分、电话、网站、地址。若找到网站则进一步访问 contact 页面补充邮箱和社交媒体链接。

4

Facebook 页面解析

将 Facebook URL 标准化为 mbasic 版本以获取更好解析效果,提取 og:title 作为商家名称,从页面文本中用正则提取邮箱、电话和外站链接。

5

Website Enrichment

对从 Maps 获取到的网站 URL,自动访问主页和 /contact、/contact-us 页面,补充提取邮箱、电话、Facebook/Instagram 链接,丰富线索数据。

6

去重与入库

先进行内存级去重(基于 phone/website),再对 Supabase 已有数据查重,最终将唯一线索写入 leads 表。返回执行摘要(耗时、各阶段数量统计)。

项目结构

facebook_auto_getcustomer/ ├── Dockerfile # Python 3.11-slim 容器镜像 ├── docker-compose.yml # 一键部署配置 ├── requirements.txt # 依赖清单 ├── schema.sql # Supabase 建表 SQL ├── README.md ├── app/ │ ├── main.py # FastAPI 入口 — 路由 & lifespan │ ├── config.py # pydantic-settings 配置 │ ├── database.py # Supabase 客户端 & LeadRepository │ ├── models/ │ │ └── lead.py # Pydantic Lead 数据模型 │ ├── services/ │ │ ├── serper_service.py # Serper search + places API │ │ ├── lead_extractor.py # 统一线索提取入口 │ │ ├── facebook_parser.py # Facebook 页面解析 │ │ ├── maps_parser.py # Google Maps 页面解析 │ │ └── website_enricher.py # 网站信息补充 │ ├── pipelines/ │ │ └── weekly_pipeline.py # 周定时管道主逻辑 │ └── utils/ │ ├── keyword_generator.py # 关键词组合生成 │ ├── deduplication.py # 内存级去重 │ ├── scheduler.py # APScheduler 封装 │ └── logging.py # 日志配置 └── scripts/ └── run_pipeline.py # 手动触发管道脚本

核心模块说明

SerperService

app/services/serper_service.py

封装 Serper.dev 的 Search 和 Places 两个 API 端点。Search 获取自然搜索结果并过滤出 Facebook / Google Maps 链接;Places 直接获取 Google Maps 商家数据(名称、电话、网站、地址)。

FacebookParser

app/services/facebook_parser.py

将 Facebook URL 标准化为 mbasic 版本提高解析率。通过 og:title 提取商家名称,使用正则从页面文本中提取邮箱、电话,遍历链接标签寻找外站 URL。

GoogleMapsParser

app/services/maps_parser.py

解析 Google Maps 页面 HTML,提取商家名称(去除 " - Google Maps" 后缀)、评分、电话号码、网站链接和地址。解析成功后自动调用 WebsiteEnricher 补充数据。

WebsiteEnricher

app/services/website_enricher.py

访问商家网站主页及 /contact、/contact-us 页面,用正则补充提取邮箱和电话,遍历社交链接获取 Facebook / Instagram 地址。

WeeklyLeadPipeline

app/pipelines/weekly_pipeline.py

管道主编排逻辑:关键词生成 → Serper 搜索 → 多源解析 → 内存去重 → Supabase 查重 → 入库。每步记录统计数据,返回完整执行摘要。

LeadRepository

app/database.py

Supabase 数据访问层,封装 list_leads(支持 city/source 筛选分页)、get_stats(按 source 分组统计)、exists_by_phone_or_website(去重查询)、insert_lead(写入)。

KeywordGenerator

app/utils/keyword_generator.py

将基础关键词(如 bbq catering)与地区(Texas, UK 等)交叉组合,自动归一化去重后输出搜索关键词列表。

Deduplication

app/utils/deduplication.py

内存级去重:遍历线索列表,若两条线索的 phone 或 website 相同则视为重复,仅保留首条。

数据模型 (Lead)

Pydantic 模型定义 + Supabase leads 表结构,支持 UUID 主键自动生成的完整商家线索数据。

class Lead(BaseModel): id: UUID | None = None business_name: str # 商家名称(必填) category: str | None # 搜索关键词类别 city: str | None # 城市 country: str | None # 国家 address: str | None # 完整地址 phone: str | None # 电话号码 email: str | None # 邮箱 website: str | None # 官网 URL facebook: str | None # Facebook 页面 URL instagram: str | None # Instagram URL source: str # 来源 (facebook / google_maps) created_at: datetime | None # 创建时间

Supabase 建表 SQL

CREATE EXTENSION IF NOT EXISTS "pgcrypto"; CREATE TABLE IF NOT EXISTS public.leads ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), business_name text NOT NULL, category text, city text, country text, address text, phone text, email text, website text, facebook text, instagram text, source text NOT NULL, created_at timestamptz NOT NULL DEFAULT now() ); -- 唯一索引:基于电话号码去重 CREATE UNIQUE INDEX IF NOT EXISTS leads_unique_phone ON public.leads (phone) WHERE phone IS NOT NULL AND phone <> ''; -- 唯一索引:基于网站去重 CREATE UNIQUE INDEX IF NOT EXISTS leads_unique_website ON public.leads (website) WHERE website IS NOT NULL AND website <> '';

API 接口

方法路径说明参数
GET /leads 获取线索列表,支持筛选和分页 city (模糊匹配), source, limit (1~1000, 默认200), offset
GET /stats 获取统计信息:总线索数 + 按来源分组计数 -
POST /pipeline/run 手动触发一次管道执行,返回执行摘要 -

响应示例 — GET /stats

{ "total": 156, "by_source": { "google_maps": 98, "facebook": 58 } }

响应示例 — POST /pipeline/run

{ "started_at": "2025-03-10T02:00:00+00:00", "finished_at": "2025-03-10T02:03:42+00:00", "duration_seconds": 222.3, "keywords": 102, "serper_results_total": 1840, "extracted": 312, "inserted": 287, "skipped_duplicates": 25 }

环境变量配置

变量名默认值说明
SERPER_API_KEY必填 - Serper.dev API 密钥,用于 Google Search 和 Places 搜索
SUPABASE_URL必填 - Supabase 项目 URL
SUPABASE_KEY必填 - Supabase Service Role Key(推荐服务端写入使用)
PIPELINE_ENABLED可选 true 是否启用定时管道
PIPELINE_CRON可选 0 2 * * 0 Cron 表达式,默认每周日 02:00 UTC 执行
LOG_LEVEL可选 INFO 日志级别
HTTP_TIMEOUT_SECONDS可选 30 HTTP 请求超时时间(秒)
SERPER_RESULTS_LIMIT可选 20 每次 Serper 搜索返回结果数上限

部署指南

本地运行

# 1. 创建虚拟环境 python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate # 2. 安装依赖 pip install -r requirements.txt # 3. 配置 .env 文件 # SERPER_API_KEY=your_key # SUPABASE_URL=https://xxx.supabase.co # SUPABASE_KEY=your_service_role_key # 4. 在 Supabase SQL Editor 执行 schema.sql 建表 # 5. 启动 FastAPI 服务 uvicorn app.main:app --reload --port 8000 # 6. 手动触发一次管道(可选) python scripts/run_pipeline.py

Docker 部署

# 构建镜像 docker build -t bbq-leadgen . # 运行容器 docker run --rm -p 8000:8000 \ -e SERPER_API_KEY=your_key \ -e SUPABASE_URL=https://xxx.supabase.co \ -e SUPABASE_KEY=your_service_role_key \ bbq-leadgen

Docker Compose(推荐)

# 设置环境变量后一键启动 SERPER_API_KEY=... SUPABASE_URL=... SUPABASE_KEY=... docker compose up --build

依赖清单:fastapi, uvicorn, pydantic, pydantic-settings, httpx, beautifulsoup4, lxml, APScheduler, supabase, python-dateutil, pandas

演示视频

FastAPI + Uvicorn 与 Supabase 连接教程,演示如何配置和运行整个线索生成管道。


下载演示视频