
$PSVersionTable).urllib, json, re, pathlib). Если хотите requests/bs4 — сверьтесь с документацией по установке.LLM_BASE_URL, LLM_MODEL, LLM_API_KEY.robots.txt и условия использования сайта (ToS — terms of service, правила площадки), не сохраняем персональные данные (имена, телефоны, e-mail из форм).Что сделать: проверить инструменты и задать переменные окружения.
Зачем: если ключ или curl недоступны, все последующие шаги упадут на середине и результат будет неполным.
Как проверить: каждая команда должна вернуть версию или подтверждение.
python3 --version
curl --version | head -n 1
df -h . | tail -n 1
export LLM_BASE_URL="https://<ваш-llm-эндпоинт>"
export LLM_MODEL="<модель-из-документации>"
export LLM_API_KEY="<ключ>"
test -n "$LLM_API_KEY" && echo "key set"
Ожидаемый вывод: строка вида Python 3.x.x, curl 8.x.x, строка с свободным местом и key set.
Пример: df -h . | tail -n 1 печатает /dev/sda1 100G 20G 80G 20% / — 80 ГБ свободно, места достаточно.
Что сделать: прочитать robots.txt проверяемого домена.
Зачем: файл robots.txt — текстовый список правил для роботов; если там Disallow: /, автоматический сбор запрещён и дальше идти нельзя.
curl -sS "https://<ваш-домен>/robots.txt" | head -n 20
Ожидаемый вывод: строки User-agent: *, Disallow: ... и, часто, Sitemap: https://<ваш-домен>/sitemap.xml. Запомните путь к sitemap — он понадобится на шаге 1.
Что сделать: создать рабочую папку и скачать три публичных файла.
Зачем: это исходники, по которым ИИ будет строить карту воронки (воронка — путь от первого касания с рекламой до покупки). Без локальных копий нечего проверять: любая фраза модели останется без доказательства.
mkdir -p funnel/<ваш-домен>/{raw,out}
curl -sSL -A "Mozilla/5.0 (compatible; FunnelAudit/1.0)" \
"https://<ваш-домен>/" -o funnel/<ваш-домен>/raw/index.html
curl -sSL "https://<ваш-домен>/robots.txt" -o funnel/<ваш-домен>/raw/robots.txt
curl -sSL "https://<ваш-домен>/sitemap.xml" -o funnel/<ваш-домен>/raw/sitemap.xml
Флаг -L заставляет curl идти по редиректам (частая причина «пустого» файла), -A подставляет честный User-Agent — строку, которой клиент представляется серверу.
Как проверить:
wc -c funnel/<ваш-домен>/raw/index.html
grep -c "<loc>" funnel/<ваш-домен>/raw/sitemap.xml
Ожидаемый вывод: число больше 10000 для index.html и число больше 1 для sitemap. Если index.html весит 0 байт — см. раздел проблем.
Что сделать: вытащить из HTML заголовки, описание, H1, кнопки-призывы (CTA — call to action, текст кнопки «Купить», «Оставить заявку») и список счётчиков аналитики.
Зачем: заголовок и H1 показывают оффер (обещание клиенту), CTA — этап воронки, а счётчики (gtag, fbq, ym) — на каких рекламных платформах конкурент вообще ведёт кампании.
# funnel/<ваш-домен>/extract.py
import sys, json, re
from pathlib import Path
html = Path(sys.argv[1]).read_text(encoding="utf-8", errors="ignore")
def first(pattern):
m = re.search(pattern, html, re.I | re.S)
return re.sub(r"\s+", " ", m.group(1)).strip() if m else None
data = {
"title": first(r"<title[^>]*>(.*?)</title>"),
"description": first(r'<meta[^>]+name=["\']description["\'][^>]+content="\'["\']'),
"h1": first(r"<h1[^>]*>(.*?)</h1>"),
"counters": sorted(set(re.findall(r"\b(gtag|fbq|ym|rtrg)\b", html))),
"cta": re.findall(r"<a[^>]+>([^<]{3,60})</a>", html)[:20],
}
Path("out/page.json").write_text(json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8")
print("OK:", len(data["cta"]), "CTA, счётчики:", data["counters"])
Пример: запуск python3 extract.py funnel/example.com/raw/index.html печатает OK: 14 CTA, счётчики: ['fbq', 'gtag', 'ym'] — значит, на странице стоят пиксели трёх рекламных систем.
Как проверить: откройте funnel/<ваш-домен>/out/page.json — поля title и h1 не должны быть null. Если null, значит разметка страницы нестандартная: сверьтесь с HTML вручную через grep -i "<title" raw/index.html.

Что сделать: сохранить промпт-инструкцию в файл prompt.txt рядом со скриптом.
Зачем: без схемы модель отвечает свободным текстом, и результат нельзя ни сравнить между доменами, ни проверить. Требование поля evidence (доказательство) заставляет модель цитировать исходную строку, а не придумывать оффер.
Ты — аналитик маркетинга. На вход подаётся JSON с данными публичной страницы.
Верни ТОЛЬКО JSON-объект без markdown-обёртки по схеме:
{"items":[{"channel":"ads|social|seo","stage":"awareness|interest|decision",
"offer":"...","cta":"...","evidence":"точная строка из входных данных"}]}
Правила: не добавляй факты, которых нет во входном JSON; если данных для поля нет — null;
evidence обязательно и должно дословно совпадать с фрагментом входа.
Что сделать: отправить page.json в LLM и сохранить ответ.
# funnel/<ваш-домен>/ask_llm.py
import json, os, urllib.request
from pathlib import Path
payload = json.loads(Path("out/page.json").read_text(encoding="utf-8"))
prompt = Path("prompt.txt").read_text(encoding="utf-8")
body = json.dumps({
"model": os.environ["LLM_MODEL"],
"messages": [
{"role": "system", "content": prompt},
{"role": "user", "content": json.dumps(payload, ensure_ascii=False)},
],
}).encode()
req = urllib.request.Request(
os.environ["LLM_BASE_URL"].rstrip("/") + "/chat/completions",
data=body,
headers={"Authorization": "Bearer " + os.environ["LLM_API_KEY"],
"Content-Type": "application/json"},
)
with urllib.request.urlopen(req, timeout=120) as r:
ans = json.load(r)
text = ans["choices"][0]["message"]["content"]
Path("out/funnel.json").write_text(text, encoding="utf-8")
print("saved:", len(text), "символов")
Путь /chat/completions и структура ответа choices[0].message.content — формат OpenAI-совместимых API; если ваш провайдер устроен иначе, сверьтесь с его документацией.
Как проверить: ответ должен парситься как JSON и содержать записи с непустым evidence.
python3 -c "import json;d=json.load(open('funnel/<ваш-домен>/out/funnel.json'));\
print('записей:',len(d['items']));\
print('без evidence:',sum(1 for i in d['items'] if not i.get('evidence')))"
Ожидаемый вывод: записей: 9 и без evidence: 0. Если вторая цифра больше нуля — повторите запрос с формулировкой «каждая запись обязана содержать evidence».
Собранные записи удобно свести в таблицу с колонками «Канал / Этап / Оффер / CTA / Доказательство»; как быстро собрать такую сводку в документе — разобрано в гайде ChatGPT Office: как создавать документы и таблицы с помощью ИИ.
| Поле | Что писать | Откуда брать |
|---|---|---|
| channel | `ads`, `social` или `seo` | счётчики и разметка страницы |
| stage | `awareness`, `interest`, `decision` | тип CTA («Читать» → interest, «Купить» → decision) |
| offer | обещание клиенту одной фразой | `title`, `h1` |
| cta | текст кнопки дословно | массив `cta` |
| evidence | цитата из `page.json` | входные данные, дословно |

Что сделать: выборочно проверить, что каждая цитата evidence реально есть в скачанном HTML.
Зачем: это единственный способ поймать галлюцинацию (выдуманный факт) — модель могла «дописать» акцию, которой на странице нет.
grep -F -o "$(python3 -c "import json;print(json.load(open('funnel/<ваш-домен>/out/funnel.json'))['items'][0]['evidence'])")" \
funnel/<ваш-домен>/raw/index.html
Пример: если evidence равно Скидка 20% на первый заказ, команда печатает эту же строку. Пустой вывод = цитаты на странице нет, запись надо удалить.
Что сделать: собрать итоговый файл out/report.md из проверенных записей и зафиксировать дату среза в имени папки (funnel/<ваш-домен>-2025-06-01).
Зачем: воронка конкурента меняется, и без даты через месяц невозможно понять, что изменилось — оффер или ваша ошибка.
Как проверить: чек-лист ниже должен быть полностью закрыт.
raw/index.html весит больше 10 КБrobots.txt не содержит Disallow: /out/funnel.json парсится и содержит 0 записей без evidencegrep -Fout/report.md есть колонка «Доказательство» и дата срезаЕсли результат нужно показать команде на созвоне, соберите из отчёта короткую колоду — приёмы описаны в гайде Как создавать презентации с помощью ИИ: лучшие сервисы и советы.
Проблема. curl скачивает пустой файл или страницу-заглушку
< HTTP/2 403 в выводе curl -sS -o /dev/null -w "%{http_code}\n" "https://<ваш-домен>/" либо в index.html текст Attention Required! | Cloudflarerobots.txt и ToS; используйте официальные API площадки или ручной экспорт страницы через «Сохранить как → HTML полностью» и положите файл в raw/index.html. Дальнейшие шаги не меняются.Проблема. Модель вернула текст вместо JSON
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0) `json ```.python3 -c "
import re,pathlib
t=pathlib.Path('funnel/<ваш-домен>/out/funnel.json').read_text(encoding='utf-8')
m=re.search(r'\{.*\}', t, re.S)
pathlib.Path('funnel/<ваш-домен>/out/funnel.json').write_text(m.group(0), encoding='utf-8')
print('cleaned')"
и в следующем запросе усилить инструкцию: «ответ должен начинаться с { и заканчиваться }».
Проблема. sitemap не найден
grep -c "<loc>" возвращает 0, а в файле видна HTML-страница с текстом 404 Not FoundSitemap: в robots.txt и скачайте указанный там URL:grep -i "^sitemap:" funnel/<ваш-домен>/raw/robots.txt
curl -sSL "<URL-из-вывода>" -o funnel/<ваш-домен>/raw/sitemap.xml
Вы получили воспроизводимый срез воронки конкурента: локальные копии публичных файлов, JSON-карту каналов и офферов с цитатами-доказательствами и отчёт с датой среза. Следующий шаг — повторить процедуру для 3–5 конкурентов из вашей ниши и сравнить колонку channel, чтобы увидеть, какие каналы они реально используют.
Можно ли разобрать воронку без кода, только через чат с ИИ?
Да, но проверяемость падает: вы вставляете текст страницы вручную и не сможете автоматически сверить цитаты через grep -F. Схема с evidence и локальными файлами надёжнее, потому что каждое утверждение модели проверяется одной командой.
Законно ли собирать данные о рекламе и сайте конкурента?
Собирать можно только публично доступные страницы, соблюдая robots.txt и ToS площадки. Не сохраняйте персональные данные из форм и не пытайтесь обходить защиту от ботов — при блокировке используйте официальные API или ручной экспорт.
Почему ИИ пишет офферы и акции, которых нет на сайте?
Потому что в промпте не было требования цитировать источник. Добавьте обязательное поле evidence с дословной строкой из входных данных и проверку python3 -c "... sum(1 for i in d['items'] if not i.get('evidence'))" — записи без цитаты удаляются.
Как часто повторять такой разбор?
Зависит от того, как часто конкурент меняет офферы и креативы; фиксированной нормы нет. Практично хранить каждый срез в папке с датой в имени и сравнивать отчёты между собой, не забывая про лимиты запросов к сайту и robots.txt.