Привет, Хабр. Вряд ли эта статья откроет Америку для прожженных разработчиков "парсеров" или тестировщиков, но должна помочь тем, кто работает с Selenium время от времени.

Если вы иногда занимаетесь автоматизированным сбором информации с веб-ресурсов (т.н. веб-скрапинг или парсинг), то однозначно сталкивались с проблемой, что некоторые ресурсы недоступны из некоторых регионов, по разным причинам. Сейчас этой проблемы стало даже чуточку больше. И для преодоления этого препятствия нам помогают прокси-серверы (промежуточные серверы). Однако, если в части библиотек для работы с запросами использование прокси является очевидным решением, идущим из коробки, то такой инструмент как Selenium очевидного встроенного решения или не имеет, или я не смог его обнаружить. Я не смог его обнаружить, в том числе, и на Хабре, поэтому решил это исправить.

Далее приведу некоторые примеры с использованием Python, но привязки к языку не будет. Решение универсальное, связанное с написанием расширения для веб-драйвера.

В Python для работы с HTTP-запросами любого уровня сложности вполне достаточно встроенной библиотеки Requests. А для использования прокси с библиотекой, достаточно всего лишь передать данные о сервере в качестве аргумента. Например, так:

import requests

proxies = {
    'http': 'http://login:password@ip:port',
    'https': 'http://login:password@ip:port'
}

url = "https://badsite.com/"

response = requests.get(url, proxies=proxies)

Но для работы со многими современными веб-ресурсами этой библиотеки недостаточно. Например, она не поддерживает JS и нужные данные просто не всегда подгружаются, а ответ приходит неполный. В таких случаях и нужна связка с Selenium, который автоматизирует браузер, может обработать любую веб-страницу, обойти защиту от роботов и так далее.

Разумеется, если вы попробуете открыть с помощью Selenium ресурс, который недоступен из вашего региона, ничего не выйдет. Можно придумать много вариантов решения, но как по мне, самый удобный способ настройки прокси в Selenium- это написать прокси-расширение. В моем случае это будет расширение для веб-драйвера Chrome.

Допустим, у вас есть проект с логикой работы Selenium для получения HTML в отдельном файле main.py. Теперь в директории проекта создадим папку proxi_extension и добавим в неё два файла:

  1. manifest.json с таким наполнением:

{
    "version": "1.0.0",
    "manifest_version": 2,
    "name": "Chrome Proxy",
    "permissions": [
        "proxy",
        "tabs",
        "unlimitedStorage",
        "storage",
        "<all_urls>",
        "webRequest",
        "webRequestBlocking"
    ],
    "background": {
        "scripts": ["background.js"]
    },
    "minimum_chrome_version": "22.0.0"
}
  1. background.js с таким наполнением:

var config = {
    mode: "fixed_servers",
    rules: {
        singleProxy: {
            scheme: "http",
            host: "ip_address",
            port: parseInt("8000")
        },
        bypassList: ["localhost"]
    }
};

chrome.proxy.settings.set({value: config, scope: "regular"}, function() {});

function callbackFn(details) {
    return {
        authCredentials: {
            username: "login",
            password: "password"
        }
    };
}

chrome.webRequest.onAuthRequired.addListener(
    callbackFn,
    {urls: ["<all_urls>"]},
    ['blocking']
);

В background.js необходимо лишь заменить значения host, port, username и password на нужные.

Далее возвращаемся к файлу main.py и подключаем к веб-драйверу написанное расширение, указав путь к нему в соответствующем аргументе:

from loggers import logger #  уже настроенный логгер
from selenium import webdriver
from selenium.webdriver.chrome.options import Options


def get_html(url: str) -> str:
    """
    Возвращает HTML.
    """
    chrome_options = Options()
    chrome_options.add_argument(f"--load-extension={'/home/username/my_project/proxi_extension'}") #  подключаем расширение с прокси
    driver = webdriver.Chrome(options=chrome_options)
    driver.set_page_load_timeout(5)

    try:
        driver.get(url)
    except Exception as e:
        logger.error(e)

    finally:
        html = driver.page_source
        driver.quit()

        return html

Вот и весь способ. Теперь автоматизированный Chrome будет открывать всё, что не заблокировано в регионе, в котором установлен прокси-сервер.

Надеюсь, что я не побаловал вас диким баяном и кому-то, да помог. Хорошего дня.

Комментарии (5)


  1. GalWat
    07.08.2024 14:02
    +2

    Кажется, что самым простым способом решения данной проблемы (если нет большого багажа старого кода) является использование playwright :)

    В работе они очень сильно похожи, даже были на хабре статьи, как автоматически сконвертировать тесты с селениума на playwright

    Бонусом идет избавление еще от некоторого числа костылей, которые требуются селениуму, чтобы адекватно работать


    1. it_police Автор
      07.08.2024 14:02

      Мне кажется об этом решении могут знать только тестировщики. Для разрабов мой вариант должен быть топом)) Кстати, его можно упростить, и в аргумент к настройке веб-драйвера передать просто строку с учетными данными прокси:

      proxy_user = "USERNAME"
      proxy_password = "PASSWORD"
      proxy_host = "PROXY_HOST"
      proxy_port = "PROXY_PORT"
      
      # Настройка прокси с учетом учетных данных
      proxy = f"http://{proxy_user}:{proxy_password}@{proxy_host}:{proxy_port}"
      
      chrome_options = Options()
      chrome_options.add_argument(f'--proxy-server={proxy}')

      Но я решил описать способ, который работает на 200%


    1. pvzh
      07.08.2024 14:02

      Поддержу, достойная библиотека, мощные спонсоры, качественная поддержка Пайтона, подробная дока, настройка прокси там описана и легко находится. Давно уже пора забыть про Селениум, но нет, куча пересказов устаревших статей, обрывки примеров, как же это всё бросить...


      1. it_police Автор
        07.08.2024 14:02

        Спасибо хабру и сообществу, я первый раз услышал об этой библиотеке. Крутая штука. Даже асинхронный API поддерживает.


  1. Sigest
    07.08.2024 14:02
    +1

    Такая же проблема была у меня под java/kotlin при работе selenium+chrome. Пришлось писать прокси обертку. Но больше всего удивило, что казалось бы такая стандартная потребность как прокси должно быть из коробки, а ее блин нет