Асинхронный пайплайн сбора B2B-лидов для AdTech-компании adWMG. Полностью интегрирован с Google Sheets — входные домены, результаты и отчёты о запусках хранятся в трёх таблицах на Google Drive.
[1/4] Input — читаем домены из Google Sheets (один столбец)
[2/4] ads.txt — проверяем /ads.txt и /app-ads.txt, считаем валидные строки
→ проходят только те, у кого суммарно > 5 строк
→ маркер adwmg.com → флаг Is_adWMG_Partner
[3/4] SimilarWeb — получаем total_visits
→ проходят только те, у кого ≥ MIN_MONTHLY_VISITS
[4/4] Snov.io — /v2/domain-emails-with-info, все сотрудники
→ фильтр по keywords.txt (подстрока, без учёта регистра)
Output — пишем лиды в Google Sheets + отчёт в отдельный лист
Скорее всего уже сделано, если у тебя уже есть JSON-ключ. Если нет:
- Зайди на https://console.cloud.google.com/ → создай проект (или выбери существующий).
- APIs & Services → Library → включи Google Sheets API и Google Drive API.
- APIs & Services → Credentials → Create Credentials → Service Account → создай сервис-аккаунт.
- У созданного аккаунта: Keys → Add Key → Create new key → JSON → скачай файл.
- Открой скачанный JSON — там есть поле
client_emailвидаxxx-xxx@xxx.iam.gserviceaccount.com. Этот email нужен для следующего шага.
Это та часть, которую обычно забывают и потом получают 403. Сервис-аккаунт — это отдельный «пользователь» Google, у него нет прав к твоим файлам по умолчанию.
Открой каждую из трёх таблиц в браузере → нажми Share (Поделиться) → впиши email сервис-аккаунта из Шага 1 → дай права Editor → сними галку «Notify people».
Таблицы, к которым нужен доступ:
Проще всего — поделиться правами Editor на всю папку, тогда доступ будет и к нынешним и к будущим таблицам внутри неё.
python -m venv .venv
.venv\Scripts\activate # Windows
source .venv/bin/activate # macOS / Linux
pip install -r requirements.txtПоложи JSON-ключ сервис-аккаунта в папку credentials/ (назови как угодно, например service_account.json).
Скопируй .env.example в .env:
copy .env.example .env # Windows
cp .env.example .env # macOS / LinuxОткрой .env и впиши только три ключа:
SIMILARWEB_API_KEY(твой rapidapi key для similarweb-insights)SNOV_CLIENT_IDиSNOV_CLIENT_SECRET(из кабинета Snov → Settings → API)
ID таблиц и путь к JSON-ключу уже заполнены дефолтными значениями.
Открой входную таблицу и впиши домены в первую колонку (по одному на строку). Можно с заголовком Domain / URL в первой строке — скрипт его распознает и пропустит. Можно в любом виде: nytimes.com, https://www.Forbes.COM, bbc.com/about — всё будет приведено к чистому домену.
python main.pyСмотри прогресс воронки в консоли. После завершения:
- Лиды появятся в таблице результатов.
- Отчёт о запуске — новый лист
run_YYYYMMDD_HHMMSSв таблице отчётов.
leadforge-scout/
├── main.py # Оркестратор
├── config/
│ ├── settings.py # .env + пороги
│ └── keywords.txt # ключевые слова для фильтра позиций
├── core/
│ ├── logger.py
│ ├── models.py # Pydantic-модели
│ └── gsheets.py # общий клиент Google Sheets
├── io_layer/
│ ├── sheets_reader.py # чтение входных доменов
│ ├── sheets_writer.py # запись итоговых лидов
│ └── report_writer.py # отчёт о запуске
├── checkers/
│ └── ads_checker.py # Шаг 2: ads.txt
├── api/
│ ├── similarweb.py # Шаг 3: трафик
│ └── snov.py # Шаг 4: контакты
├── credentials/
│ └── service_account.json # сюда кладёшь свой JSON-ключ
└── logs/
└── leadforge.log # локальный лог (ротация 5 МБ × 3)
| Domain | First Name | Last Name | Position | Total Visits | Is_adWMG_Partner | Ads_Txt_Lines | App_Ads_Txt_Lines | Run Timestamp | |
|---|---|---|---|---|---|---|---|---|---|
| nytimes.com | John | Smith | j.smith@nytimes.com | Head of Programmatic | 550000000 | FALSE | 342 | 2026-04-18 19:30 |
Один домен → столько строк, сколько подходящих контактов нашлось. Run Timestamp позволяет фильтровать данные конкретного запуска, если история копится.
Каждый запуск создаёт отдельный лист run_YYYYMMDD_HHMMSS с двумя секциями:
- Сводка — время старта/финиша, длительность, статус (
success/partial/failed), полная воронка в цифрах. - Журнал событий — timestamp, уровень (INFO/WARNING/ERROR), этап, сообщение. Если пайплайн упал — в журнале будет traceback.
Отчёт пишется даже при падении — это специально, чтобы увидеть что пошло не так без доступа к серверу.
| Переменная | Что делает | Дефолт |
|---|---|---|
MIN_MONTHLY_VISITS |
минимум визитов для прохождения Шага 3 | 100000 |
MIN_ADS_TXT_LINES |
минимум строк ads.txt+app-ads.txt (строго больше) | 5 |
CONCURRENCY_LIMIT |
параллелизм для ads.txt | 30 |
API_CONCURRENCY_LIMIT |
параллелизм для SimilarWeb и Snov | 10 |
REQUEST_TIMEOUT |
таймаут на HTTP-запрос, сек | 15 |
Файл config/keywords.txt. По одному слову/словосочетанию на строку. Матчинг — подстрока без учёта регистра (programmatic поймает Senior Programmatic Manager).
Таблица с ID '...' не найдена или у сервис-аккаунта нет доступа — ты забыл расшарить таблицу сервис-аккаунту (Шаг 2). Возьми email из JSON-ключа (поле client_email) и поделись таблицей с ним как Editor.
Не найден JSON-ключ сервис-аккаунта — проверь, что файл лежит по пути credentials/service_account.json или путь в .env указан правильно.
Отсутствуют переменные окружения: SIMILARWEB_API_KEY... — не заполнил .env. Скопируй .env.example в .env и впиши ключи.
Snov.io 402 (нет кредитов) — закончились кредиты Snov. Подожди сброса лимита или пополни.
Квота SimilarWeb исчерпана — rapidapi вернул 429. Скрипт аккуратно сохранит то, что успел собрать.
На Render, Railway или любом VPS:
- Запуши код в приватный GitHub-репозиторий (JSON-ключ не коммить — он в
.gitignore). - На хостинге прокинь
SIMILARWEB_API_KEY,SNOV_CLIENT_ID,SNOV_CLIENT_SECRETчерез переменные окружения. - JSON-ключ загрузи через Secret Files (на Render это в настройках сервиса) как
credentials/service_account.json. - Стартовая команда:
python main.py.