Как мы с локальной LLM легально победили жесткий антифрод и создали визуально-текстового DOM-агента в обход блокировок
DISCLAIMER: Автор статьи категорически осуждает злонамеренное использование методов и способов массового и деструктивного автоматизированного сбора данных с любых сайтов объявлений и обсуждение подобных методов. Цель статьи в другом - рассказать о том как можно искать нужное, не нарушая при этом правил.
Эта история о том, как обычная задача по поиску редкого оборудования на вторичном рынке превратилась в полноценное инженерное исследование. В итоге мне удалось научить локальную языковую модель класса 7B/8B (Qwen) обходить агрессивные системы защиты, обфусцированный фронтенд и сетевые ограничения изолированной песочницы. Никакого платного API поисковиков, никаких прокси. Только Python, Playwright и пиксельно-логический подход. Статья написана по горячим следам нашего с ИИ “технологического прорыва”.
Предыстория и боль: Почему requests к крупным сайтам - это гарантированный бан.
Задача казалась тривиальной: попросить локальную модель в среде Strata найти объявления о продаже флагманской мобильной рабочей станции в максимальной комплектации с редкой профессиональной видеокартой на популярном сайте объявлений. Но современные крупные площадки объявлений обладают просто свирепой системой защиты от ботов. Попытка сделать прямой requests.get() или использовать стандартный BeautifulSoup моментально упирается в банальные “HTTP 403 Forbidden” или “HTTP 429 Too Many Requests”. Работу модели мгновенно отсекал WAF (веб-инструмент защиты) на сетевом уровне.
Тогда я попытался использовать классическую «прослойку» - научить модель искать объявления через поисковые системы (Google, Яндекс, DuckDuckGo) с оператором site:domain.ru. Но и тут меня ждало фиаско! Один поисковик выдал типовую заглушку «Вы не робот», которую текстовый скрипт не мог пройти. Текстовое HTML-зеркало второго поисковика намертво зависало по таймауту из-за сетевого allow-list’а внутри изолированного окружения Strata. Третий поисковик отдавал живой HTML, который при парсинге оказывался пустой JavaScript-заглушкой перенаправления.
Модель раз за разом выдавала ноль результатов, путалась в регулярных выражениях и ломала URL-адреса. Стало понятно: стандартные методы веб-скрейпинга если и не мертвы, то требуют крайне нетривиальных методов на грани фола. Нужно было менять саму архитектуру взаимодействия.
Найденный метод: Визуально-текстовый DOM-агент прямого действия (Visual Web Agent).
Вместо того чтобы пытаться обмануть сеть на уровне сырых пакетов, я решил попросить модель сымитировать человека на пиксельном и логическом уровнях. Метод базируется на “двух китах”: эмуляции реального браузера системы и парсинге DOM-дерева по «человеческим» маркерам вместо завязки на CSS-классы.
Шаг 1. Браузерный «плащ-невидимка» - эмуляция системного Edge.
Антифрод-системы легко вычисляют headless-браузеры Playwright или Selenium по специфическим флагам (например, navigator.webdriver = true). Поэтому я попросил модель перехватить управление уже установленным в операционной системе браузером Microsoft Edge, и модель мигом собрала скрипт на Python.
Скрипт запускается в видимом режиме (headless=False), используя системный бинарник браузера через параметр channel=“msedge”. Дополнительно в аргументы передаются флаги --start-maximized и --focus, а в коде вызывается метод page.bring_to_front(). Это принудительно выталкивает окно браузера из трея прямо на экран поверх всех окон. Для систем защиты это выглядит как абсолютно легитимный запуск страницы пользователем. Пауза в 8 секунд гарантирует, что все скрипты защиты успеют отработать, а страница - полностью прогрузиться.
Шаг 2. Победа над обфускацией: Парсинг по текстовым маркерам.
Фронтенд крупных платформ перегружен хэшированными динамическими классами (например, .styles-module-root-XyZ12), которые генерируются сервером случайным образом и меняются при каждом обновлении сайта. Но и тут модель оказалась на высоте и принципиально стала игнорировать CSS-селекторы. Агент сканирует DOM-дерево в один проход, цепляясь за смысловые текстовые маркеры:
Находит текстовый узел, содержащий знак валюты (или другой маркер цены) - это становится точкой фиксации.
Поднимается к родительскому контейнеру и забирает соседний крупный текст - это заголовок.
Извлекает из этого же блока ссылку, валидируя её регулярным выражением на наличие паттерна _\d{5,} (уникальный цифровой ID объявления в хвосте URL карточки).
Шаг 3. Сборка разнесенных данных по Item ID (iid).
Архитектура современных маркетплейсов часто разносит данные в целях оптимизации: имя продавца и его город могут лежать за пределами компактного контейнера карточки. Локальная модель сама нашла изящное решение: она обнаружила, что ссылки на профили магазинов в коде страницы имеют жесткий параметр привязки к карточке (например, ?iid=, где - это уникальный ID объявления). Скрипт, тут же совершенно самостоятельно оптимизированный моделью, стал снимать сквозной текстовый дамп страницы, находить совпадения по iid и склеивать пары «товар + цена + город + ссылка на продавца» со 100% точностью, полностью исключая смещение строк при выводе отчета.
В итоге я практически занял позицию наблюдателя, который просто сидел и смотрел на то, как «художник творит», получив в конце работы модели спарсенные объявления и ссылки!
Универсальный промпт-контейнер для запуска ИИ-агента.
В результате «родился» простой и универсальный промпт-контейнер для запуска ИИ-агента под описанную задачу. Чтобы ваша локальная (или облачная) модель сразу поняла этот метод и применила его к любому другому сложному сайту, не сваливаясь в requests и не пытаясь скачать встроенный Chromium (что часто ломает окружение), вы можете использовать следующий проверенный промпт-контейнер:
Запусти режим «Визуально-текстового DOM-агента прямого действия». Твоя задача - найти [что ищем] на сайте [имя сайта здесь].
Действуй строго по алгоритму:
Не используй requests и не пытайся скачивать встроенные браузеры библиотеки Playwright.
Запускай браузер в видимом режиме (headless=False), жестко прописав использование уже установленного Edge через параметр: channel=“msedge” (это отменит скачивание Chromium из сети).
Чтобы окно не оставалось свернутым в трее, обязательно передай в args запуска браузера флаги: “–start-maximized” and “–focus”, а после создания страницы вызови метод page.bring_to_front(), чтобы принудительно поднять окно на передний план экрана поверх всех окон.
Сформируй поисковый URL с зашитым запросом внутри ссылки, чтобы открыть страницу выдачи напрямую.
Выдержи паузу в 8 секунд после загрузки для обхода систем защиты и Cloudflare.
Сделай скриншот для визуального контроля и OCR-анализа.
Игнорируй хэшированные CSS-классы. Собирай данные (название, цена, ссылка) через проход по структуре DOM, зацепляясь за текстовые маркеры (знаки валют, паттерны ID карточек).
Если данные разнесены в DOM, сними сквозной текстовый дамп страницы и сопоставь блоки по индексам (например, через Item ID объявления).
Напиши код скрипта, выполни его прямо сейчас через инструмент запуска и выведи готовый отчет с объявлениями в чат.
Результаты: Что в итоге принес мой «автономный робот»?
Несмотря на то, что в процессе плотной интерактивной отладки скрипта целевой сайт в итоге выдал временное ограничение “429” на шестом заходе, модель проявила высокий уровень автономности. Она не растерялась, заблокировала новые сетевые запросы (пока “отвиснет” бан по IP) и собрала финальный структурированный отчет из уже сохраненных в памяти локальных буферов данных.
Самое интересное: ИИ-агент проявил элементы критического мышления. В одном из объявлений в заголовке было указано топовое название видеокарты, но модель просканировала DOM-текст описания и обнаружила строчку продавца: «Видеокарта: младшая серия на 16 гб». Модель сама вынесла в отчет предупреждение: «Внимание, противоречие в спецификации заголовка и описания, уточнять у продавца». Тем самым модель дала мне понять, что объявление мне не подходит даже при формальном “попадании” в запрос.
Заключение.
Ценность этого эксперимента - не конкретные файлы скриптов или json-логи, оставшиеся на диске, и не «изобретение велосипеда». Да и любой профессионал нашел бы куда как более серьезное решение. Главное тут - это доказательство концепции (PoC). Локальная модель класса 7B/8B уже легко способна выступать полноценным автономным ИИ-оператором вашего компьютера!
Объединяя «глаза» (визуальный контроль через скриншоты) и «руки» (управление DOM-деревом через системный браузер), локальная LLM становится практически неуязвимой для стандартных сетевых блокировок. Она может “видеть” интернет точно так же, как его видит живой пользователь.
И это всё больше приближает LLM к той самой, сакраментальной концепции, из слов одной легендарной электронной композиции: «Я твой слуга, я твой рАботник»*…
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.