Прокси-серверы буквально незаменимы в задачах, связанных с парсингом. Прокси могут решать сразу несколько задач: от обеспечения анонимности и защиты пользователя (его устройств), до масштабирования и распараллеливания процессов сбора данных.
Этот материал будет о том, как интегрировать между собой самописный софт на базе фреймворка Scrapy и ротируемые прокси. Начнём с теории.
Что такое 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
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
По факту парсер уже готов к работе. Достаточно воспользоваться встроенной командной оболочкой (официальная документация):
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 3
import scrapy
from urllib.parse import urljoin
class 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-прокси, они же обратные прокси). Такие прокси работают следующим образом:
- Имеется некая точка доступа к сети (точка входа), это тоже прокси.
- Когда запрос отправляется на точку входа, он маршрутизируется по внутренней сети в соответствии с определёнными правилами (конечный пользователь отчасти может повлиять на эти правила, если ему предоставляются для этого соответствующие интерфейсы – API или web-кабинет).
- Выход запроса из сети выполняется в той точке, которая соответствует правилам маршрутизации.
- Так как прокси-сеть находится в постоянном движении (часть адресов выходит из строя, часть, наоборот, добавляется и т.п.), то супер-прокси должен знать куда он может переслать запрос, а куда нет. Как раз для этого и настраиваются каналы обратной связи. Все прокси в сети общаются с супер-прокси, сообщая ему свой статус и IP.
- Технически для пользователя подключение осуществляется только к одному прокси (он вводит только один адрес). Но вот точка выхода может каждый раз меняться, так как все доступные прокси намеренно ротируются в системе – для снижения и балансировки нагрузки, а также для исключения риска блокировок.
Тем не менее, ротация IP может происходить и по другим схемам. Например, статичные IP (серверные прокси) можно ротировать своими силами – с помощью скриптов или специальных прокси-менеджеров.
Некоторые программы сами умеют тестировать прокси и ротировать их на базе введённого списка.
Под определение ротируемых прокси подпадают: серверные, резидентные и мобильные прокси – если пул адресов большой, и они подключаются по схеме с обратной связью.
3 самых популярных метода интеграции прокси
Если с форматом прокси мы определились, то осталось разобраться со способами подключения. В каждом случае есть свои плюсы и минусы. О них ниже.
Списки прокси
В этом случае вы работаете на опережение – копируете сразу целый список прокси-серверов и подключаете их к своему скрипту.
Скрипт сам может тестировать прокси и включать их в работу, распределять по паукам (рабочим процессам). Ротацию можно проводить на своих условиях и по своим триггерам.
Фактически вы можете взять большой список статических прокси и сделать их ротируемыми.
Но есть и минусы:
- Существенно возрастает объём кода и сложность скрипта.
- Так как проксей в списке может быть неприлично много, каждый из адресов нужно учесть отдельно и распределить по характеристикам/категориям: работает он или нет, какой пинг, расположение и т.п.
- Каким бы большим ни был список, он всё равно может закончиться. Соответственно, нужно подумать о балансировке и о повторном включении в работу заранее.
- Если прокси были низкого качества, например, бесплатные, то процент работоспособных прокси может быть критически низким. Соответственно, даже большой список через какое-то время легко может превратиться «в тыкву». Новые прокси опять нужно где-то найти и снова включить в список (и в работу).
Обратные/ротируемые прокси
BackConnect-прокси решают большинство обозначенных проблем. Всю работу по ротации, по анализу качества и расположения прокси, по их сортировке и т.п., берёт на себя удалённый сервер.
Вам выдаётся только адрес подключения (точка входа в прокси-сеть), вы настраиваете параметры подбора адресов и их ротации. Всё. Дальше нужно только работать.
Но и тут есть ряд спорных моментов:
- Услуги ротации никогда не бывают бесплатными. Обратные прокси – это всегда полноценные сервисы, со своими подписками и тарифами (простой пример – Froxy).
- Внутренние алгоритмы ротации могут конфликтовать с вашими требованиями к прокси. Например, в выбранной локации или в подсети с тем же ASN-номером, может не оказаться свободных IP. В этом случае может сброситься авторизация, соответственно, чаще придётся входить в аккаунт. Это в свою очередь повышает риск блокировки конкретной учётной записи.
К неоспоримым преимуществам можно отнести то, что к парсеру подключается всего один адрес прокси и никакой ротации в коде производить не нужно (по крайней мере в большинстве случаев).
API
Предположим, что конечный (выходной) IP работает стабильно, но блокируется конкретным сайтом или web-сервисов. Парсинг в этом случае невозможен.
В итоге вам нужно лично зайти в панель управления прокси и принудительно обновить IP. Но это не самый удобный вариант для автоматизированного скрипта парсинга.
Логично написать код, который бы обновлял «проблемный» IP без участия пользователя. Но не все прокси-сервисы имеют специальный интерфейс для взаимодействия с программами. Это так называемый API (сокращение от Application Programming Interface, дословно «программный интерфейс приложения»).
Ротация по API осуществляется специальными командами, которые предусмотрел прокси-сервис. У всех сервисов свой синтаксис. Плюс, чтобы не путать клиентов между собой, прямо в API-запросах передаётся специальный ключ, одновременно выступающий в роли идентификатора пользователя.
Вместо сложных конструкций обращения к серверу по API многие прокси-сервисы, предоставляют возможность обновления выходного IP-адреса по специальной ссылке. Достаточно перейти по ней или отправить запрос на этот адрес из парсера, и сработает триггер, который отвечает за ротацию прокси.
Из плюсов: адрес подключения к прокси может оставаться статичным (если используются обратные прокси и есть ссылка/API для обновления).
Если API проработано хорошо, то без участия пользователя можно автоматизировать практически любые действия: пополнение баланса, создание нового прокси-порта (фильтра) или его удаление, ротацию и т.п.
Как интегрировать и ротировать списки прокси в паре со Scrapy
Свои ротируемые прокси в 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'] = None
request.meta['proxy'] = "<my-own-proxy>"
У плагина есть дополнительные настройки (указываются через файл settings.py):
- ROTATING_PROXY_CLOSE_SPIDER – может быть истиной или ложью. В True приводит к остановке всех пауков, если в списке заканчиваются «живые прокси». Если стоит False (это значение по умолчанию), то ротатор запустит «мёртвые» прокси на повторную проверку.
- ROTATING_PROXY_PAGE_RETRY_TIMES – принимает числовые значения, отвечает за количество перебранных прокси для одной страницы. Если ротатор переберёт такое количество прокси, но всё равно получит ошибку, то это будет считаться ошибкой скрипта парсинга (так как потрачено слишком много прокси на одну страницу).
- ROTATING_PROXY_BACKOFF_BASE – базовое время отсрочки в секундах, по дефолту 300 секунд (5 минут).
- ROTATING_PROXY_BACKOFF_CAP – максимальное время отсрочки, по дефолту 3600 секунд (то есть 1 час).
- ROTATING_PROXY_BAN_POLICY – путь к политике обнаружения банов (по умолчанию используется дефолтная политика Скрапи 'rotating_proxies.policy.BanDetectionPolicy'.
Как использовать ротируемые/backconnect прокси
Самый простой способ обращения через ротируемые прокси – это прописать параметры подключения прямо в запросе через поле meta. Например:
## ваш паук first_spider.py
def 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 base64
class MyFirstProxyMiddleware(object):
@classmethod
def 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'] = host
request.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-интерфейс. Без него удалённое управление невозможно. И не забудьте в запросах использовать свой ключ доступа (он у всех разный)!
Многое будет зависеть от логики работы вашей программы, а также от набора методов 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, таргетинг до города и оператора связи.