Как мы с локальной 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-дерево в один проход, цепляясь за смысловые текстовые маркеры:

  1. Находит текстовый узел, содержащий знак валюты (или другой маркер цены) — это становится точкой фиксации.

  2. Поднимается к родительскому контейнеру и забирает соседний крупный текст — это заголовок.

  3. Извлекает из этого же блока ссылку, валидируя её регулярным выражением на наличие паттерна _\d{5,} (уникальный цифровой ID объявления в хвосте URL карточки).

Шаг 3. Сборка разнесенных данных по Item ID (iid).

Архитектура современных маркетплейсов часто разносит данные в целях оптимизации: имя продавца и его город могут лежать за пределами компактного контейнера карточки. Локальная модель сама нашла изящное решение: она обнаружила, что ссылки на профили магазинов в коде страницы имеют жесткий параметр привязки к карточке (например, ?iid=, где — это уникальный ID объявления). Скрипт, тут же совершенно самостоятельно оптимизированный моделью, стал снимать сквозной текстовый дамп страницы, находить совпадения по iid и склеивать пары «товар + цена + город + ссылка на продавца» со 100% точностью, полностью исключая смещение строк при выводе отчета.

В итоге я практически занял позицию наблюдателя, который просто сидел и смотрел на то, как «художник творит», получив в конце работы модели спарсенные объявления и ссылки!

Универсальный промпт-контейнер для запуска ИИ-агента.

В результате «родился» простой и универсальный промпт-контейнер для запуска ИИ-агента под описанную задачу. Чтобы ваша локальная (или облачная) модель сразу поняла этот метод и применила его к любому другому сложному сайту, не сваливаясь в requests и не пытаясь скачать встроенный Chromium (что часто ломает окружение), вы можете использовать следующий проверенный промпт-контейнер:

Запусти режим «Визуально-текстового DOM-агента прямого действия». Твоя задача — найти [что ищем] на сайте [имя сайта здесь].

Действуй строго по алгоритму:

  1. Не используй requests и не пытайся скачивать встроенные браузеры библиотеки Playwright.

  2. Запускай браузер в видимом режиме (headless=False), жестко прописав использование уже установленного Edge через параметр: channel=“msedge” (это отменит скачивание Chromium из сети).

  3. Чтобы окно не оставалось свернутым в трее, обязательно передай в args запуска браузера флаги: “–start-maximized” and “–focus”, а после создания страницы вызови метод page.bring_to_front(), чтобы принудительно поднять окно на передний план экрана поверх всех окон.

  4. Сформируй поисковый URL с зашитым запросом внутри ссылки, чтобы открыть страницу выдачи напрямую.

  5. Выдержи паузу в 8 секунд после загрузки для обхода систем защиты и Cloudflare.

  6. Сделай скриншот для визуального контроля и OCR-анализа.

  7. Игнорируй хэшированные CSS-классы. Собирай данные (название, цена, ссылка) через проход по структуре DOM, зацепляясь за текстовые маркеры (знаки валют, паттерны ID карточек).

  8. Если данные разнесены в DOM, сними сквозной текстовый дамп страницы и сопоставь блоки по индексам (например, через Item ID объявления).

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

Результаты: Что в итоге принес мой «автономный робот»?

Несмотря на то, что в процессе плотной интерактивной отладки скрипта целевой сайт в итоге выдал временное ограничение “429” на шестом заходе, модель проявила высокий уровень автономности. Она не растерялась, заблокировала новые сетевые запросы (пока “отвиснет” бан по IP) и собрала финальный структурированный отчет из уже сохраненных в памяти локальных буферов данных.

Самое интересное: ИИ-агент проявил элементы критического мышления. В одном из объявлений в заголовке было указано топовое название видеокарты, но модель просканировала DOM-текст описания и обнаружила строчку продавца: «Видеокарта: младшая серия на 16 гб». Модель сама вынесла в отчет предупреждение: «Внимание, противоречие в спецификации заголовка и описания, уточнять у продавца». Тем самым модель дала мне понять, что объявление мне не подходит даже при формальном “попадании” в запрос.

Заключение.

Ценность этого эксперимента — не конкретные файлы скриптов или json-логи, оставшиеся на диске, и не «изобретение велосипеда». Да и любой профессионал нашел бы куда как более серьезное решение. Главное тут — это доказательство концепции (PoC). Локальная модель класса 7B/8B уже легко способна выступать полноценным автономным ИИ-оператором вашего компьютера!

Объединяя «глаза» (визуальный контроль через скриншоты) и «руки» (управление DOM-деревом через системный браузер), локальная LLM становится практически неуязвимой для стандартных сетевых блокировок. Она может “видеть” интернет точно так же, как его видит живой пользователь.

И это всё больше приближает LLM к той самой, сакраментальной концепции, из слов одной легендарной электронной композиции: «Я твой слуга, я твой рАботник»*…

ссылка на оригинал статьи https://habr.com/ru/articles/1104808/