Selenium управляет настоящим браузером. В этом и состоит весь смысл его использования для скрапинга: когда страница формирует содержимое с помощью JavaScript после загрузки начального HTML, простой HTTP-запрос возвращает пустую оболочку, а браузер выполняет скрипты и отдаёт готовый DOM. Расплата за это: теперь для каждой страницы запускается Chrome, что медленнее и тяжелее, чем простое получение HTML. Данное руководство строит рабочий Selenium-скрапер на актуальной основе (Selenium 4), а затем показывает, когда управление браузером собственными силами перестаёт себя оправдывать.
Разрыв версий здесь важен. Большинство учебников по Selenium всё ещё обучают паттернам Selenium 3: find_element_by_id, вручную загружаемые бинарники ChromeDriver, аргументы executable_path. Всё это удалено или объявлено устаревшим. Selenium 4 заменил вспомогательные методы поиска единым API локаторов By, а Selenium Manager (встроенный с версии 4.6) теперь автоматически определяет нужный драйвер для установленного браузера, поэтому шаг загрузки драйвера, с которого открывается большинство руководств, исчез. Всё ниже написано для этой актуальной версии.
Что вам понадобится
Три вещи: Python 3.8 или новее, установленный Google Chrome и пакет Selenium. Это всё. Вам не нужно загружать ChromeDriver и не нужен webdriver-manager при современной установке, потому что Selenium Manager справляется с драйвером самостоятельно.
# Selenium 4.6+ ships Selenium Manager, which resolves # the matching driver for your installed Chrome. pip install selenium
Если вы видели webdriver-manager в старых руководствах, он вам больше не нужен. Он решал ту же задачу, что теперь решает Selenium Manager нативно. Оставляйте его только в легаси-коде, который уже от него зависит; для всего нового достаточно установки одного selenium.
Запуск Chrome в headless-режиме
Selenium 4 настраивает браузер через объект Options, передаваемый в драйвер. Для скрапинга почти всегда нужен headless-режим (без видимого окна), а также реальный размер окна и user agent, поскольку некоторые сайты ведут себя иначе, когда окно просмотра крошечное или агент кричит об автоматизации.
from selenium import webdriver from selenium.webdriver.chrome.options import Options options = Options() options.add_argument("--headless=new") options.add_argument("--window-size=1920,1080") options.add_argument("--disable-gpu") options.add_argument( "user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) " "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36" ) # No driver path needed. Selenium Manager resolves it. driver = webdriver.Chrome(options=options)
Если вам всё же нужно зафиксировать конкретный бинарник драйвера, это теперь делается через объект Service (webdriver.Chrome(service=Service("/path/to/chromedriver"), options=options)), а не через удалённый аргумент executable_path. Для большинства пользователей правильный выбор предоставить Selenium Manager выполнять свою работу.
Открытие страницы и поиск элементов
Имея драйвер, driver.get(url) выполняет навигацию и блокирует выполнение до срабатывания начальной загрузки. Затем элементы находятся через API By. Это наиболее существенное изменение по сравнению с Selenium 3: все вспомогательные методы find_element_by_* исчезли, заменённые на find_element(By.X, value).
from selenium.webdriver.common.by import By driver.get("https://quotes.toscrape.com/") # One element, then many. title = driver.find_element(By.TAG_NAME, "h1").text quotes = driver.find_elements(By.CLASS_NAME, "quote") for q in quotes: text = q.find_element(By.CLASS_NAME, "text").text author = q.find_element(By.CLASS_NAME, "author").text print(author, "-", text)
Наиболее нужные локаторы: By.ID, By.CLASS_NAME, By.CSS_SELECTOR и By.XPATH. CSS-селекторы покрывают большинство потребностей и читаются чисто; XPath используйте только когда нужно сопоставить по текстовому содержимому или перемещаться вверх к родителю, что CSS не умеет. Обратите внимание: find_element выбрасывает исключение, если ничего не найдено, а find_elements возвращает пустой список, поэтому перебирайте форму множественного числа и проверяйте длину, вместо того чтобы оборачивать одиночные поиски в try/except.
Ожидание динамического контента
Вот ошибка, которая ломает большинство начинающих Selenium-скраперов: вызов find_element сразу после возврата get(). На странице с JavaScript-отрисовкой нужный элемент может ещё не существовать, и вы получаете NoSuchElementException на странице, которая нормально загружается при визуальном просмотре. Исправление: явное ожидание, которое опрашивает состояние до выполнения условия или истечения таймаута.
Не тянитесь к time.sleep(). Фиксированный сон либо тратит время, когда страница быстрая, либо даёт сбой, когда она медленная; явное ожидание возвращает результат в момент готовности элемента и истекает только если тот действительно так и не появился.
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC wait = WebDriverWait(driver, 10) # Wait until at least one .quote is present in the DOM. wait.until( EC.presence_of_element_located((By.CLASS_NAME, "quote")) ) # Now it is safe to read the rendered content. quotes = driver.find_elements(By.CLASS_NAME, "quote")
Используйте presence_of_element_located, когда нужно лишь присутствие элемента в DOM, и visibility_of_element_located или element_to_be_clickable, когда вы собираетесь читать текст или кликать. Локатор передаётся в виде кортежа, что легко перепутать: это ((By.CLASS_NAME, "quote")), один аргумент-кортеж, а не два.
Обработка пагинации
Большинство реальных целей распределяют данные по страницам. Паттерн: цикл, в котором вы скрапите текущую страницу, находите элемент управления следующей страницей, кликаете по нему, ждёте нового контента и повторяете, пока элемент не исчезнет. Ловушка: объект страницы устаревает после навигации, поэтому находите кнопку «далее» заново на каждой итерации, а не храните ссылку на неё.
from selenium.common.exceptions import NoSuchElementException all_quotes = [] while True: wait.until( EC.presence_of_element_located((By.CLASS_NAME, "quote")) ) for q in driver.find_elements(By.CLASS_NAME, "quote"): all_quotes.append(q.find_element(By.CLASS_NAME, "text").text) try: next_btn = driver.find_element(By.CSS_SELECTOR, "li.next a") except NoSuchElementException: break # last page reached next_btn.click() print("scraped", len(all_quotes), "quotes") driver.quit()
Всегда вызывайте driver.quit() по завершении. Он закрывает браузер и процесс драйвера; driver.close() закрывает только текущее окно и оставляет процесс запущенным, что в цикле быстро приводит к утечке экземпляров Chrome. Для страниц с бесконечной прокруткой вместо кнопки «далее» эквивалентом является прокрутка через driver.execute_script("window.scrollTo(0, document.body.scrollHeight)") в цикле с ожиданием, пока количество элементов перестанет расти.
Маршрутизация Selenium через прокси
При скрапинге любой цели в объёме с одного IP вы получите ограничение скорости или блокировку. Маршрутизация браузера через прокси-сервер распределяет запросы по разным адресам, так что ни один не достигает лимита. Простейшая форма передаёт прокси как аргумент Chrome.
options = Options() options.add_argument("--headless=new") options.add_argument("--proxy-server=http://proxy.example.com:8080") driver = webdriver.Chrome(options=options) driver.get("https://httpbin.org/ip") print(driver.find_element(By.TAG_NAME, "body").text)
Это работает для прокси без аутентификации. Сложность в том, что флаг Chrome --proxy-server не принимает логин и пароль в URL, поэтому прокси с учётными данными требует расширения браузера, внедряющего заголовок аутентификации, или локального ретранслятора, хранящего учётные данные. Это один из неудобных углов прямого управления Chrome. Ротирующий эндпоинт с аутентификацией по токену обходит проблему: вы направляете Selenium на один хост, а ротация и доверие находятся за ним. Компромиссы между управлением собственным пулом и использованием управляемого эндпоинта разбираются в статье backconnect-прокси против crawling API, а если ваши цели хорошо защищены, датацентровые против жилых прокси объясняет, какой тип IP вам действительно нужен.
Когда браузер является неправильным инструментом
Selenium правильный выбор, когда страница действительно требует браузера: тяжёлая клиентская отрисовка, контент, доступный только после взаимодействия, или рабочие процессы, зависящие от кликов и ввода. Он является неправильным инструментом, когда к нему тянутся по привычке. Если данные находятся в начальном HTML, requests плюс парсер на порядок быстрее и легче. А как только цель начинает активно противодействовать с помощью anti-bot-защиты, сырой headless-браузер всё равно получает отпечаток и блокируется, и вы в итоге вручную перестраиваете ротацию, повторные попытки и маскировку.
| Approach | Renders JS | Скорость / стоимость | Best for |
|---|---|---|---|
| requests + парсер | No | Fastest, lightest | Данные в исходном HTML, без JS |
| Selenium | Да (реальный браузер) | Slow, heavy | Рендеринг JS, клики, формы, авторизация |
| Управляемый crawling API | Yes (server-side) | Один запрос, без инфраструктуры | Защищённые цели, масштаб, без парка браузеров |
Третья строка описывает место большинства боевых скраперов. Управляемый crawling API отрисовывает страницу на стороне сервера, ротирует IP, повторяет запрос при блокировках и возвращает готовый HTML из одного запроса, поэтому вы никогда не запускаете и не закаляете собственный парвк браузеров от отпечатков. Selenium оставляйте для реальных взаимодействий, а высокообъёмную работу, которая блокируется, делегируйте API.
Когда страница требует JavaScript, но вы предпочитаете не запускать парк браузеров, Crawling API отрисовывает её на стороне сервера, ротирует IP и повторяет запрос при блокировках, возвращая готовый HTML из одного запроса. Отправьте URL с &javascript=true и полностью избегите headless-инфраструктуры. Опробуйте на бесплатном уровне против вашей реальной цели.
Вызов представляет собой один HTTP-запрос: нет драйвера, нет явных ожиданий, нет танцев с аутентификацией прокси. Просто отправьте токен и целевой URL, запросите рендеринг и считайте тело ответа.
import requests resp = requests.get( "https://api.crawlbase.com/", params={ "token": "_YOUR_TOKEN_", "url": "https://quotes.toscrape.com/js/", "javascript": "true", # render the page server-side }, ) print(resp.status_code) print(resp.text[:500])
Тот же отрисованный результат, что вы получили бы от Selenium, без браузера, явных ожиданий или обходных путей с аутентификацией прокси. Для более глубокого сравнения управления собственным пулом с использованием эндпоинта, берущего на себя всю работу, смотрите лучшие прокси для веб-скраперов.
Ключевые выводы
-
Используйте паттерны Selenium 4. API локаторов
Byзаменил все вспомогательные методыfind_element_by_*, а Selenium Manager разрешает драйвер, поэтому шаг ручной загрузки исчез. - Настраивайте браузер через Options. Headless-режим, реальный размер окна и user agent являются базовыми параметрами для скрапинга.
-
Ждите явно, никогда не спите.
WebDriverWaitв сочетании сexpected_conditionsвозвращает результат в момент готовности контента и является исправлением для гонок при JS-отрисовке. -
Повторно ищите элементы после навигации. Ссылки устаревают после смены страниц; заново запрашивайте кнопку следующей страницы в каждом цикле и вызывайте
driver.quit()в конце. -
Браузер не всегда является ответом. Используйте
requests, когда данные находятся в HTML, и управляемый crawling API, когда цели хорошо защищены или нужен масштаб без парка браузеров.
Часто задаваемые вопросы
Нужно ли загружать ChromeDriver для Selenium 4?
Нет. Начиная с Selenium 4.6, встроенный Selenium Manager определяет установленный Chrome и автоматически загружает соответствующий драйвер, поэтому достаточно простой команды pip install selenium. Путь к драйверу указывается через объект Service только когда нужно намеренно зафиксировать конкретный бинарник; старый аргумент executable_path удалён.
Почему find_element выбрасывает NoSuchElementException на странице, которая нормально загружается?
Потому что элемент отрисовывается JavaScript после начальной загрузки, а ваш код запросил DOM раньше, чем он появился. Замените немедленный поиск явным ожиданием: WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.CLASS_NAME, "quote"))). Ожидание опрашивает состояние до появления элемента или истечения таймаута, что устраняет как фиксированные задержки, так и гонки состояний.
Что лучше использовать для веб-скрапинга: Selenium или BeautifulSoup?
Используйте requests с парсером вроде BeautifulSoup, когда данные присутствуют в начальном HTML страницы; это несравнимо быстрее и легче, поскольку браузер не запускается. Используйте Selenium, когда контент отрисовывается JavaScript или нужно кликать, вводить текст, прокручивать или входить в систему. Многие скраперы комбинируют оба подхода: Selenium отрисовывает страницу, затем BeautifulSoup разбирает полученный HTML.
Как использовать аутентифицированный прокси с Selenium?
Аргумент Chrome --proxy-server не принимает логин и пароль в URL, поэтому прокси с учётными данными требует расширения браузера, внедряющего заголовок аутентификации, или локального ретранслятора, хранящего учётные данные. Ротирующий эндпоинт с аутентификацией по токену решает проблему: вы направляете Selenium на один хост, а учётные данные хранятся за ним, а не в флагах запуска.
Хорошо ли Selenium подходит для масштабного скрапинга?
Масштабируется он плохо. Каждая страница запускает полноценный браузер, что медленно и требовательно к памяти, а на хорошо защищённых целях сырой headless-браузер всё равно получает отпечаток и блокируется. Для большого объёма управляемый crawling API с серверной отрисовкой, ротацией IP и повторными попытками при блокировках обычно является лучшим выбором, оставляя Selenium для реальных взаимодействий.
В чём разница между driver.close() и driver.quit()?
Метод driver.close() закрывает текущее окно браузера, но оставляет процесс драйвера и все другие окна запущенными. Метод driver.quit() закрывает каждое окно и завершает процесс драйвера. В цикле скрапинга всегда завершайте работу вызовом driver.quit(), иначе осиротевшие процессы Chrome накапливаются и исчерпывают память.
Обходите любой сайт в масштабе, без борьбы с инфраструктурой.
Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.

