Ротацию мобильного прокси легко вызвать вручную — открыл ссылку, получил новый адрес. В коде появляются нюансы: во время переподключения модема запросы падают, старые соединения зависают, а браузерные драйверы вообще отказываются принимать прокси с паролем. Собрали рабочие решения для четырёх популярных инструментов, с кодом, который можно забрать себе.
Схема одинаковая для любого инструмента и состоит из двух независимых частей:
Отсюда важное следствие: переписывать настройки прокси после смены адреса не нужно. Единственное, о чём стоит помнить, — открытые keep-alive соединения после переподключения модема становятся нерабочими, поэтому сессию после ротации лучше пересоздавать.
Вместо разрозненных вызовов удобно завести небольшой класс, который сам следит за ротацией, ждёт восстановления канала и проверяет, что адрес действительно сменился.
import time, threading
import requests
class MobileProxy:
def __init__(self, proxy_url, rotate_url):
self.proxies = {"http": proxy_url, "https": proxy_url}
self.rotate_url = rotate_url
self.lock = threading.Lock()
self.session = requests.Session()
def current_ip(self, timeout=15):
r = self.session.get("https://api.ipify.org",
proxies=self.proxies, timeout=timeout)
return r.text.strip()
def rotate(self, wait_max=40):
"""Меняет IP и ждёт, пока адрес реально изменится."""
with self.lock: # ротацию выполняет только один поток
try:
old = self.current_ip()
except requests.RequestException:
old = None
requests.get(self.rotate_url, timeout=30)
self.session.close()
self.session = requests.Session() # сбрасываем keep-alive
deadline = time.time() + wait_max
while time.time() < deadline:
time.sleep(3)
try:
new = self.current_ip(timeout=10)
except requests.RequestException:
continue # канал ещё поднимается
if new != old:
return new
raise TimeoutError("IP не сменился за отведённое время")
def get(self, url, retries=3, **kwargs):
for attempt in range(retries):
try:
return self.session.get(url, proxies=self.proxies,
timeout=20, **kwargs)
except requests.RequestException:
if attempt == retries - 1:
raise
self.rotate() # на ошибке — новый адрес и повтор
Ключевая идея здесь — метод rotate не просто ждёт фиксированную паузу, а опрашивает адрес до фактической смены. Это надёжнее: если модем поднялся за четыре секунды, вы не теряете десять, а если завис на пятнадцать — код не побежит дальше раньше времени.
Когда запросов тысячи, синхронный цикл становится узким местом. Мобильный порт держит ограниченное число параллельных соединений, поэтому паралеллизм нужно ограничивать семафором — иначе канал захлебнётся.
import asyncio, aiohttp
PROXY = "http://user:pass@195.24.10.15:8000"
sem = asyncio.Semaphore(5) # не больше 5 одновременных запросов
async def fetch(session, url):
async with sem:
try:
async with session.get(url, proxy=PROXY, timeout=20) as r:
return await r.text()
except Exception:
return None
async def main(urls):
async with aiohttp.ClientSession() as session:
results = []
# пачками по 50 URL, между пачками — ротация
for i in range(0, len(urls), 50):
batch = urls[i:i + 50]
results += await asyncio.gather(*(fetch(session, u) for u in batch))
await asyncio.sleep(1)
return results
Про параллельность. Мобильный канал — это один модем, а не пул серверов. Пять-десять одновременных соединений он держит спокойно, сотня приведёт к таймаутам и не ускорит сбор. Если нужно больше — берите несколько портов и распределяйте задачи между ними.
Здесь всех ждёт неприятный сюрприз: Chrome через стандартный ключ --proxy-server не умеет принимать логин и пароль. При запуске браузер покажет системное окно ввода, и автоматизация встанет. Есть три выхода:
| Способ | Как работает | Когда подходит |
|---|---|---|
| Белый список IP | Добавляете адрес своего сервера в панели провайдера, пароль не нужен вовсе | Самый простой и надёжный вариант, если у машины статичный адрес |
| Расширение | Генерируете на лету расширение, которое подставляет учётные данные | Когда белый список недоступен, а нужен чистый Selenium |
| selenium-wire | Библиотека проксирует трафик и сама решает вопрос авторизации | Быстрое решение для скриптов, где не критичен лишний слой |
# Вариант с белым списком: пароль не требуется
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_argument("--proxy-server=http://195.24.10.15:8000")
driver = webdriver.Chrome(options=options)
# Ротация между сессиями: закрыли браузер, сменили IP, открыли заново
driver.quit()
proxy.rotate()
driver = webdriver.Chrome(options=options)
В Playwright авторизация на прокси предусмотрена из коробки — отдельными полями, без всяких расширений. Это самый удобный вариант, если вы только выбираете инструмент.
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(proxy={
"server": "http://195.24.10.15:8000",
"username": "user",
"password": "pass",
})
page = browser.new_page()
page.goto("https://api.ipify.org")
print(page.content())
browser.close()
Выделенные 4G-порты с REST API для ротации, авторизацией по логину или белому списку IP и безлимитным трафиком.
Арендовать 4G-проксиРазвилка, к которой приходят все, когда объём вырастает. Ротация одного порта — это последовательная работа: пока модем переподключается, скрипт стоит. Восемь секунд простоя после каждой пачки запросов кажутся мелочью, но на тысяче ротаций превращаются в два часа ожидания.
Пул из нескольких портов работает иначе: пока один переподключается, остальные продолжают отдавать данные. Схема простая — храните список портов, берите следующий по кругу, а порт, который сейчас ротируется, временно исключайте из выдачи.
import itertools
ports = [MobileProxy(p, r) for p, r in CONFIG] # несколько портов
pool = itertools.cycle(ports)
def fetch(url):
for _ in range(len(ports)):
proxy = next(pool)
if proxy.lock.locked(): # этот порт сейчас ротируется
continue
return proxy.get(url)
time.sleep(5) # все заняты — короткая пауза
return fetch(url)
Практический ориентир: до нескольких сотен запросов в час хватает одного порта с ротацией. Дальше выгоднее брать второй и третий — они окупаются не столько скоростью, сколько тем, что сбор перестаёт зависеть от одного канала: упал модем, и вся ночная работа не встала.
Одна привычка, которая экономит часы разбирательств: записывайте, каким адресом получены какие данные. Когда через неделю обнаружится, что часть результатов пустая или подозрительная, вы сразу увидите, относится ли это к конкретному IP или к периоду времени.
Минимальный набор полей в лог: время запроса, внешний IP на момент сбора, URL, код ответа, число попыток. Этого достаточно, чтобы отличить проблему канала от проблемы целевого сайта и понять, не слишком ли часто вы ротируете.
sleep(5) после ротации иногда мало, иногда много. Опрос адреса до смены надёжнее.Нужно ли пересоздавать соединение после смены IP?
Настройки прокси не меняются, но keep-alive соединения после переподключения мертвы — сессию лучше создать заново.
Как проверить, что IP сменился?
Запросить внешний адрес через тот же прокси до и после ротации и сравнить. Если адрес прежний, повторите вызов через паузу.
Почему Selenium не принимает прокси с паролем?
Chrome через --proxy-server не поддерживает авторизацию. Решения: белый список IP, расширение с учётными данными или selenium-wire.
Сколько ждать после ротации?
Переподключение занимает 3–8 секунд, безопасная пауза — около 10. Надёжнее опрашивать адрес в цикле до фактической смены.
Можно ли ротировать из нескольких потоков?
Не нужно: модем переподключится один раз. Оберните ротацию блокировкой, чтобы её выполнял один поток.