Бібліотека requests — найпопулярніший HTTP-клієнт в екосистемі Python. Поєднання її з ротацією мобільних проксі — це різниця між парсером, який працює годинами, і тим, що блокується за лічені хвилини.
IP-адреси датацентрів блокуються на рівні ASN до відправлення першого запиту. Резидентні проксі кращі, але швидко вигорають під інтенсивним навантаженням. Мобільні проксі на реальних 4G/5G модемах несуть ASN мобільних операторів, які практично неможливо заблокувати в масштабі — блокування означає блокування тисяч реальних мобільних користувачів, що ділять один і той самий IP-діапазон CGNAT.
Цей посібник охоплює повне налаштування: від першого pip install до production-парсера з автоматичною ротацією IP, логікою повторних спроб та антидетект-заголовками.
Get Rotating Mobile Proxies
Real 4G/5G IPs with API rotation — Ukraine, Romania, Latvia. From $50/mo dedicated.
Необхідні умови
Встановіть необхідні пакети. Розширення requests[socks] додає підтримку SOCKS5 через urllib3, а PySocks відповідає за низькорівневий протокол:
pip install requests "requests[socks]" python-socks
Перевірте, що встановлення пройшло успішно:
import requests
import socks # from PySocks
print(requests.__version__)
Вам також знадобляться облікові дані ProxyGrow: хост, порт, ім'я користувача, пароль і URL ротаційного API (доступні в особистому кабінеті ProxyGrow).
Базове налаштування проксі з requests
Найпростіший спосіб направити запит через SOCKS5-проксі — параметр proxies:
import requests
proxies = {
'http': 'socks5h://username:password@host:port',
'https': 'socks5h://username:password@host:port',
}
response = requests.get('https://httpbin.org/ip', proxies=proxies)
print(response.json())
Запустіть це — у відповіді буде український, румунський або латвійський IP мобільного оператора замість вашої реальної адреси.
Чому socks5h, а не socks5
Це найважливіша деталь у конфігурації проксі для Python:
socks5: DNS-розрізнення відбувається на вашому комп'ютері, потім вже розрізнений IP передається через проксі. Ваші DNS-запити видимі.socks5h: DNS-розрізнення відбувається через проксі, на віддаленій стороні. Ваша машина ніколи не розрізняє ім'я хоста напряму.
Буква h означає «host-name» — ім'я хоста передається через тунель і розрізняється на сервері проксі. Це критично для анонімності: витоки DNS можуть розкрити ваше реальне місцезнаходження навіть коли HTTP-трафік коректно проксується.
Завжди використовуйте socks5h для парсингу. Єдина причина використовувати звичайний socks5 — якщо сервер проксі не підтримує віддалене DNS-розрізнення, але сервери ProxyGrow підтримують.
Сесійні проксі (постійна SOCKS5 сесія)
Створення об'єкта Session — майже завжди правильний підхід. Сесії повторно використовують базове TCP-з'єднання, автоматично зберігають cookies і дозволяють задати проксі та заголовки один раз замість кожного виклику:
import requests
session = requests.Session()
session.proxies = {
'http': 'socks5h://user:pass@host:port',
'https': 'socks5h://user:pass@host:port',
}
# All requests through this session use the proxy
r = session.get('https://example.com')
print(r.status_code)
Для робочих процесів парсингу з десятками або сотнями запитів сесія економить накладні витрати на з'єднання і зберігає автентифікацію проксі. Ви налаштовуєте проксі один раз і забуваєте про нього.
Ротація IP через ProxyGrow API
Ротація IP — це не перемикання між різними проксі-серверами, а запуск повторного підключення на фізичному модемі, щоб оператор призначив нову IP-адресу. ProxyGrow надає це як простий API-виклик.
Ось повний робочий процес ротації:
import requests
import time
PROXY_HOST = "your-proxy-host"
PROXY_PORT = 1080
PROXY_USER = "username"
PROXY_PASS = "password"
ROTATION_URL = "https://api.proxygrow.com/rotate?key=YOUR_API_KEY"
def rotate_ip():
requests.get(ROTATION_URL)
time.sleep(5) # wait for modem to reconnect
def get_current_ip(session):
r = session.get('https://httpbin.org/ip')
return r.json()['origin']
session = requests.Session()
session.proxies = {
'http': f'socks5h://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}',
'https': f'socks5h://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}',
}
urls_to_scrape = [
'https://example.com/page/1',
'https://example.com/page/2',
# ... more URLs
]
# Scrape with rotation every 10 requests
for i, url in enumerate(urls_to_scrape):
if i > 0 and i % 10 == 0:
rotate_ip()
print(f"Rotated IP. New IP: {get_current_ip(session)}")
r = session.get(url)
# process r.text
Чому ротація кожні 10 запитів, а не кожен запит? Надто часта ротація витрачає час (кожне перепідключення займає 3-6 секунд) і може викликати обмеження швидкості на самому ротаційному API. Надто рідка ротація дозволяє цільовому сайту формувати поведінковий профіль на одному IP. Кожні 10-20 запитів — практичний баланс для більшості цілей.
Чому time.sleep(5) після ротації? Модем відключається від мережі оператора, проводить повторне узгодження і отримує нову IP-адресу через DHCP або призначення CGNAT. Якщо ваш наступний запит піде до повного перепідключення модема — отримаєте помилку з'єднання. П'ять секунд надійно покривають вікно перепідключення.
Обробка помилок і логіка повторних спроб
Мережеві помилки неминучі при роботі з проксі. Модеми перепідключаються, оператори обмежують швидкість, цілі повертають 429. Механізм повторних спроб urllib3 автоматично обробляє тимчасові збої:
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
retry = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503],
)
adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter)
session.mount('https://', adapter)
Що робить кожен параметр:
total=3: повторювати до 3 разів перед відмовоюbackoff_factor=1: чекати 1с, потім 2с, потім 4с між спробами (експоненціальне відкладення)status_forcelist: вважати ці HTTP-коди повторюваними помилками
Монтуйте адаптер на http:// і https:// для охоплення всіх запитів. Робіть це один раз після створення сесії, до виконання запитів.
Налаштування реалістичних заголовків для обходу детектування
IP мобільного оператора із заголовками десктопного браузера — статистична аномалія. Реальний трафік з Kyivstar або Orange Romania переважно надходить з мобільних пристроїв. Антибот-системи знають це і помічають невідповідності.
Задайте заголовки, сумісні з походженням проксі:
session.headers.update({
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15',
'Accept-Language': 'uk-UA,uk;q=0.9,en;q=0.8', # match proxy country
'Accept-Encoding': 'gzip, deflate, br',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Connection': 'keep-alive',
})
Ключові моменти:
- User-Agent: використовуйте мобільний UA. Для українських проксі підходять iOS або Android. Для румунських — те саме: більшість мобільних користувачів у Румунії та Україні використовують iOS або Android.
- Accept-Language: відповідайте країні проксі.
uk-UAдля України,ro-ROдля Румунії,lv-LVдля Латвії. Деякі сайти перевіряють це як сигнал узгодженості. - Accept-Encoding: завжди включайте
br(Brotli). Реальні браузери надсилають його. Парсери, що його опускають, легше ідентифікувати.
Обмеження швидкості між запитами
Запити зі швидкістю машини — миттєвий сигнал детектування. Додавайте випадкові затримки між запитами для імітації паттернів перегляду людини:
import time
import random
for url in urls_to_scrape:
r = session.get(url)
# process r.text
time.sleep(random.uniform(1, 3))
random.uniform(1, 3) повертає число з плаваючою точкою від 1.0 до 3.0 секунд — ваш парсер працює зі швидкістю 20-60 запитів на хвилину. Це в межах швидкого користувача-людини і значно нижче порогів, що запускають автоматичне обмеження швидкості на більшості сайтів.
Для більш захищених цілей збільшіть діапазон: random.uniform(2, 6). Для внутрішніх API або цілей без антибот-захисту можна зменшити.
Перевірка роботи проксі
Перед запуском повноцінного парсингу завжди перевіряйте, що проксі активний і ротація дійсно змінює IP:
import requests
import time
PROXY_HOST = "your-proxy-host"
PROXY_PORT = 1080
PROXY_USER = "username"
PROXY_PASS = "password"
ROTATION_URL = "https://api.proxygrow.com/rotate?key=YOUR_API_KEY"
session = requests.Session()
session.proxies = {
'http': f'socks5h://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}',
'https': f'socks5h://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}',
}
# Check IP before rotation
ip_before = session.get('https://httpbin.org/ip', timeout=10).json()['origin']
print(f"IP before rotation: {ip_before}")
# Trigger rotation
requests.get(ROTATION_URL)
time.sleep(5)
# Check IP after rotation
ip_after = session.get('https://httpbin.org/ip', timeout=10).json()['origin']
print(f"IP after rotation: {ip_after}")
if ip_before != ip_after:
print("Rotation successful.")
else:
print("Warning: IP did not change. Check rotation URL or wait longer.")
Запускайте це перед кожною новою сесією парсингу. Якщо IP однакові — або неправильний URL ротації, або модем не завершив перепідключення. Збільшіть час очікування і спробуйте знову.
Інтеграція зі Scrapy
Для масштабних проектів парсингу система middleware Scrapy дозволяє підключити ротацію проксі на рівні павука. Патерн — кастомний DownloaderMiddleware, який встановлює request.meta['proxy'] для кожного вихідного запиту і запускає ротацію на основі лічильника або коду відповіді.
Основна ідея:
class ProxyRotationMiddleware:
def process_request(self, request, spider):
request.meta['proxy'] = 'socks5h://user:pass@host:port'
# Rotation logic: call the API every N requests
Для простіших налаштувань Scrapy пакет scrapy-rotating-proxies керує пулом, але не підтримує ротацію модема через API, яку надає ProxyGrow. Кастомний middleware дає повний контроль над часом і способом ротації.
Поширені помилки та способи їх усунення
requests.exceptions.ConnectionError
Проксі-сервер недоступний. Причини:
- Неправильний хост або порт в URL проксі
- Модем у процесі перепідключення після ротації
- Мережева проблема між вашою машиною і проксі-сервером
Рішення: перевірте облікові дані, зачекайте 5-10 секунд після ротації, переконайтесь що хост розрізняється коректно.
requests.exceptions.ProxyError
Проксі-сервер відхилив з'єднання — зазвичай через помилку автентифікації. Причини:
- Неправильне ім'я користувача або пароль
- Облікові дані закінчились або відкликані
- Ваш IP не в білому списку, якщо проксі використовує автентифікацію за IP
Рішення: повторно перевірте ім'я користувача і пароль в особистому кабінеті ProxyGrow. Переконайтесь, що використовуєте формат socks5h://user:pass@host:port (не http://).
requests.exceptions.Timeout
Запит не завершився протягом періоду тайм-ауту. Найпоширеніші причини в налаштуваннях проксі:
- Запит одразу після ротації (модем ще перепідключається)
- Цільовий сайт повільно відповідає
- Модем тимчасово втратив сигнал
Рішення: завжди задавайте явні тайм-аути і завжди очікуйте після ротації:
try:
r = session.get(url, timeout=(10, 30)) # (connect timeout, read timeout)
except requests.exceptions.Timeout:
print(f"Request to {url} timed out — retrying after delay")
time.sleep(10)
Форма кортежу (connect_timeout, read_timeout) корисніша за одиночне значення, тому що з'єднання з проксі і відповідь сервера мають різні характеристики відмови.
Повний робочий приклад
Все разом — парсер із сесією, ротацією, повторними спробами, заголовками та обробкою помилок:
import requests
import time
import random
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
PROXY_HOST = "your-proxy-host"
PROXY_PORT = 1080
PROXY_USER = "username"
PROXY_PASS = "password"
ROTATION_URL = "https://api.proxygrow.com/rotate?key=YOUR_API_KEY"
def build_session():
session = requests.Session()
session.proxies = {
'http': f'socks5h://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}',
'https': f'socks5h://{PROXY_USER}:{PROXY_PASS}@{PROXY_HOST}:{PROXY_PORT}',
}
session.headers.update({
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15',
'Accept-Language': 'uk-UA,uk;q=0.9,en;q=0.8',
'Accept-Encoding': 'gzip, deflate, br',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
})
retry = Retry(total=3, backoff_factor=1, status_forcelist=[429, 500, 502, 503])
adapter = HTTPAdapter(max_retries=retry)
session.mount('http://', adapter)
session.mount('https://', adapter)
return session
def rotate_ip():
try:
requests.get(ROTATION_URL, timeout=10)
time.sleep(5)
except Exception as e:
print(f"Rotation error: {e}")
time.sleep(10)
def scrape(urls):
session = build_session()
for i, url in enumerate(urls):
if i > 0 and i % 10 == 0:
rotate_ip()
try:
r = session.get(url, timeout=(10, 30))
r.raise_for_status()
print(f"[{i}] {url} — {len(r.text)} bytes")
# process r.text here
except requests.exceptions.ProxyError:
print(f"[{i}] Proxy auth error — check credentials")
except requests.exceptions.Timeout:
print(f"[{i}] Timeout — skipping {url}")
except requests.exceptions.HTTPError as e:
print(f"[{i}] HTTP {e.response.status_code} — {url}")
time.sleep(random.uniform(1, 3))
urls = [f"https://example.com/page/{n}" for n in range(1, 51)]
scrape(urls)
Цей код обробляє 50 URL, виконує ротацію кожні 10, автоматично повторює тимчасові збої і виводить зрозумілі повідомлення про помилки для кожного типу відмови.
Get Rotating Mobile Proxies
Real 4G/5G IPs with API rotation — Ukraine, Romania, Latvia. From $50/mo dedicated.
Підсумок
Ключові тези цього посібника:
- Використовуйте
socks5h://замістьsocks5://— віддалене DNS-розрізнення обов'язкове для анонімності - Використовуйте
requests.Sessionдля всіх багатозапитних робочих процесів - Ротуйте IP через ProxyGrow API, а не шляхом перемикання проксі-серверів
- Завжди чекайте 5 секунд після запуску ротації перед наступним запитом
- Задавайте заголовки, що відповідають країні проксі та типу IP (мобільний UA для мобільних проксі)
- Додавайте затримки
random.uniform(1, 3)між запитами - Використовуйте
Retryзbackoff_factorдля обробки тимчасових збоїв без ручних циклів повторів - Відповідайте
Accept-Languageгео-локації проксі — це сигнал узгодженості, який перевіряють антибот-системи