Прокси-серверы буквально незаменимы в задачах, связанных с парсингом. Прокси могут решать сразу несколько задач: от обеспечения анонимности и защиты пользователя (его устройств), до масштабирования и распараллеливания процессов сбора данных.
Этот материал будет о том, как интегрировать между собой самописный софт на базе фреймворка 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 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-прокси, они же обратные прокси). Такие прокси работают следующим образом:
Тем не менее, ротация 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'] = None
request.meta['proxy'] = "<my-own-proxy>"
У плагина есть дополнительные настройки (указываются через файл settings.py):
Самый простой способ обращения через ротируемые прокси – это прописать параметры подключения прямо в запросе через поле 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 у конкретного прокси-сервиса. Например, скрипт может управлять подпиской, настраивать новые фильтры под разные локации, распределять прокси между несколькими пауками или парсерами.
Всё это просто физически невозможно описать в нашем материале.
Например, 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, таргетинг до города и оператора связи.