Разработчики парсеров давно заметили, что наиболее сложные системы защиты целевых сайтов научились определять автоматический трафик, даже если скрипт использует headless-браузеры и веб-драйверы. Частичным решением стали готовые реализации stealth-плагинов и библиотек типа nodriver. Ниже постараемся в деталях разобраться с тем, как и на основе чего выявляются и блокируются автоматические запросы, а также в том, как обойти WAF без готовых решений.
Что такое брандмауэр веб-приложений (WAF)?
Брандмауэр веб-приложений (Web Application Firewall или WAF) — это программный комплекс, который защищает сайты и веб-приложения от атак, направленных на уязвимости в их коде, архитектуре или логике работы. В отличие от классического сетевого брандмауэра, который фильтрует трафик по IP-адресам, портам и протоколам, WAF анализирует HTTP(S)-запросы и ответы максимально глубоко, фактически отслеживая то, что происходит на уровне приложений (на прикладном уровне по модели OSI).
WAF позволяет обнаружить и предотвратить попытки эксплуатации уязвимостей, таких как:
- SQL-инъекции;
- XSS (межсайтовый скриптинг);
- атаки с подменой cookie или сессий;
- внедрение вредоносных команд через формы и API-запросы;
- попытки обхода механизмов аутентификации.
WAF может взять на себя не только мониторинг (когда софт просто фиксирует подозрительные запросы), но и активную блокировку (когда брандмауэр останавливает вредоносный трафик). Многие продвинутые WAF-комплексы умеют обучаться в процессе работы — они анализируют поведение пользователей и статистику запросов для более тонкой настройки правил защиты.
Современные решения WAF часто внедряются в составе систем облачной безопасности или CDN-платформ (например, как в случае с Cloudflare), интегрируются с SIEM-системами (Security Information and Event Management), а также используют искусственный интеллект и машинное обучение для выявления аномалий.
Но нам в контексте статьи более интересно применение WAF при борьбе со скрапингом. А если ещё точнее — как обойти WAF при парсинге.
Почему и как WAF блокирует веб-скрапинг

Во-первых, многие сайты и веб-приложения намеренно блокируют ботов и любой автоматический трафик, который создаёт паразитную нагрузку. Никому не хочется переплачивать за хостинг. Да и при определённой нагрузке сайт может замедлить свою работу, соответственно, реальным «живым» клиентам пользоваться им станет невозможно — примерно, как при DDoS-атаках.
Во-вторых, WAF анализирует подозрительные действия со стороны клиентов и может ошибочно определить запросы от парсеров как вредоносные или опасные для сайта. Например, если запросы с одного IP-адреса слишком частые или бот пытается заполнить формы-ловушки.
В-третьих, автоматические запросы и парсинг могут противоречить правилам использования ресурса, а контент может быть защищен авторскими правами, соответственно, владельцы сайта могут предпринимать свои превентивные технические меры по его защите от считывания.
По каким именно признакам брандмауэр веб-приложений может определять парсеров и ботов:
- Aнализ сигнатур (специальных технических признаков и заголовков ботов, парсеров и headless-браузеров) — по аналогии с цифровыми отпечатками;
- Поведенческий анализ (он наиболее ресурсоёмкий, поэтому задействуется редко либо проводится по простейшим паттернам и правилам);
- Проверка «человечности» за счёт капчи и выполнения специальных JS-скриптов на стороне клиента;
- Анализ IP-адресов (репутация + расположение и тип).
Этические соображения

Этические соображения при обходе брандмауэра веб-приложений связаны с балансом между безопасностью, конфиденциальностью, свободой доступа и ответственностью за обработку данных пользователей/сайта (если они защищены авторскими правами). Ключевые аспекты:
- Разработчики парсеров должны учитывать, что WAF защищает сайт не только технически, но и юридически. Игнорирование фильтров, попытки обхода капчи или подмены заголовков могут нарушать правила использования ресурса и трактоваться как вмешательство в работу системы.
- WAF-байпас должен предпочитать работу через официальный API-интерфейс, если он имеется для партнёров сайта или сервиса.
- Не стоит создавать слишком сильную нагрузку на хостинг, отправляя одновременно большое количество запросов. Так вы можете нарушить работу сервиса.
- Нужно чётко отделять контент, хранение которого может нарушать чьи-то авторские права или нарушать конфиденциальность (речь уже о персональных данных пользователей).
Соблюдение этих правил позволит вам оставаться в нормальном правовом и этическом поле, не провоцируя блокировки и не нарушая баланс интересов между владельцем сайта и конечными пользователями.
Методы обхода WAF при веб-скрапинге с примерами кода

