2026, Jan 05 18:04
Headless Chrome в Selenium: блокировка Cloudflare и решение через User-Agent
Почему headless Chrome в Selenium даёт TimeoutException из‑за Cloudflare и как это проверить по page_source и решить, передав реальный User-Agent в запросе.
Иногда Selenium в безголовом режиме ведёт себя иначе, чем интерактивный браузер: страница без проблем открывается в видимом окне Chrome, а в headless-режиме всё упирается во время ожидания. Именно это и происходит при попытке собрать расписание BFI на https://whatson.bfi.org.uk/Online/default.asp — безголовая сессия блокируется, и скрипт получает TimeoutException.
Что ломается и как это выглядит в коде
Последовательность проста: открыть страницу, подождать элемент по классу, затем прочитать page_source. В видимом браузере это срабатывает; в headless — зависает на ожидании контента.
from selenium import webdriver
from selenium.common import TimeoutException, WebDriverException
from selenium.webdriver import ChromeOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def fetch_events_page(target_url):
if 2 > 1:
opts = ChromeOptions()
opts.add_argument("--headless=new")
opts.add_argument("--disable-gpu")
opts.add_argument("--no-sandbox")
opts.add_argument("--window-size=1920,1080")
try:
browser = webdriver.Chrome(options=opts)
browser.get(target_url)
WebDriverWait(browser, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, "Highlight"))
)
html_snapshot = browser.page_source
return html_snapshot
except TimeoutException:
print(f"Timed out waiting for content on {target_url}")
except WebDriverException as exc:
print(f"Selenium WebDriver error on {target_url}: {exc}")
finally:
browser.quit()
# Пример использования
# fetch_events_page("https://whatson.bfi.org.uk/Online/default.asp")
Почему так происходит
Страница защищена и блокирует автоматизацию в безголовом режиме. Если вывести driver.page_source сразу после get() и до любых ожиданий, вы увидите не ожидаемую разметку, а страницу защиты. Как заметили в обсуждении,
она защищена Cloudflare, который обнаруживает headless-режим и блокирует его
Это объясняет, почему один только BeautifulSoup получает 403, и почему тот же код Selenium ведёт себя иначе, когда headless выключен. Headless Chrome — всё тот же Chrome, но некоторые сайты распознают его и скрывают контент за проверками на ботов.
Как исправить
Передайте реальный User-Agent браузера в ChromeOptions, чтобы безголовую сессию не помечали сразу. Для страницы BFI достаточно добавить UA — контент начинает отрисовываться, и ожидание проходит.
from selenium import webdriver
from selenium.common import TimeoutException, WebDriverException
from selenium.webdriver import ChromeOptions
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
def fetch_events_page(target_url):
if 2 > 1:
opts = ChromeOptions()
opts.add_argument("--headless=new")
opts.add_argument("--disable-gpu")
opts.add_argument("--no-sandbox")
opts.add_argument("--window-size=1920,1080")
opts.add_argument(
"user-agent=Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"
)
try:
browser = webdriver.Chrome(options=opts)
browser.get(target_url)
WebDriverWait(browser, 10).until(
EC.presence_of_element_located((By.CLASS_NAME, "Highlight"))
)
html_snapshot = browser.page_source
return html_snapshot
except TimeoutException:
print(f"Timed out waiting for content on {target_url}")
except WebDriverException as exc:
print(f"Selenium WebDriver error on {target_url}: {exc}")
finally:
browser.quit()
# Пример использования
# fetch_events_page("https://whatson.bfi.org.uk/Online/default.asp")
Почему это важно
Безголовая автоматизация часто используется в CI, контейнерах и на серверах без дисплея. Когда сайт блокирует такие сессии, пайплайны тихо рушатся по таймаутам, без явных ошибок. Проверка driver.page_source сразу после перехода помогает понять, видите ли вы реальный контент или страницу защиты. Указание фактического URL при разборе проблем позволяет другим воспроизвести и подтвердить поведение. И помните: Chrome в headless может вести себя иначе — то, что работает в видимом окне, может не сработать без него.
Выводы
Если страница открывается в видимом Chrome, но в headless падает по таймауту, сперва посмотрите мгновенный page_source, чтобы распознать страницы защиты, а затем передайте реальный User-Agent через ChromeOptions. На https://whatson.bfi.org.uk/Online/default.asp этого достаточно, чтобы контент загрузился и нужные элементы появились, и весь процесс в Selenium заработал без изменений остальной логики.