Формат выдачи прокси: как приходит список адресов и что с ним делать
Список адресов приходит в одном из двух форматов на выбор: IP:PORT и IP:PORT:LOGIN:PASS. Забрать его можно двумя путями, ссылкой на выдачу или файлом, оба варианта лежат в кабинете рядом, и список обновляется в режиме реального времени.
Дальше разобрано, чем эти два формата отличаются на практике, почему ссылка удобнее файла при регулярной работе, как распарсить строку списка в bash и python, как превратить сырые строки в конфиг для curl, requests, A-Parser, ZennoPoster и антидетект-браузера, и как настроить автоматическую подтяжку списка перед каждым прогоном.
Два формата строки: что стоит за двоеточиями
Формат IP:PORT содержит две величины: адрес выходного узла и порт, на котором посредник принимает соединение. Этого достаточно, когда доступ открыт по привязке рабочей машины: сервер сам сверяет адрес источника со списком привязок в пакете и пропускает запрос без пары логина и пароля.
# формат из двух полей, доступ по привязанному адресу
185.24.87.14:8000
185.24.87.15:8000
92.118.44.203:8000
Формат IP:PORT:LOGIN:PASS добавляет к тем же двум полям учётные данные. Логин и пароль едут внутри самой строки, поэтому запрос авторизуется откуда угодно и привязка машины при этом не требуется. Формат берут те, у кого прогон идёт с нескольких серверов сразу, или те, кто гоняет задачи в облачном сборщике, где внешний адрес заранее неизвестен.
# формат из четырёх полей, доступ по учётным данным
185.24.87.14:8000:user5521:pf39kd
185.24.87.15:8000:user5521:pf39kd
92.118.44.203:8000:user5521:pf39kd
Порт в обоих случаях один и тот же. Меняется только длина строки и наличие пары в хвосте. Мы выдаём оба формата в одном кабинете, переключение занимает одно нажатие, и повторно покупать ничего не нужно: адреса под обоими вариантами одинаковые.
Выбор между ними мы сводим к одному вопросу: адрес рабочей машины постоянный или меняется. Постоянный сервер с фиксированным внешним адресом просит формат из двух полей, потому что учётные данные в строке ему ничего не добавляют. Ноутбук с домашним провайдером, контейнер в облаке и любая машина, которая получает внешний адрес динамически, работают спокойнее с четырьмя полями: строка везёт доступ с собой и от адреса источника не зависит.
| Формат | Сколько полей | Когда берут | Что должно быть настроено |
|---|---|---|---|
IP:PORT | 2 | Прогон идёт с одной или двух постоянных машин | Адрес машины прописан в настройках привязки |
IP:PORT:LOGIN:PASS | 4 | Прогон идёт с меняющихся или временных машин | Ничего, пара логина и пароля едет в строке |
IP:PORT для команды | 2 | Раздача списка внутри одного сервера | Привязка на адрес этого сервера |
IP:PORT:LOGIN:PASS для облака | 4 | Задачи в облачном сборщике или в контейнерах | Хранилище секретов на стороне запуска |
Про сам механизм проверки права на доступ есть отдельный разбор: два способа доступа описаны там по шагам, с примерами настроек и разбором отказов.
Ссылка на выдачу и файл: чем они отличаются в работе
В кабинете два варианта получения: скопировать ссылку или скачать файл. Ссылка отдаёт список текстом, по одной строке на адрес, без заголовков и без обёрток. Файл содержит ровно те же строки, разница сидит в способе доставки.
Файл удобен один раз. Человек скачал его, открыл, скопировал десяток строк в поле программы и пошёл работать. Такой путь закрывает разовую задачу: проверить сайт со стороны, прогнать сотню страниц, посмотреть, как целевой домен отвечает через посредника.
Ссылка выигрывает там, где прогоны повторяются. Программа обращается по адресу выдачи сама, забирает актуальные строки и подставляет их в очередь перед запуском. Ручной шаг исчезает совсем: администратор один раз вписал адрес в настройки планировщика, и дальше список приезжает свежим каждый запуск, без участия человека и без переноса файлов между машинами.
| Способ | Что происходит | Где выигрывает | Где мешает |
|---|---|---|---|
| Ссылка на выдачу | Программа тянет строки по HTTP перед запуском | Регулярные прогоны, планировщики, несколько машин | Софт без поддержки внешних источников |
| Файл со списком | Строки лежат на диске рабочей машины | Разовая проверка, ручной импорт, офлайн-стенд | Список стареет с момента скачивания |
| Ссылка плюс локальный кеш | Строки тянутся, сохраняются во временный файл | Прогоны на нескольких серверах с общей логикой | Требует небольшого скрипта на входе |
Мы советуем брать ссылку всем, у кого задача повторяется хотя бы раз в неделю. Причина простая: пул живой.
Есть и промежуточный вариант, который берут администраторы нескольких серверов. Ссылка тянется одним скриптом на управляющей машине, результат раскладывается по рабочим узлам штатным средством развёртывания, и на каждом узле лежит локальная копия одного и того же среза. Так мы получаем и свежесть ссылки, и предсказуемость файла: все узлы работают с одинаковым набором строк, что заметно упрощает сравнение логов между ними.
Почему список обновляется и что происходит со старым файлом
Пул держится в районе 12 000 активных адресов в сутки. Ротация внутри пула автоматическая, состав меняется постоянно, и список адресов обновляется в режиме реального времени. Ссылка на выдачу отдаёт срез на момент обращения, файл фиксирует срез на момент скачивания.
Отсюда следует практический вывод. Файл, сохранённый месяц назад, содержит строки, которых в пуле уже нет. Программа честно пытается открыть соединение, получает таймаут либо отказ на уровне сокета, повторяет попытку по своим настройкам и в итоге пишет в лог отказ. Прогон при этом идёт медленнее ожидаемого, доля успешных ответов падает, и администратор начинает искать причину в настройках целевого сайта, хотя причина лежит в устаревшем файле на диске.
Картина в логах узнаваемая. Часть строк отвечает нормально, часть отваливается по таймауту, соотношение не меняется от запуска к запуску. Ровно этот признак отличает старый список от проблем с целевым доменом: при проблемах на стороне сайта отказы распределяются по всем адресам примерно поровну.
# быстрый ответ на вопрос «список свежий или нет»
# берём первые 30 строк и смотрим, сколько из них отвечает
head -n 30 proxy.txt | while IFS=: read -r ip port user pass; do
code=$(curl -s -o /dev/null -w '%{http_code}' --max-time 8 \
-x "http://${user:+$user:$pass@}$ip:$port" https://ifconfig.me)
echo "$ip:$port -> ${code:-timeout}"
done
Если отвечает меньше двух третей строк, список перечитывается из кабинета до начала работы. Занимает это секунды, экономит час разбора.
Разбор строки списка в bash
Строка списка разбирается по двоеточию. В bash для этого хватает встроенного read с заданным разделителем, внешние утилиты не нужны. Разделитель задаётся через IFS, поля читаются в переменные, пустые хвостовые поля остаются пустыми.
#!/bin/bash
# proxy.txt содержит строки IP:PORT либо IP:PORT:LOGIN:PASS
while IFS=: read -r ip port user pass; do
[ -z "$ip" ] && continue
if [ -n "$user" ]; then
echo "http://$user:$pass@$ip:$port"
else
echo "http://$ip:$port"
fi
done < proxy.txt > proxy-urls.txt
Скрипт на выходе даёт готовые URL посредника, которые принимает почти любая программа. Тот же цикл переписывается под SOCKS5 заменой схемы в двух местах. Если нужны обе схемы сразу, добавляется второй echo со схемой socks5h://, и один список превращается в два файла под разные задачи.
# счётчик полей: понять, какой формат пришёл, до всякого разбора
awk -F: '{print NF}' proxy.txt | sort -u
# 2 -> IP:PORT
# 4 -> IP:PORT:LOGIN:PASS
Одна деталь экономит время. Файл из кабинета иногда приезжает с окончаниями строк в стиле Windows, и тогда последнее поле получает невидимый символ возврата каретки, из-за которого пароль перестаёт совпадать. Проверяется это одной командой, поправляется тоже одной.
# увидеть невидимое
cat -A proxy.txt | head -n 3
# убрать возврат каретки, если он есть
tr -d '\r' < proxy.txt > proxy-fixed.txt
Разбор строки списка в python
В python разбор строится на split(':') с проверкой числа полей. Функция ниже принимает строку любого из двух форматов и возвращает словарь с готовыми URL под HTTP и SOCKS5, а также отдельные поля для программ, которые ждут логин и пароль в разных полях интерфейса.
import re
LINE = re.compile(r'^\s*(\d{1,3}(?:\.\d{1,3}){3}):(\d{1,5})(?::([^:\s]+):([^:\s]+))?\s*$')
def parse(line):
m = LINE.match(line)
if not m:
return None
ip, port, user, password = m.groups()
auth = f'{user}:{password}@' if user else ''
return {
'host': ip,
'port': int(port),
'user': user,
'password': password,
'http': f'http://{auth}{ip}:{port}',
'socks5': f'socks5h://{auth}{ip}:{port}',
}
def load(path):
out = []
with open(path, encoding='utf-8') as f:
for raw in f:
item = parse(raw)
if item:
out.append(item)
return out
pool = load('proxy.txt')
print(len(pool), 'строк принято')
Регулярное выражение отсекает мусор: комментарии, пустые строки, случайно скопированный заголовок таблицы. Это важнее, чем кажется. Программа, которой скормили строку с лишним пробелом, обычно падает на первом же соединении и пишет в лог сообщение об ошибке разбора адреса, по которому непонятно, какая именно строка виновата.
Готовый список сразу подставляется в requests. Библиотека ждёт словарь со схемами, поэтому конвертация занимает одну строку.
import requests, random
p = random.choice(pool)
proxies = {'http': p['http'], 'https': p['http']}
r = requests.get('https://ifconfig.me', proxies=proxies, timeout=10)
print(r.status_code, r.text.strip())
Обратите внимание на ключ https в словаре: там стоит схема http. Так и должно быть. Ключ обозначает протокол целевого адреса, значение обозначает адрес посредника, и для HTTPS-запросов через HTTP-посредник поднимается туннель, тип которого от схемы в словаре не зависит. Пакет с HTTP и HTTPS оформляется на странице, где мы даём доступ к пулу по HTTP и HTTPS.
Как превратить список в формат конкретных программ
Разные программы ждут разного. Одни принимают строку целиком, другие требуют четыре отдельных поля, третьи читают файл своего формата. Ниже сведено, что именно ждёт каждый распространённый потребитель списка. Сам пул при этом один, и обе схемы обращения к нему открыты сразу после включения пакета, где мы держим адреса под HTTP и HTTPS.
| Программа | Что подаётся | Форма записи |
|---|---|---|
| curl | Один посредник на запрос | -x http://user:pass@ip:port |
| requests | Словарь схем | {'http': 'http://user:pass@ip:port'} |
| A-Parser | Файл списка либо адрес выдачи | http://user:pass@ip:port построчно |
| ZennoPoster | Поля в кубике настройки или строка из файла | ip:port:login:pass построчно |
| Антидетект-браузер | Четыре поля в карточке профиля | Хост, порт, логин, пароль по отдельности |
| Системный клиент на сервере | Переменные окружения | export http_proxy=http://user:pass@ip:port |
curl принимает строку целиком, включая учётные данные. Проверка одного адреса выглядит так:
# HTTP-посредник, доступ по привязке машины
curl -x http://185.24.87.14:8000 https://ifconfig.me
# HTTP-посредник, доступ по учётным данным
curl -x http://user5521:[email protected]:8000 https://ifconfig.me
# SOCKS5 с разрешением имён на стороне выхода
curl -x socks5h://user5521:[email protected]:8000 https://ifconfig.me
A-Parser читает список из файла либо тянет его по адресу выдачи, причём второй путь настраивается прямо в настройках парсера и обновляется по расписанию. Формат строки там сетевой, со схемой впереди, поэтому файл proxy-urls.txt из bash-скрипта выше подходит без правок. Подробности настройки собраны на странице про работу A-Parser через пул адресов.
ZennoPoster держит список в своём менеджере и ждёт строки в исходном виде, через двоеточие. Здесь удобнее отдать программе файл из кабинета как есть, без конвертации. Порядок подключения разобран там, где описана связка ZennoPoster с пулом IPv4.
Антидетект-браузер разносит поля по карточке профиля: тип посредника, хост, порт, логин, пароль. Копирование строки целиком в поле хоста приводит к отказу соединения, поэтому строку разбивают заранее. Скрипт на python выше отдаёт готовые поля host, port, user, password, которые остаётся разложить по колонкам таблицы импорта. Особенности профилей описаны на странице про прокси для антидетект-браузеров.
Системные утилиты на сервере читают посредника из переменных окружения. Строку туда кладут в том же виде, со схемой и учётными данными, и после этого через пул начинают ходить wget, apt, менеджеры пакетов языков программирования и всё остальное, что уважает эти переменные. Отдельно прописывается no_proxy, чтобы обращения внутрь своей сети шли напрямую и не занимали потоки пакета.
export http_proxy="http://user5521:[email protected]:8000"
export https_proxy="$http_proxy"
export no_proxy="localhost,127.0.0.1,10.0.0.0/8"
Для сокетного варианта строка меняется одной схемой. Программы, которые ходят по произвольным портам, работают с SOCKS5 ровнее, и адреса при этом остаются те же самые: пакет с сокетным доступом оформляется там, где мы выдаём SOCKS5 для программ и скриптов.
Автоматическая подтяжка списка перед прогоном
Ручной шаг убирается одним скриптом на входе. Задача скрипта простая: сходить по адресу выдачи, забрать актуальные строки, проверить, что ответ похож на список, и положить результат туда, откуда его читает рабочая программа. Старый файл при этом сохраняется до успешной записи нового, чтобы сбой сети не оставил прогон вовсе без адресов.
#!/bin/bash
# refresh-proxy.sh, запускается из планировщика перед каждым прогоном
URL="$PROXY_LIST_URL" # адрес выдачи берём из окружения
DST="/opt/run/proxy.txt"
TMP="$(mktemp)"
curl -fsS --max-time 20 "$URL" | tr -d '\r' | grep -E '^[0-9.]+:[0-9]+' > "$TMP"
lines=$(wc -l < "$TMP")
if [ "$lines" -lt 50 ]; then
echo "выдача вернула $lines строк, оставляем прежний файл" >&2
rm -f "$TMP"; exit 1
fi
mv "$TMP" "$DST"
chmod 600 "$DST"
echo "список обновлён: $lines строк"
Три вещи в этом скрипте держат прогон на ногах. Флаг -fsS заставляет curl вернуть ненулевой код при ответе сервера с ошибкой, вместо того чтобы записать текст ошибки в файл списка. Фильтр grep отсекает всё, что не похоже на строку адреса. Порог в 50 строк отбивает случай, когда выдача ответила коротким телом из-за сетевого сбоя.
В python то же самое укладывается в полтора десятка строк, если прогон запускается изнутри питоновского скрипта.
import os, requests
def refresh(dst='proxy.txt', minimum=50):
r = requests.get(os.environ['PROXY_LIST_URL'], timeout=20)
r.raise_for_status()
lines = [l.strip() for l in r.text.splitlines() if parse(l)]
if len(lines) < minimum:
raise RuntimeError(f'выдача вернула {len(lines)} строк')
tmp = dst + '.new'
with open(tmp, 'w', encoding='utf-8') as f:
f.write('\n'.join(lines) + '\n')
os.replace(tmp, dst)
os.chmod(dst, 0o600)
return len(lines)
Запись через временный файл с последующим os.replace гарантирует, что параллельно работающий прогон никогда не увидит наполовину записанный список. На нагруженных стендах это спасает от плавающих отказов, которые потом ищут неделями.
Расписание обновления мы привязываем к запуску прогона, не к календарю. Скрипт вызывается первой строкой рабочей задачи, отрабатывает за секунду и отдаёт управление дальше. Отдельная запись в планировщике раз в час тоже работает, хотя смысла в ней меньше: между обновлением и стартом прогона всё равно остаётся окно, за которое состав пула успевает поменяться. Логи обновления держим рядом с логами прогона, тогда по любому спорному запуску видно, каким срезом списка он работал.
Где держать список и как его не оставить в открытом виде
Строка формата IP:PORT:LOGIN:PASS содержит рабочие учётные данные пакета. Файл со списком на общей машине читает любой, у кого есть учётная запись на этой машине: коллега, подрядчик, скрипт сборки, резервное копирование, которое утащит его в архив вместе со всем каталогом. Дальше учётными данными пользуются со стороны, потоки пакета уходят на чужие задачи, а лимит при этом делится на всех.
Порядок хранения короткий и понятный. Права на файл выставляются в 600, владелец служебный, каталог лежит вне репозитория и вне общего сетевого диска.
# файл читает только владелец процесса
chmod 600 /opt/run/proxy.txt
chown runner:runner /opt/run/proxy.txt
# каталог закрыт целиком
chmod 700 /opt/run
Адрес выдачи и учётные данные держим в переменных окружения либо в хранилище секретов, которое даёт CI. В репозиторий не попадает ни строка списка, ни адрес выдачи: имя файла добавляется в .gitignore до первого коммита, потому что вычищать историю потом дороже.
| Что храним | Где держим | Права | Чего избегаем |
|---|---|---|---|
| Файл со списком | Служебный каталог на рабочей машине | 600, владелец служебный | Общий сетевой диск, домашний каталог пользователя |
| Адрес выдачи | Переменная окружения или хранилище секретов | Доступ у процесса прогона | Жёсткая запись внутри скрипта |
| Учётные данные пакета | Хранилище секретов CI | Доступ у задачи сборки | Переписка в мессенджере, таблица с общим доступом |
| Временный файл при обновлении | Тот же каталог, mktemp | 600 | Общий каталог временных файлов |
Формат IP:PORT в этом смысле спокойнее: без учётных данных внутри строки список сам по себе доступа не открывает, потому что сервер сверяет адрес источника с привязкой в пакете. Мы даём в пакете одновременную привязку 2 адресов, менять её разрешено без ограничений прямо в настройках, так что перевести прогон на привязку и убрать пару из строк списка обычно получается за пару минут.
Что делать, когда часть строк из списка не отвечает
Сначала проверяем масштаб. Прогон по первым тридцати строкам показывает долю рабочих: если отвечает подавляющее большинство, дело в отдельных узлах и прогон продолжается штатно. Если отвечает меньше половины, причина общая, и её ищем по порядку.
| Признак | Вероятная причина | Что делаем |
|---|---|---|
| Отвечает часть строк, доля стабильна | Список устарел, файл давно на диске | Перечитываем выдачу по ссылке |
| Ни одна строка не отвечает, код 407 | Формат доступа не совпал с настройкой | Сверяем привязку и наличие пары в строке |
| Ни одна строка не отвечает, отказ сокета | Пакет ещё включается или доступ закрыт | Ждём включения, пакет поднимается примерно за 5 минут |
| Отказы растут при росте потоков | Упёрлись в лимит пакета | Считаем потоки с учётом деления при двух привязках |
| Пароль не принимается | Возврат каретки в конце строки | Прогоняем файл через tr -d '\r' |
| Программа ругается на формат хоста | Строка целиком попала в поле хоста | Разбиваем строку на поля скриптом |
Код 407 говорит о доступе. Он приходит, когда сервер ждёт учётные данные, а запрос пришёл без них, либо когда прогон идёт с машины, чей адрес в привязке не значится. Проверяется это одной командой с ключом -v, где видно и заголовок ответа, и адрес источника.
curl -v -x http://185.24.87.14:8000 https://ifconfig.me 2>&1 | grep -i 'HTTP/1.1 407'
Отдельно смотрим на потоки. Стандартные пакеты дают до 1000 одновременных соединений, корпоративный до 3000, пакеты по потокам не складываются, и при двух привязанных адресах общий лимит делится между ними пополам. Программа, настроенная на 800 потоков при двух привязках, упирается в потолок 500 и начинает получать отказы, которые в логах выглядят как проблемы со списком. Порядок проверки работоспособности целиком разобран в материале про проверку прокси после подключения.
Частые вопросы
В каком формате выдаётся список прокси?
Форматов два на выбор: IP:PORT и IP:PORT:LOGIN:PASS. Первый берут при доступе по привязке рабочего адреса, второй при доступе по учётным данным. Порт в обоих одинаковый, переключение формата делается в кабинете и повторной оплаты не требует.
В каком виде выдаётся список, файлом?
В кабинете два варианта: получить ссылку на выдачу или скачать файл. Ссылку удобно отдавать программе, которая тянет список сама перед каждым запуском. Файл удобнее для ручного импорта и раздачи внутри одного сервера.
Как часто обновляется список адресов?
Список адресов обновляется в режиме реального времени. Онлайн пула держится в районе 12 000 адресов в сутки, ротация внутри пула автоматическая, поэтому срез, забранный неделю назад, к моменту прогона уже отличается от актуального.
Прокси каких стран приходят в списке?
Пул устроен как микс со всего мира, адреса приходят из множества стран, отбор по отдельной стране не делается. Для сбора открытых данных, съёма позиций и работы с несколькими кабинетами такая схема даёт разнообразие подсетей без ручной настройки со стороны пользователя.
Соседние материалы раздела продолжают тему покупки: какие прокси покупать под задачу с разбором по протоколам и потокам, как купить прокси IPv4 от регистрации до первого запроса, что входит в пакет по адресам, трафику и привязкам. Когда список уже на руках, следующий шаг описан в разборе про подключение в программах и на сервере.