Что такое обход WAF (WAF-байпас) — это попытка обойти фильтры и алгоритмы блокировки брандмауэра веб-приложений, чтобы получить доступ к содержимому сайта или веб-сервиса в целях автоматизации действий, парсинга или для реализации других задач.
Чтобы обойти защиту WAF, достаточно знать и учитывать основные методы обнаружения автоматического трафика. Мы перечислили их выше. Например, если WAF блокирует серверные IP, нужно использовать резидентные или мобильные прокси, если анализирует куки, сохраняет токены сессий и проверяет наличие исполнения JavaScript, то логично организовать работу через headless-браузеры. Если детально изучает цифровые отпечатки и следы автоматизации, то можно задействовать систему реалистичных браузерных профилей в связке с антидетект-браузерами и т.п.
Ниже разберём ряд технических приёмов для конкретных узких задач парсинга.
Смена пользовательских агентов и заголовков
Значение заголовка с user-агентом должно быть реалистичным и в идеале должно соответствовать актуальным версиям популярных браузеров. Не менее важно подумать о естественности других заголовков: локаль, кодировка и т.п.
Пример случайных заголовков на Python:
import random
user_agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36',
' Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36'
]
headers = {
'User-Agent': random.choice(user_agents), # Здесь выбирается случайный юзер-агент из списка выше
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', # показываем какие медиа-типы (форматы содержимого) браузер предпочитает получать в ответ и с каким приоритетом/качеством
'Accept-Language': 'en-US,en;q=0.5', # Предпочитаемый язык
'Accept-Encoding': 'gzip, deflate', # Предпочитаемые варианты сжатия
'Referer': 'https://www.google.com/' # Кто выступил в роли источника перехода
}
Если вы подключаетесь через headless-браузеры, то нужно подумать ещё и об отключении определённым атрибутов, а также о более «естественных» настройках контекста: размер окна браузера, используемые шрифты и т.п. Пример указания таких настроек смотрите ниже — в блоке с ротацией прокси.
Использование резидентных и ротационных прокси
Если у вас разрозненные списки прокси, то их нужно ротировать своими силами. Например, так:
import requests
import random
proxies_list = [
'http://user:pass@192.168.1.1:8080',
'http://user:pass@192.168.1.2:8080',
'http://user:pass@192.168.1.3:8080'
]
proxy = {'http': random.choice(proxies_list), 'https': random.choice(proxies_list)}
response = requests.get('https://example.com', proxies=proxy, timeout=10) # Тут таймаут фиксированный, но так лучше не делать, код просто для примера и понимания
Но если вы задействуете сервис ротируемых прокси, такой как Froxy, то прокси в коде могут подключаться всего один раз, а дальнейшая ротация и правила подбора настраиваются в личном кабинете сервиса или по API.
Пример для подключения парсера через прокси с использованием headless-браузера и «человеческих» атрибутов без задействования Stealth-плагинов (работаем через веб-драйвер Playwright):
from playwright.sync_api import sync_playwright
def create_browser_with_residential_proxy():
with sync_playwright() as p:
# Конфигурация резидентного прокси
proxy_config = {
'server': 'http://proxy.froxy.com:9000', # или socks5:// для SOCKS5
'username': 'your_login',
'password': 'your_password'
}
# Запуск браузера с прокси
browser = p.chromium.launch(
headless=True,
proxy=proxy_config, # передаём конфигурацию прокси
args=[
# Скрываем атрибуты автоматизации
'--disable-blink-features=AutomationControlled',
'--no-sandbox',
'--disable-dev-shm-usage'
]
)
# Создание контекста с дополнительными настройками
context = browser.new_context(
viewport={'width': 1920, 'height': 1080}, # Реалистичное разрешение экрана
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' # Реалистичный юзер-агент
)
# Дополнительное скрытие признаков работы через веб-драйвер на уровне JS-кода на этапе инициализации браузера
page = context.new_page()
page.add_init_script("""
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined,
});
""")
return browser, page
# Использование
browser, page = create_browser_with_residential_proxy()
try:
# Переход на целевой сайт через прокси
page.goto('https://target-site.org/page1', timeout=60000)
# Дальнейшие действия скрапинга...
# data = page.locator('.content').text_content()
finally:
# Закрытие браузера
browser.close()
Правильное (естественное) управление файлами cookie и сеансами
Скрипт обхода WAF должен уметь сохранять и повторно использовать cookies для имитации реальной пользовательской сессии.
Пример на Python + Requests (подробнее о библиотеке Requests):
import requests
session = requests.Session()
# Первый запрос для получения cookies
session.get('https://example.com/login')
# Последующие запросы используют те же cookies
response = session.get('https://example.com/protected-page')
# Ручная установка cookies
session.cookies.update({'session_id': 'abc123', 'user_prefs': 'en'})
Headless-браузеры автоматически сохраняют куки в рамках одной сессии. Хотя вы можете настроить кастомную логику работы с ними и ручное добавление кук.
Добавление задержек и рандомизации
Человек никогда не выдерживает между своими действиями одинаковых интервалов. Поэтому элемент случайности в задержках между запросами крайне важен. Мы рекомендуем устанавливать лимиты времени — от … и до …, чтобы не получить слишком больших и слишком маленьких задержек.
import time
import random
def human_delay(min_delay=1, max_delay=5):
# Случайная задержка между запросами
time.sleep(random.uniform(min_delay, max_delay))
# Использование в цикле скрапинга
for page in range(10):
human_delay(2, 7) # Пауза от 2 до 7 секунд
# ... выполнение запроса
Скрытие атрибутов headless-браузеров (без стелс-библиотек)
Вариант маскировки признаков автоматизированного браузера через настройки Selenium (Python):
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
options = Options()
options.add_argument("--disable-blink-features=AutomationControlled")
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
options.add_argument('--disable-dev-shm-usage')
options.add_argument('--no-sandbox')
driver = webdriver.Chrome(options=options)
# Скрытие признаков веб-драйвера на уровне JS-кода
driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")
# Установка нормального user-agent
options.add_argument("--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
Как избежать подозрительных шаблонов запросов — дополнительные стратегии
Обозначим остальные подходы для обхода WAF, чтобы вы знали какие реализации и для чего можно использовать:
- Случайный фактор в последовательности действий, особенно в списке страниц на обход.
- Эмуляция человеческого поведения в браузере — шевеление курсором, естественный скроллинг страниц, случайные клики по элементам страницы, посимвольный ввод текста в формах и т.п.
- Интеграция с сервисами для решения капчи.
- Подбор естественных прокси — на базе адресов домашних или мобильных пользователей из разных подсетей и регионов — чтобы все прокси не были из одного кластера при частых обращениях, так как это очень сильно напоминает DDoS. Но вместе с тем, в некоторых случаях логично удерживать сессию максимально долго, а при ротации подбирать адреса в той же подсети или у того же оператора связи. Такой реконнект будет выглядеть весьма естественно, так как у многих ISP динамическая ротация IP-адресов.
- Проверка страниц и ответов сервера на наличие ошибок. Если сервер сообщает о превышении количества подключений, то достаточно уменьшить нагрузку и выждать немного времени. А если это ошибки доступа, свидетельствующие о блокировке, то пора ротировать IP и цифровой отпечаток.
- Исключение страниц для обхода на основе правил, которые сформулированы в robots.txt.
- Отключение WebRTC в настройках headless-браузера, а также генерация правдоподобных потоков на аудиоустройствах и веб-камере.
- Предварительное наполнение кук — можно организовать за счёт последовательного посещения случайного набора сайтов в сети (чтобы куки не были пустыми).
- Задействование кеширования картинок и статических файлов — чтобы исключить массовую повторную загрузку (тоже явный признак автоматизации).
Если вы знакомы с ИИ, то можно организовать полноценный пайплайн с привлечением LLM. Так вы сможете генерировать более естественное поведение и паттерны, а также цифровые отпечатки, которые сложно описать на уровне случайных значений и простого кода.
Пример скрапинга вместе с Chat GPT, а также материал о применении фреймворков LangChain и LangGraph.
Заключение и рекомендации

Как мы неоднократно уже говорили, сайты и механизмы их защиты становятся с каждым годом всё сложнее. Но в эту игру можно играть вдвоём: механизмы и способы обхода WAF (Web Application Firewall) тоже совершенствуются и не стоят на месте. При желании можно эмулировать абсолютно всё — от цифровых отпечатков и поведения пользователя до расположения клиента и аудиопотока с микрофона. Это как танцевать танго, двигаясь синхронно с партнёром.
Мы предоставляем качественные ротируемые прокси с точным таргетингом до конкретного населённого пункта и ISP-провайдера по всему миру, а также набор готовых облачных парсеров, работающих по API.

