Собираем сборщик с Claude Code

В прогоне курса агент собрал сборщик за 7,7 минуты, тесты прошли 13 из 13, отчёт выглядел аккуратно. Потом оказалось, что три адреса из пяти он придумал, и они отвечают 404 (страница не найдена). Понадобилось ещё четыре задания, чтобы данные стали правдой. Пройдём этот путь и научимся ловить такие ошибки до владельца.

Чему научишься в этом уроке:
• поставить агенту задачу на сборщик с правилами сбора;
• проверять robots.txt библиотекой, которая понимает звёздочку;
• ловить уверенную неправду в собранных данных;
• следить за числом тестов и возвращать удалённые.

Подготовка и грабля с robots.txt

Создай новую папку проекта, например monitoring-kodium, и открой в ней Claude Code так же, как в П1 и П2. Ключ CloseRouter и токен бота пойдут в .env этого проекта, как ты делал в уроке 1.6.

Одна ловушка спрятана ещё до сбора. В Python есть стандартная проверка robots.txt, urllib.robotparser. При подготовке курса её проверили на настоящих файлах, и она ответила «можно» для двух запрещённых адресов: страницы Практикума с ?utm=1 и отзывов Stepik. Она не понимает звёздочку в середине правила. Поэтому в задании прямо названа библиотека protego, которая звёздочки понимает. Если агент предложит robotparser, верни его к protego.

Шаг 1. Задание на сборщик

Задание из прогона, его можно отдать агенту как есть:

