Прокси-серверы буквально незаменимы в задачах, связанных с парсингом. Прокси могут решать сразу несколько задач: от обеспечения анонимности и защиты пользователя (его устройств), до масштабирования и распараллеливания процессов сбора данных.
Этот материал будет о том, как интегрировать между собой самописный софт на базе фреймворка Scrapy и ротируемые прокси. Начнём с теории.
Scrapy – это мощный фреймворк с открытым исходным кодом, который комплексно закрывает вопрос написания программ-парсеров для собственных нужд. Мы упоминали Scrapy в материале о библиотеках для Python. И это не случайно, так как фреймворк написан на Python.
Процесс создания скриптов здесь решается буквально в несколько строк кода. Более того, Scrapy поддерживает обслуживание параллельных потоков, умеет интегрироваться с готовыми сервисами парсинга по API (такими как Zyte), легко развёртывается в облаке и имеет огромное множество расширений.
По факту это готовый парсер, который при желании можно быстро адаптировать под себя и свои задачи. Он уже умеет выгружать данные в наиболее распространённых форматах (JSON, CSV и XML), управлять файлами Cookies, создавать очередь срейпинга и т.д.
Но самая интересная фишка Scrapy (Python) – прокси. Из коробки Скрепи умеет интегрироваться с популярными типами прокси: SOCKS и HTTP(S). Поддерживаются как статичные IP (v4/v6), так и ротируемые IP proxy.
Плюс Scrapy имеет встроенный API-интерфейс и может взаимодействовать с headless-браузерами, например, с Playwright, или с антидетектами.
Scrapy устанавливается через штатный пакетный менеджер pip командой:
pip install Scrapy
Если у вас используется дистрибутив Anaconda или Miniconda, то можно задействовать пакетный менеджер conda:
conda install -c conda-forge scrapy
Обратите внимание: при установке в Windows-среде нужно заранее установить среду Microsoft Visual C++. Она может потребоваться для отдельных функций.
Так как Scrapy зависит от некоторых других библиотек, их можно установить заранее: lxml, parsel, w3lib, twisted, cryptography и pyOpenSSL (хотя они обычно подтягиваются автоматически при установке через пакетный менеджер). Библиотеку Twisted нужно устанавливать с дополнительным параметром «TLS» для исключения некоторых распространённых ошибок:
pip install twisted[tls]
По факту парсер уже готов к работе. Достаточно воспользоваться встроенной командной оболочкой (официальная документация):
scrapy shell
После ввода команды откроется терминал Scrapy. Здесь уже можно скрейпить страницы. Например:
fetch(“https://scrapingclub.com/exercise/list_infinite_scroll/”)
Чтобы вывести полученный HTML-код, используйте команду:
print(response.text)
В запросах можно получать текст или содержимое конкретных тегов (в том числе на основе CSS-свойств). Но если для поиска элемента на странице нужно использовать сложную схему из тегов, классов и стилей, то следует задействовать синтаксис XPath. Например:
response.xpath("//div[@class='p-4']/h4/a/text()").extract()
Полноценное создание своих парсеров осуществляется через команду startproject, например:
scrapy startproject my-first-parser
В результате Scrapy создаст отдельный каталог проекта, где будут располагаться файлы настроек, пауков и т.п.
Как минимум, в проекте нужно создать своего первого паука, у которого указываются URL для парсинга и задания на обход (со схемами разметки).
Пример такого паука (создайте файл my_first_spider.py в папке my-first-parser/spiders):
# python 3import scrapyfrom urllib.parse import urljoinclass MySpider(scrapy.Spider):name = "firstparser"start_urls = ['https://scrapingclub.com/exercise/list_infinite_scroll/',]def parse(self, response):for post_link in response.xpath('//div[@class="p-4"]/h4/a/@href').extract():url = urljoin(response.url, post_link)print(url)
Останется запустить паука:
scrapy crawl firstparser
В нашем случае в консоли выведется список из URL-адресов, найденных в названиях товаров (но только на одной странице).
Ротируемые прокси – это прокси, которые могут заменять IP-адреса опорных точек в процессе работы. Ротация может происходить по разным условиям и причинам (триггерам).
Чаще всего под ротируемыми прокси принято подразумевать прокси с обратной связью (BackConnect-прокси, они же обратные прокси). Такие прокси работают следующим образом:
Тем не менее, ротация IP может происходить и по другим схемам. Например, статичные IP (серверные прокси) можно ротировать своими силами – с помощью скриптов или специальных прокси-менеджеров.
Некоторые программы сами умеют тестировать прокси и ротировать их на базе введённого списка.
Под определение ротируемых прокси подпадают: серверные, резидентные и мобильные прокси – если пул адресов большой, и они подключаются по схеме с обратной связью.
Если с форматом прокси мы определились, то осталось разобраться со способами подключения. В каждом случае есть свои плюсы и минусы. О них ниже.
В этом случае вы работаете на опережение – копируете сразу целый список прокси-серверов и подключаете их к своему скрипту.
Скрипт сам может тестировать прокси и включать их в работу, распределять по паукам (рабочим процессам). Ротацию можно проводить на своих условиях и по своим триггерам.
Фактически вы можете взять большой список статических прокси и сделать их ротируемыми.
Но есть и минусы:
BackConnect-прокси решают большинство обозначенных проблем. Всю работу по ротации, по анализу качества и расположения прокси, по их сортировке и т.п., берёт на себя удалённый сервер.
Вам выдаётся только адрес подключения (точка входа в прокси-сеть), вы настраиваете параметры подбора адресов и их ротации. Всё. Дальше нужно только работать.
Но и тут есть ряд спорных моментов:
К неоспоримым преимуществам можно отнести то, что к парсеру подключается всего один адрес прокси и никакой ротации в коде производить не нужно (по крайней мере в большинстве случаев).
Предположим, что конечный (выходной) IP работает стабильно, но блокируется конкретным сайтом или web-сервисов. Парсинг в этом случае невозможен.
В итоге вам нужно лично зайти в панель управления прокси и принудительно обновить IP. Но это не самый удобный вариант для автоматизированного скрипта парсинга.
Логично написать код, который бы обновлял «проблемный» IP без участия пользователя. Но не все прокси-сервисы имеют специальный интерфейс для взаимодействия с программами. Это так называемый API (сокращение от Application Programming Interface, дословно «программный интерфейс приложения»).
Ротация по API осуществляется специальными командами, которые предусмотрел прокси-сервис. У всех сервисов свой синтаксис. Плюс, чтобы не путать клиентов между собой, прямо в API-запросах передаётся специальный ключ, одновременно выступающий в роли идентификатора пользователя.
Вместо сложных конструкций обращения к серверу по API многие прокси-сервисы, предоставляют возможность обновления выходного IP-адреса по специальной ссылке. Достаточно перейти по ней или отправить запрос на этот адрес из парсера, и сработает триггер, который отвечает за ротацию прокси.
Из плюсов: адрес подключения к прокси может оставаться статичным (если используются обратные прокси и есть ссылка/API для обновления).
Если API проработано хорошо, то без участия пользователя можно автоматизировать практически любые действия: пополнение баланса, создание нового прокси-порта (фильтра) или его удаление, ротацию и т.п.
Свои ротируемые прокси в Scrapy можно организовать с помощью бесплатного дополнения scrapy-rotating-proxies (официальная страница на GitHub + документация).
Для того, чтобы интегрировать ротатор в свой парсер, нужно сначала установить пакет в менеджере pip:
pip install scrapy-rotating-proxies
После инсталляции нужно добавить список прокси в настройки парсера. Для Scrapy это файл settings.py.
Пример кода:
ROTATING_PROXY_LIST = ['Адрес.прокси1.com:8001','Адрес.прокси2.com:8002','Адрес.прокси3.com:8003','Адрес.прокси4.com:8004']
Список может быть любого размера. Главное, разделить записи запятыми, а после последней строки запятую нужно убрать (всё в соответствии с правилами оформления массивов Python).
Если вам удобнее выгружать списки прокси в текстовый файл, то подключить список можно так:
ROTATING_PROXY_LIST_PATH = '/папка/с/прокси/proxies.txt'
Обратите внимание на правильность указания пути – в Python отсчёт идёт от каталога, в котором исполняется скрипт. То есть вы можете использовать относительные пути, но сам список нужно поместить в папку внутри проекта со Scrapy.
Если вы используете и вызов ROTATING_PROXY_LIST_PATH, и ROTATING_PROXY_LIST, то вызов файла будет иметь наивысший приоритет.
Ротатор обязательно нужно добавить в загрузчик промежуточного ПО Scrapy (подробнее в документации о DOWNLOADER_MIDDLEWARES). Делается это таким кодом:
DOWNLOADER_MIDDLEWARES = {# какой-то код, если уже имеется'rotating_proxies.middlewares.RotatingProxyMiddleware': 610,'rotating_proxies.middlewares.BanDetectionMiddleware': 620,# другой код, для других модулей}
Всё, после этого запросы пауков Scrapy будут отправляться через прокси из списка.
Обратите внимание, если в вашем коде используются метаданные с параметром «proxy», то они обрабатываться ротатором не будут.
Поэтому, если вы хотите отключить прокси или использовать что-то определённое на уровне конкретного запроса, то нужно использовать конструкции:
request.meta['proxy'] = Nonerequest.meta['proxy'] = "<my-own-proxy>"
У плагина есть дополнительные настройки (указываются через файл settings.py):
Самый простой способ обращения через ротируемые прокси – это прописать параметры подключения прямо в запросе через поле meta. Например:
## ваш паук first_spider.pydef start_requests(self):for url in self.start_urls:return Request(url=url, callback=self.parse,meta={"proxy": "http://user:password@your-proxy:8081"})# а дальше код паука
Второй вариант – прокси можно прописать на уровне настроек промежуточного программного обеспечения загрузчика для своего парсера.
Делается это через наследование HttpProxyMiddleware (документация).
Пример файла middlewares.py (сохраните в корне своего проекта):
import base64class MyFirstProxyMiddleware(object):@classmethoddef from_crawler(cls, crawler):return cls(crawler.settings)def __init__(self, settings):self.user = settings.get('USER')self.password = settings.get('PASSWORD')self.endpoint = settings.get('ENDPOINT')self.port = settings.get('PORT')def process_request(self, request, spider):user_credentials = '{user}:{passw}'.format(user=self.user, passw=self.password)basic_authentication = 'Basic ' + base64.b64encode(user_credentials.encode()).decode()host = 'http://{endpoint}:{port}'.format(endpoint=self.endpoint, port=self.port)request.meta['proxy'] = hostrequest.headers['Proxy-Authorization'] = basic_authentication
После этого в настройках проекта нужно определить все переменные и переопределить методы загрузчика DOWNLOADER_MIDDLEWARES. Для этого отредактируйте файл settings.py, чтобы в нём были строки:
# не забудьте заменить значения на своиUSER = 'proxy-user'PASSWORD = 'proxy-password'ENDPOINT = 'your.proxy.provider.com'PROXY_PORT = '8083'DOWNLOADER_MIDDLEWARES = { 'my-first-parser.middlewares.MyFirstProxyMiddleware': 350, 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': 400, }
200+ локаций, без ограничений
Получайте доступ к любым данным, пользуясь нашей глобальной прокси-сетью.
На всякий случай напомним: ваш прокси-провайдер должен иметь API-интерфейс. Без него удалённое управление невозможно. И не забудьте в запросах использовать свой ключ доступа (он у всех разный)!
Многое будет зависеть от логики работы вашей программы, а также от набора методов API у конкретного прокси-сервиса. Например, скрипт может управлять подпиской, настраивать новые фильтры под разные локации, распределять прокси между несколькими пауками или парсерами.
Всё это просто физически невозможно описать в нашем материале.
Например, Froxy позволяет парсить сайты без дополнительных фреймворков (то есть без Scrapy). Мы рассматривали работу онлайн-парсера на примере сбора данных из поисковой выдачи Google.
Онлайн-парсер позволяет получить готовые структурированные данные по наиболее популярным площадкам: соцсети, поисковики, маркетплейсы и т.п. Для всех остальных сайтов есть общий инструмент – для получения HTML-кода страницы.
Документация по API (в том числе по скрейперам).
После завершения задачи скейперы могут уведомлять внешний сервис с помощью веб-хуков.
Пример обращения к API для парсинга выдачи Google:
curl -X POST https://froxy.com/api/subscription/API-КЛЮЧ/task \-H "X-Authorization: Authorization Token" \-d "location[country]"="US" \-d "filters[upload_date]"="past_year" \-d domain="us" \-d page=1 \-d per_page=10 \-d query="фраза для поиска" \-d type="google" \
А вот так будет выглядеть запрос в JSON-разметке:
{"type": "google","query": "Поисковая фраза","location": {"country": "US"},"domain": "us","filters": {"content_type": "news","upload_date": "past_week"},"is_device": false,"is_safe_search": false,"page": 1,"per_page": "10","webhook": "my.webhook.url"}
Синтаксис легко можно встроить в любой python-скрипт.
Ротационные прокси можно организовать своими силами, но в этом случае все технические детали нужно будет предусмотреть в коде своего парсера: как минимум потребуется деление прокси на рабочие и нерабочие.
Чтобы сэкономить время, можно воспользоваться готовыми решениями, как ротатор scrapy-rotating-proxies, или просто подключить прокси с обратной связью (ротируемые прокси).
Найти качественные прокси с ротацией можно у нас. Froxy позволяет настраивать параметры таргетинга и ротации в личном кабинете (или по API). Подключение к парсеру выполняется всего один раз, все остальные действия осуществляются на стороне нашего сервиса: по таймеру или при каждом новом запросе.
На выбор доступны мобильные, резидентные или серверные прокси. Пул адресов – свыше 10 млн. IP, таргетинг до города и оператора связи.