ХабрПостыНовостиМегапосты?

Используем Proxy в Selenium

Привет, Хабр. Вряд ли эта статья откроет Америку для прожженных разработчиков "парсеров" или тестировщиков, но должна помочь тем, кто работает с 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 будет открывать всё, что не заблокировано в регионе, в котором установлен прокси-сервер.

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

@it_police
07.08.2024 14:36 UTC
Первоисточник

Комментарии

@GalWat
07.08.2024 14:02 UTC
+2

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

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

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

07.08.2024 14:07 UTC
0

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

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%

07.08.2024 19:57 UTC
0

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

08.08.2024 08:55 UTC
0

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

@Sigest
08.08.2024 04:59 UTC
+1

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

@imba
11.08.2024 04:57 UTC
+1

Это конечно интересно но "парсинг" и "эмулация деиствии пользователя" - это разные задачи и требуют разных инструментов.
1) Если требуется рендеринг js, то есть куча расширении бублиотек парсинга для этого. У Requests есть Requests-HTML (Full JavaScript support ), у BS4 тоже можно запустить рендеринг js.
2) Но если требуется 1, то с вероятностью 100% на саите есть вебхуки, которые отдадут эту информауию без рендеринга и в удобном json формате. В 90% случаев для обхода защиты хука достаточно базовых знании как работае веб. Наити хуки можно в тех же devtools на вкладке network, для облегчения можно фильтр fetch/xhr применить.

Selenium - это инструмент эмуляции а не парсинга.
Правильно выбраите инструменты , тренируите вашего змея и да прибудет с вами сила

11.08.2024 09:11 UTC
0

Аригато, сенсей! Но bs4 в рендер не умеет, на сколько знаю, только в связке с чем-то. С вебхуками в некоторых современных ресурсах тоже не всё так просто, например в coingecko com. Проще подгрузить страницу целиком.

11.08.2024 10:34 UTC
0

С bs4 , и в правду могу ошибаться, пользовался им в чистом виде давно, где-то в его документации видел такое упоминание.
Но все-таки, расковырять хук гораздо выгоднее, хоть возможно и дольше. Кроме всего прочего, это может позволить запросить бек кастомным запросом (бывает очень полезно/удобно). Если параметры будут не дико разными с типовыми запросами, то тебя не спалят довольно долго.

Селениум в парсинге, скорее жест отчаяния. Даже неудачное ковыряние хуков, дает понимани общих принципов работы. Поэтому я написал, что в 90% случаев селениум "без труда" заменяем и ненужен, требуется чуть поковырять и посмотреть какая защита у хука и как ее проити/получить аворизацию.
Чем больше практики в ковырянии хуков, тем реже нужен селениум. )))

11.08.2024 10:56 UTC
0

Абсолютно так. Ковырять хуки- это прекрасно, интересно, и полезно, но не для всех задач годится. Современные разрабы, которые предоставляют платное API своего ресурса, возможность работы с хуками максимально усложняют. Как пример, названный выше ресурс.

А что селениум в парсинге- это жест отчаяния, звучит прикольно)) Но на сколько мне известно, практика широко распространена даже в серьёзных командах, так что позволю не согласиться. Для этого может быть множество причин, не только js, но и получение кукис... Однако, прежде чем доходить до селениума, нужно попробовать всё остальное, так как он и по ресурсам требователен, и скорость работы оставляет желать лучшего. Но в связке с другими инструментами он всё же необходим, по моему скромному мнению.