Новый проект для «Кодиума» (паттерн «Мониторинг»): каждое утро владелец получает в Telegram короткую сводку, что изменилось у конкурентов.
Сделай в этой папке (виртуальное окружение .venv создай сам, свежие версии библиотек, на Windows добавь tzdata):
1. Список из 5 страниц курсов Python у школ: Skillbox, Нетология, Яндекс Практикум, Хекслет, Stepik — найди по одной публичной странице курса Python у каждой (без входа на сайт). Сначала проверь robots.txt каждого сайта библиотекой, которая понимает символ * в правилах (protego), и не бери адреса с «?», /pricing и отзывы. Запросы — со своим User-Agent «KodiumMonitor/0.1 (+контакт владельца)», пауза 5–10 секунд между сайтами, таймаут, без повторов чаще раза в сутки.
2. Со страницы бери: название курса, цену (если она есть в HTML), длительность, ближайший старт. Если данные подгружаются скриптом и в HTML их нет — так и запиши «нет в HTML», не выдумывай.
3. Храни снимки по дням в SQLite (snapshots.db). Изменения со вчера считает КОД, не модель.
4. Если изменений нет — в Telegram короткое «Конкуренты: без изменений». Если есть — модель через CloseRouter (ключ CLOSEROUTER_API_KEY в .env, адрес https://api.closerouter.dev/v1, основная модель google/gemini-3.6-flash, запасная openai/gpt-5.4-mini) пишет 5–7 строк «что это значит для Кодиума», к каждому выводу — цитата из снимка. Повторы: таймаут, 2 попытки, потом запасная модель, потом сообщение «разбор не удался» с сырыми изменениями.
5. Токен бота и ключ не должны попадать в журнал (httpx/httpcore — уровень WARNING).
Тесты на сравнение снимков и разбор robots (подмена сети и модели). Запусти тесты, затем ОДИН настоящий прогон сбора (без модели, если изменений нет — первый день изменений нет) и покажи, что удалось собрать с каждого сайта.

Обрати внимание на пункт 2: «не выдумывай». И на просьбу в конце показать, что собрано с каждого сайта. Без неё ты увидел бы только «тесты прошли». Агент работал 7,7 минуты: тесты 13 из 13, но три адреса из пяти придуманные, а у Stepik в дату старта попал текст кнопки «Начать просмотр».

Почему тесты прошли, если адреса неверные? Тесты подменяют сеть сохранённой страницей и проверяют логику. Существует ли адрес на самом деле, они не знают. Проверка правды это настоящий сбор и твои глаза.

Задание шага 1 для своего заказчика

Задание выше написано под «Кодиум». Для своего заказчика возьми ту же основу и замени только места в угловых скобках: <заказчик>, <источники> (сайты и страницы из миссии урока 4.1) и <что сравниваем> (поля и правила из миссии урока 4.2). Если оставить «курсы Python» и «ближайший старт», сборщик будет искать старт курса на странице шиномонтажа.

Новый проект для «<заказчик>» (паттерн «Мониторинг»): каждое утро владелец получает в Telegram короткую сводку, что изменилось у <кого: конкурентов, поставщиков, площадок>.
Сделай в этой папке (виртуальное окружение .venv создай сам, свежие версии библиотек, на Windows добавь tzdata):
1. Источники: <список сайтов и по одной странице на каждый: адрес или «найди страницу <что за страница> у <сайт>»>. Сначала проверь robots.txt каждого сайта библиотекой, которая понимает символ * в правилах (protego), и не бери адреса с «?» и закрытые разделы. Запросы — со своим User-Agent «<Имя>Monitor/0.1 (+контакт владельца)», пауза 5–10 секунд между сайтами, таймаут, без входа на сайт, без повторов чаще раза в сутки.
2. Со страницы бери: <что сравниваем: поля и правило для каждого — откуда брать, откуда нельзя>. Если данных в HTML нет — так и запиши «нет в HTML», не выдумывай. Персональные данные (имена, телефоны) не сохраняй.
3. Храни снимки по дням в SQLite (snapshots.db). Изменения со вчера считает КОД, не модель.
4. Если изменений нет — в Telegram короткое «<Источники>: без изменений». Если есть — модель через CloseRouter (ключ CLOSEROUTER_API_KEY в .env, адрес https://api.closerouter.dev/v1, основная и запасная модели в .env: MODEL_MAIN, MODEL_FALLBACK) пишет 5–7 строк «что это значит для <заказчик>», к каждому выводу — цитата из снимка. Повторы: таймаут, 2 попытки, потом запасная модель, потом сообщение «разбор не удался» с сырыми изменениями. В журнал после каждого ответа: какая модель ответила и usage.
5. Токен бота и ключ не должны попадать в журнал (httpx и httpcore — уровень WARNING).
Запусти тесты (покажи их число) и один настоящий сбор. Покажи таблицу «значение → фрагмент»: источник, поле, значение, откуда взято, фрагмент страницы.

Строки, которые не меняются никогда, для любого заказчика:

  • проверка robots.txt библиотекой protego, без адресов с «?» и закрытых разделов;
  • пауза 5–10 секунд между сайтами, раз в сутки;
  • свой User-Agent с контактом владельца;
  • только страницы без входа на сайт, без персональных данных;
  • «нет в HTML» вместо догадки;
  • таблица «значение → фрагмент» в каждом отчёте и число тестов;
  • изменения считает код, модель только объясняет с цитатой;
  • модели в .env (MODEL_MAIN, MODEL_FALLBACK), журнал «какая модель ответила + usage», httpx на уровне WARNING.

Задания шагов 2–5 ниже под своего заказчика не копируй: у тебя будут свои ошибки. Бери их как образец формулы: симптом с числом со страницы → правило → тест → покажи таблицу.

Шаг 2. Настоящие адреса

Проверил результат: три адреса из пяти (Практикум, Skillbox, Нетология) отдают 404 — ты их не нашёл, а придумал. А на Stepik в «дату старта» попал текст кнопки «Начать просмотр».
Почини:
1. Найди НАСТОЯЩИЕ адреса страниц курса Python у этих трёх школ: открой их главную страницу или раздел каталога программирования (одним запросом на сайт, с проверкой robots.txt), найди ссылку на курс Python в HTML и возьми её. Каждый адрес проверь: код ответа 200 и в заголовке страницы есть «Python». Если найти не удалось — честно оставь школу с пометкой «страница не найдена», не подставляй догадки.
2. Дата старта: бери только то, что похоже на дату (число и месяц), иначе «нет в HTML».
3. Для каждой школы в итоговой таблице покажи: адрес, код ответа, что нашлось (название, цена, длительность, старт) и откуда взято (тег или фрагмент текста, 1 строка).
Запусти тесты и один настоящий сбор. Паузы и User-Agent — как раньше.

За 6,1 минуты агент нашёл 4 адреса из 5, а про Нетологию честно написал «страница не найдена». Это хороший ответ. Но в таблице появились новые странности: у Skillbox цена «008 415₽», у Практикума датой старта стал телефон «8 800 700-93-29». Агент назвал это «минорным».

Шаг 3. Ценник для роботов и фрагменты

Это не мелочи: в сводку владельцу уйдут неверные цифры. У Skillbox цена «008 415₽», у Практикума датой старта стал телефон «8 800 700-93-29». Сводка с ложными данными хуже, чем никакой.
Почини разбор:
1. Сначала ищи структурированные данные на странице: JSON-LD (script type="application/ld+json" — Course/Product/Offer: name, price, priceCurrency, startDate), мета-теги og:title/og:price. Только если их нет — текст страницы.
2. Цена: целое число рублей, без ведущих нулей, правдоподобный диапазон 1 000–1 000 000 ₽; иначе «нет в HTML». Дата: только формат даты (ДД.ММ.ГГГГ, «13 октября» и т.п.), рядом не должно быть слов «телефон», «8 800», «+7»; иначе «нет в HTML».
3. Для каждого значения сохраняй «источник»: JSON-LD / meta / текст + точный фрагмент (до 80 символов). В сводку модель берёт цитату именно отсюда.
4. Тесты на эти случаи (цена с ведущими нулями, телефон вместо даты, JSON-LD).
Запусти тесты и настоящий сбор, покажи таблицу: школа, значение, источник, фрагмент.

Здесь мы применили то, что спроектировали в уроке 4.2: сначала JSON-LD и meta, потом текст, и к каждому значению фрагмент. Агент работал 10,4 минуты и отчитался: «все значения проверены, ложных данных нет».

Шаг 4. Своя сверка одной страницы

Отчёту не поверили и открыли страницу Skillbox руками. «8 415 ₽» оказалось «8 415 ₽/мес −45%», то есть платёж в месяц по акции. «2 месяца» длительности оказались рекламой «Уже через 2 месяца сможете зарабатывать…». Задание:

Ты написал «все значения проверены, ложных данных нет», но я открыл страницу Skillbox сам:
- «8 415 ₽» — это «8 415 ₽/мес −45%», то есть ежемесячный платёж по акции, а не цена курса;
- «2 месяца» — из рекламной фразы «Уже через 2 месяца сможете зарабатывать на создании ИИ-ботов», это не длительность курса.
Почини:
1. Цена: если рядом «/мес», «в месяц», «рассрочка» — сохраняй как «платёж в месяц» отдельным полем, не как цену курса; пометка акции («−45%», «скидка») — отдельно.
2. Длительность: брать только из структурированных данных или из блока с явной подписью «длительность/срок обучения/N месяцев обучения»; фразы вида «через N месяцев сможете…» — не длительность.
3. Добавь для каждого значения проверку «фрагмент содержит само значение» и тест на оба случая выше.
4. Не пиши «ложных данных нет», если сам не сверил каждое значение с фрагментом страницы — покажи таблицу «значение → фрагмент», чтобы я проверил.
Запусти тесты и настоящий сбор.

Агент работал 11 минут и исправил платёж и длительность. Но появились две новые беды. У Stepik стартом стала дата «16.05.2025» из строки «Последнее обновление 16.05.2025». И тестов стало 9 вместо 18: агент удалил проверки, которые мешали, и показал зелёный результат.

Шаг 5. Вернуть тесты

Две проблемы:
1. Stepik: «дата старта 16.05.2025» взята из «Последнее обновление 16.05.2025» — это дата обновления страницы, не старт курса. Даты рядом со словами «обновление», «опубликовано», «изменено» — не старт. Для курсов без потоков (самостоятельный темп) — «старт: в любое время» только если это прямо написано на странице, иначе «нет в HTML».
2. Тестов было 18, стало 9 — ты удалил проверки. Верни все прежние тесты (телефон/ИНН/ОГРН, ведущие нули, JSON-LD) и добавь тест на «Последнее обновление». Удалять тесты, чтобы стало зелёным, нельзя.
Запусти тесты (покажи их число) и настоящий сбор, таблицу «значение → фрагмент».

За 3,7 минуты Stepik получил «нет в HTML», тесты вернулись: 23 прошли. Таблица «значение → фрагмент» стала честной. Одна мелочь осталась: у платежа Skillbox фрагмент взят из соседнего блока страницы. Заметить это можно только при сверке глазами, поэтому сверка остаётся твоей обязанностью и дальше.

Схема по прогону: окно терминала с командой pytest и строкой «23 passed», ниже таблица «школа | поле | значение | источник | фрагмент страницы». Skillbox: название из JSON-LD, платёж в месяц 8 415 ₽ с фрагментом «8 415 ₽/мес», цена курса «нет в HTML». Нетология — «страница не найдена». Яндекс Практикум и Хекслет — цена «нет в HTML». Stepik — старт «нет в HTML», на странице только «Последнее обновление».

Что вынести из прогона

  • Сборщик врёт уверенно. Придуманные адреса, телефон как дата, платёж в месяц как цена, реклама как длительность, дата обновления как старт. И каждый раз отчёт «проверено».
  • Лекарство: таблица «значение → фрагмент» и твоя сверка хотя бы одной страницы после каждого изменения разбора.
  • Следи за числом тестов. Было 18, стало 9: значит, агент удалил проверки. Число тестов проси показывать в каждом отчёте.
  • Называй ошибку точно, с числом со страницы: точная ошибка исправляется за одно задание.

На все пять заданий ушло около 39 минут работы агента. Это нормальная цена за данные, которым можно верить.

Если не получилось

  • Сайт отвечает 404 → адрес придуман; попроси найти ссылку в HTML главной или каталога и проверить код ответа 200 и «Python» в заголовке.
  • Сайт отвечает 403, показывает капчу или «вы робот?» → это защита сайта, не обходи её; смени источник или спроси заказчика.
  • Цена или дата странные (ведущие нули, телефон, «Начать просмотр») → нужна проверка формата и правдоподобного диапазона, задание шага 3.
  • Агент пишет «ложных данных нет», а таблицы с фрагментами нет → не верь, попроси таблицу «значение → фрагмент».
  • Тестов стало меньше, чем было → агент удалил проверки; верни их заданием шага 5.
  • Проверка robots.txt говорит «можно» для адреса с «?» у Практикума → используется robotparser; нужен protego.

Контрольный вопрос. Агент отчитался: «тесты 9 из 9 прошли, ложных данных нет». В прошлый раз тестов было 18. Что делаешь первым?

АРадуюсь и выкладываю на сервер
БПрошу агента написать новые тесты с нуля
ВУдаляю тесты совсем, раз они мешают
ГПрошу вернуть удалённые тесты и показать их число

Подсказка: Куда делись девять проверок?

На курсе задания проверяются автоматически. Решить первое задание бесплатно →  ·  Подобрать курс

Контрольный вопрос. Почему в задании названа библиотека protego, а не стандартный urllib.robotparser?

Аprotego быстрее скачивает страницы
Бrobotparser не понимает звёздочку в середине правила и разрешил запрещённые адреса
Вrobotparser не умеет читать файл по HTTPS
Гprotego обходит запреты robots.txt

Подсказка: Вспомни адрес Практикума с ?utm=1.

На курсе задания проверяются автоматически. Решить первое задание бесплатно →  ·  Подобрать курс

Контрольный вопрос. В таблице сборщика: старт Stepik 16.05.2025, фрагмент «Последнее обновление 16.05.2025». Что верно?

АВсё правильно, дата есть во фрагменте
БНадо прибавить месяц к этой дате
ВЭто дата обновления страницы; старт должен быть «нет в HTML», если другого нет
ГStepik надо убрать из сводки

Подсказка: О чём говорит слово перед датой?

На курсе задания проверяются автоматически. Решить первое задание бесплатно →  ·  Подобрать курс

Задание. МИССИЯ «Сборщик, которому можно верить». 1. Дай агенту задание шага 1. Для своего заказчика возьми вариант с местами , , из раздела «Задание шага 1 для своего заказчика»; строки из списка «не меняются никогда» оставь как есть. 2. Проверь результат: коды ответа, таблицу «значение → фрагмент». Найденные ошибки возвращай агенту точно, как в шагах 2–5. 3. Открой сам минимум одну страницу и сверь каждое значение с фрагментом. 4. Сломай и почини: попроси агента сохранить HTML одной страницы в папку тестов и добавить на её основе тест, где рядом с датой стоит телефон «8 800 …» или слова «Последнее обновление». Убедись, что тест проходит, а дата не попала в старт. Проверь, что число тестов не уменьшилось. Принято, если: приложены таблица «значение → фрагмент» по всем источникам, снимок вывода тестов с их числом до и после твоего теста, и 2–3 строки о том, какую ошибку нашёл своей сверкой (или почему ошибок не нашёл).

Подсказка: Сравни число тестов в каждом отчёте агента с прошлым.

✅ Готово, если: программа запускается без ошибок и выводит то, что просят в задании.

На курсе задания проверяются автоматически. Решить первое задание бесплатно →  ·  Подобрать курс

Что дальше

Сборщик собирает правду и хранит снимки. В следующем уроке подключим модель, которая объясняет изменения, выложим всё на сервер и поставим будильник на 8:00 по Москве.

Итог:
• robots.txt проверяет protego, адреса из головы агента проверяются кодом ответа;
• «проверено» в отчёте агента ничего не доказывает, доказывает таблица с фрагментами;
• одну страницу ты сверяешь сам;
• число тестов не должно уменьшаться.

← Назад  ·  ↑ В начало урока  ·  ⌂ В начало курса  ·  Вперёд →

Школа Виктора Комлева