Прокси в ZennoPoster: настройка шаблона, профилей и привязка адреса к потоку
- Где в проекте ZennoPoster задаётся выходной адрес
- Встроенный менеджер списка: как программа раздаёт адреса потокам
- Форматы подачи списка и что принимает каждое поле
- Один выход на всё время работы шаблона в одном потоке
- Передача адреса через входные настройки задачи
- Ошибка авторизации внутри шаблона: разбор кода 407
- Профили потоков и связка с антидетект-браузером
- Потоки задачи и лимит потоков со стороны пула
- Статистика отказов: счётчики прямо из кубиков шаблона
- Ночной прогон на 4200 задач от старта до утреннего отчёта
ZennoPoster это среда автоматизации действий в браузере и по HTTP под Windows. Шаблон собирается кубиками в ProjectMaker, сохраняется отдельным файлом проекта, а сама программа запускает этот файл в несколько потоков одновременно. Каждый поток поднимает собственный экземпляр браузера или собственный HTTP-клиент, и выход в сеть у него идёт с адреса машины до тех пор, пока в проекте не сказано иначе. Прокси здесь задаёт точку выхода конкретному потоку, и от того, в каком месте проекта эта точка задана, зависит вся дальнейшая механика прогона.
Разбор ниже построен от шаблона к потоку. Сначала я показываю четыре места, где в ZennoPoster вносится выходной адрес, и чем они отличаются по охвату. Дальше идёт встроенный менеджер списка с его флажками и чекером, форматы строк, которые понимает каждое поле, приём для удержания одного выхода на всё время работы шаблона, передача адреса через входные настройки задачи, разбор ошибки авторизации внутри проекта и сбор статистики отказов прямо из кубиков. Цифры я привожу свои, снятые с прогонов на собственных шаблонах.
Где в проекте ZennoPoster задаётся выходной адрес
Мест ровно четыре, и путаница между ними даёт большую часть вопросов от новичков. Первое место это общий список программы: адреса лежат в менеджере ZennoPoster, флажок в свойствах задачи разрешает шаблону их брать, и поток получает строку в момент старта. Второе место это кубик установки прокси внутри самого шаблона: он срабатывает в той точке схемы, куда я его поставил, и меняет выход по ходу выполнения. Третье место это входные настройки задачи, где оператор вписывает адрес или ссылку на список руками перед запуском. Четвёртое место это C#-вставка с вызовом instance.SetProxy, которая подставляет строку из своей переменной.
Приоритет тут простой. То, что выполнилось позже по ходу схемы, перекрывает заданное раньше. Общий список программы отдаёт строку первым, ещё до первого кубика, поэтому любой вызов внутри шаблона имеет над ним верх. Я пользуюсь этим постоянно: менеджер держит рабочий выход по умолчанию, а отдельная ветка проекта переключает поток на другую строку, когда шаг требует смены точки входа.
| Место в проекте | Охват | Момент подстановки | Что видно в логе задачи |
|---|---|---|---|
| Менеджер списка программы | Все потоки задачи | Старт потока | Строка выдачи адреса перед первым кубиком |
| Кубик установки прокси | Ветка шаблона | Момент выполнения кубика | Отдельная запись о смене выхода |
| Входные настройки задачи | Одна задача целиком | Перед стартом, руками | Значение переменной в шапке лога |
Вставка instance.SetProxy | Экземпляр браузера потока | Любая точка схемы | То, что я сам записал в лог |
Из таблицы видно главное различие: охват. Менеджер работает на всю задачу сразу, кубик на ветку, вставка на один экземпляр браузера. Профиль потока при этом привязан к экземпляру, поэтому смена выхода вставкой действует ровно до конца работы этого экземпляра и не задевает соседей. Когда мне нужен один и тот же выход у профиля от начала до конца задачи, я работаю четвёртым способом и записываю пару в общую память программы, об этом дальше отдельный раздел.
Ещё одна тонкость касается HTTP-кубиков. Они ходят мимо браузера, своим клиентом, и адрес им передаётся отдельным параметром прямо в вызове. Установка выхода для браузера на них не распространяется совсем. Шаблон, где половина шагов идёт браузером, а половина запросами, легко получает два разных выхода в одном потоке, и такое расхождение видно сразу по журналу принимающей стороны.
Встроенный менеджер списка: как программа раздаёт адреса потокам
Менеджер живёт в настройках самой программы, отдельной вкладкой. Список туда попадает тремя путями: вставкой текста, загрузкой файла и подпиской на ссылку. Третий путь я считаю рабочим, потому что список пула обновляется в реальном времени, и подписка снимает всю ручную работу по переносу строк. Программа перечитывает ссылку по расписанию, я ставлю интервал в 30 минут.
Раздача идёт по кругу. Поток берёт верхнюю доступную строку, помечает её занятой, отпускает по завершении задачи. Рядом стоит флажок запрета одновременной выдачи одной строки нескольким потокам, и включать его нужно осознанно. При включённом флажке число одновременных потоков упирается в длину списка: сорок строк дают сорок потоков и ни одним больше. При выключенном программа спокойно сажает на одну строку хоть десять потоков, что удобно для лёгких HTTP-задач и вредно для браузерных, где принимающая сторона считает сессии по выходу.
Чекер внутри менеджера проверяет строки перед выдачей. У него три поля: адрес проверки, таймаут и периодичность. Адрес проверки я ставлю свой, простую страницу, которая отвечает текстом с адресом обращения. Таймаут держу на 8 секундах: строка, которая не отвечает за это время, в прогоне всё равно даст таймаут на первом же шаге. Периодичность ставлю в 15 минут, чаще смысла нет.
Поле фильтра по стране в менеджере я не трогаю. Пул отдаётся как микс со всего мира, и работа шаблона от географии выхода тут не зависит: важны стабильность канала и предсказуемое поведение под нагрузкой. Для прогонов я беру приватные серверные адреса на собственном оборудовании, где канал не делится с посторонними и темп обращений задаю только я сам.
Отдельно про поведение менеджера при обрыве. Когда строка перестаёт отвечать посреди прогона, программа помечает её нерабочей и выдаёт потоку следующую. Для HTTP-задач это правильно и незаметно. Для браузерных это разрыв сессии на середине сценария: страница уже открыта, куки собраны, а выход внезапно другой. Именно поэтому серьёзные браузерные шаблоны я на менеджер не завязываю и раздаю адреса своим кодом.
Форматы подачи списка и что принимает каждое поле
Пул выдаётся в двух видах сразу, IP:PORT и IP:PORT:LOGIN:PASS, ссылкой или файлом. Первый вид работает после привязки своего адреса в кабинете, второй по паре логина. ZennoPoster понимает оба, но пишутся они в разных местах по-разному, и на этом спотыкаются чаще всего.
менеджер списка программы, одна строка на адрес:
203.0.113.24:8080
203.0.113.25:8080:zp7:Kq3sT9vb
то же для SOCKS5, протокол указывается меткой списка:
203.0.113.26:1080
203.0.113.27:1080:zp7:Kq3sT9vb
В коде и в кубиках строка собирается иначе: протокол пишется префиксом, пара логина уходит перед адресом через собаку. Такую форму принимают и instance.SetProxy, и параметр прокси у HTTP-кубика.
instance.SetProxy("http://203.0.113.24:8080");
instance.SetProxy("socks5://zp7:Kq3sT9vb@203.0.113.27:1080");
| Формат строки | Где принимается | На что обратить внимание |
|---|---|---|
IP:PORT | Менеджер, входные настройки, вставки | Работает после привязки адреса машины в кабинете |
IP:PORT:LOGIN:PASS | Менеджер, файл списка | В коде эта форма разбирается вручную по двоеточиям |
http://IP:PORT | SetProxy, HTTP-кубик | Префикс обязателен, иначе браузер уйдёт напрямую |
socks5://LOGIN:PASS@IP:PORT | SetProxy, HTTP-кубик | Пара логина стоит перед адресом |
| Ссылка на список | Менеджер, подписка | Программа перечитывает адрес по расписанию |
Разбор строки из четырёх частей в форму с префиксом занимает одну строку кода, и эту функцию я держу в общем коде проекта.
string ToUri(string raw, string scheme) {
var p = raw.Split(':');
return p.Length == 4
? scheme + "://" + p[2] + ":" + p[3] + "@" + p[0] + ":" + p[1]
: scheme + "://" + p[0] + ":" + p[1];
}
Протокол под шаблон я выбираю по типу работы. Браузерные сценарии с длинной сессией идут через SOCKS5: пятый протокол несёт соединение целиком, включая запросы вне HTTP, и держит сессию ровнее на долгих сценариях с загрузкой файлов. HTTP-кубики с короткими запросами я оставляю на обычном протоколе, там разницы в поведении нет. Когда под шаблон нужен именно пятый протокол, я беру купить SOCKS5-прокси списком и подставляю строки с префиксом socks5:// во все вставки сразу.
Один выход на всё время работы шаблона в одном потоке
Это главный вопрос темы, и я разберу его подробно. Задача формулируется так: поток номер 7 стартовал, взял строку списка и должен ходить через неё до самого конца сценария, включая все повторы и все перезапуски внутри задачи. Менеджер программы сам по себе такого не гарантирует: он раздаёт строки по кругу, и после разрыва поток спокойно получит другую.
Приём, которым я пользуюсь, держится на общей памяти программы. У ZennoPoster есть глобальные переменные, видимые всем потокам сразу, и я храню там пары вида «номер потока и строка списка». Стартовый кубик проекта смотрит в эту память: пара уже есть, поток берёт свою строку, пары нет, поток вычитывает следующую из файла и записывает пару обратно. Повторный запуск того же потока попадает в готовую запись и получает тот же выход, что был у него час назад.
// стартовый кубик: выдать потоку его строку и запомнить пару
string tid = project.Variables["threadId"].Value;
string key = "zp_pool_" + tid;
string line = Global.Variables.GetVariable("pool", key);
if (string.IsNullOrEmpty(line)) {
lock (SyncObjects.ListSyncer) {
var all = System.IO.File.ReadAllLines(project.Directory + @"\pool.txt");
int idx = int.Parse(tid) % all.Length;
line = all[idx].Trim();
}
Global.Variables.SetVariable("pool", key, line);
project.SendInfoToLog("поток " + tid + " получил строку " + line.Split(':')[0], true);
}
project.Variables["proxy"].Value = line;
instance.SetProxy(ToUri(line, "socks5"));
Блокировка вокруг чтения файла нужна обязательно. Сорок потоков стартуют почти одновременно, и без синхронизации две трети из них прочитают файл в один и тот же момент, получив одинаковый индекс. Я потерял на этом полдня, пока не сообразил посмотреть, сколько разных выходов реально участвует в прогоне: их было четыре при сорока потоках.
Номер потока я держу в собственной переменной проекта и заполняю её из системного счётчика на первом кубике. Дальше эта переменная работает ключом ко всему: по ней собирается статистика, по ней пишется путь к папке профиля, по ней же ищется пара в общей памяти. Единый ключ на все три вещи сильно упрощает разбор журнала утром.
| Схема раздачи | Что происходит при разрыве | Где видно причину отказа |
|---|---|---|
| Список менеджера по кругу | Поток получает следующую доступную строку | Причина размазана по всей задаче |
| Список менеджера с запретом повтора | Поток ждёт освобождения строки | Простой потока в общем счётчике |
| Пара в общей памяти по номеру потока | Поток возвращается к своей строке | Отказ виден по конкретной паре |
| Строка из входных настроек | Задача целиком идёт одним выходом | Одна строка журнала на всю задачу |
Список под такую схему я держу заметно шире числа потоков: подмена сбойной строки тогда занимает секунды и не требует пересборки пар в памяти. Обычно я беру приватные адреса под шаблоны ZennoPoster с запасом в полтора раза от числа экземпляров браузера, и запас этот расходуется как раз на замены по ходу ночи.
Пару я держу до конца прогона и снимаю её явно в финальном кубике, когда задача завершилась успешно. Такой порядок даёт ещё один полезный эффект: строка возвращается в оборот сразу после честного завершения сценария, и следующий поток подхватывает её без ожидания. При аварийном выходе пара остаётся в памяти, поэтому перезапуск задачи продолжает работу с того же выхода.
Передача адреса через входные настройки задачи
Входные настройки это переменные проекта, помеченные особым флагом в ProjectMaker. Они видны в свойствах задачи внутри ZennoPoster и правятся руками без открытия шаблона. Я выношу туда всё, что меняется от прогона к прогону, и адрес пула стоит там первым пунктом.
Входные настройки задачи
pool_url : https://.../list.txt
pool_scheme : socks5
threads_per_ip : 3
retry_on_407 : 2
fails_csv : D:\zp\logs\fails.csv
Читаются они обычным обращением к переменной, никакой отдельной механики там нет. Ссылку на список я скачиваю первым кубиком в файл рядом с проектом и дальше работаю с файлом, потому что сорок потоков, дёргающих ссылку одновременно, дают лишнюю нагрузку на пустом месте.
string url = project.Variables["pool_url"].Value;
string dst = project.Directory + @"\pool.txt";
lock (SyncObjects.FileSyncer) {
var fi = new System.IO.FileInfo(dst);
if (!fi.Exists || (DateTime.Now - fi.LastWriteTime).TotalMinutes > 30)
new System.Net.WebClient().DownloadFile(url, dst);
}
Проверка возраста файла тут ключевая. Список обновляется в реальном времени, и полчаса это тот интервал, при котором свежесть строк меня устраивает, а обращений к ссылке набегает всего два в час на всю машину. Сам файл я держу рядом с проектом, чтобы перенос шаблона на другую машину не тянул за собой правку путей.
Второй способ передачи это прямое значение. Оператор вписывает одну строку в поле входной настройки, шаблон берёт её и работает только через неё, игнорируя и менеджер, и файл. Такой режим я включаю на отладке: когда нужно проверить поведение шаблона на конкретном выходе, лишние источники адресов только мешают. Флаг режима тоже стоит во входных настройках, обычной единицей.
Третий способ пригодится тем, кто запускает задачи из очереди сторонней программой. ZennoPoster принимает значения входных настроек прямо в команде запуска, и адрес приходит туда вместе с остальными параметрами задачи. Я так делаю, когда очередь заданий формируется на сервере и каждой задаче нужен свой выход. Схема стройная: сервер знает, какая строка кому досталась, и в его журнале это записано раньше, чем шаблон начал работу.
Ошибка авторизации внутри шаблона: разбор кода 407
Код 407 означает, что прокси-сервер ждёт пару логина и не получил её. В ZennoPoster эта ошибка проявляется тремя разными способами, и по внешнему виду они не похожи друг на друга. Браузерный кубик показывает пустую страницу и пишет в лог отказ навигации. HTTP-кубик возвращает тело ответа с текстом об авторизации. C#-вставка бросает исключение и роняет всю ветку.
Причин у кода 407 у меня набралось четыре. Первая: строка вида IP:PORT попала в шаблон, который работает по паре логина. Вторая: пара логина есть, но при сборке строки потерялся префикс протокола, и браузер отправил её как обычный адрес. Третья: в поле привязки в кабинете стоит другой адрес машины, и сервер отказывает уже по нему. Четвёртая: пара логина обновилась в кабинете, а файл списка на диске остался старым.
int tries = 0;
int limit = int.Parse(project.Variables["retry_on_407"].Value);
while (tries <= limit) {
instance.ActiveTab.Navigate(project.Variables["target"].Value);
if (instance.ActiveTab.HttpStatusCode != 407) break;
Note("407", line); // счётчик отказов, см. ниже
line = NextLine(project); // следующая строка того же файла
Global.Variables.SetVariable("pool", key, line);
instance.SetProxy(ToUri(line, project.Variables["pool_scheme"].Value));
tries++;
}
Логика повтора у меня жёсткая: две попытки и остановка ветки. Третья попытка при коде 407 бессмысленна, потому что причина сидит в настройке, и перебор строк её не вылечит. Остановка с записью в журнал даёт мне утром точный адрес проблемы вместо тысячи одинаковых строк лога.
Отдельно предупрежу про порядок вызовов. Установку выхода нужно делать до открытия первой страницы в экземпляре браузера. Вызов SetProxy на уже открытой вкладке применяется к следующей навигации, и первый запрос успевает уйти со старой точки. В шаблонах, где кубик установки прокси стоял вторым после запуска браузера, я находил ровно одно постороннее обращение с адреса машины на каждый прогон. Кубик переехал на первую позицию схемы, обращения исчезли.
Профили потоков и связка с антидетект-браузером
Профиль в ZennoPoster это папка с куками, кэшем и настройками экземпляра браузера. Программа умеет сохранять профиль в файл и поднимать его обратно на следующем запуске, и вся ценность приёма держится на постоянстве выхода: профиль, поднятый на новой точке, ведёт себя как незнакомый посетитель. Поэтому имя файла профиля я собираю из того же ключа, что и пара в общей памяти.
string dir = project.Directory + @"\profiles\" + tid;
string file = dir + @"\profile.zpprofile";
System.IO.Directory.CreateDirectory(dir);
if (System.IO.File.Exists(file)) instance.ProfileLoadFromFile(file, true, true, true, true, true, true);
// ... сценарий ...
instance.ProfileSaveToFile(file, true, true, true, true, true, true);
Связка «номер потока, строка списка, папка профиля» держится вместе на всём прогоне и переживает перезапуск задачи. Профиль поднимается с диска, строка приходит из общей памяти, экземпляр браузера получает обе части до первой навигации. Такой порядок я считаю обязательным для любого шаблона, где сценарий идёт дольше пары минут.
Часть работы уходит во внешний антидетект-браузер, когда шаблону нужен отпечаток, которого встроенный экземпляр не даёт. ZennoPoster подключается к такому браузеру по локальному порту отладки и водит его теми же кубиками. Выход в этом случае прописывается в настройках профиля внешнего браузера, и синхронизировать две настройки приходится вручную: строка в файле пула и строка в карточке профиля должны совпадать. Под такие связки я держу отдельный список и беру прокси для профилей Dolphin Anty с той же выдачей списком, чтобы формат строки в обеих программах был одинаковым.
Число профилей на машину я ограничиваю сверху по памяти. Каждый экземпляр браузера съедает от 200 до 400 мегабайт, и сорок экземпляров на машине с 32 гигабайтами уже подходят к пределу. HTTP-шаблоны такой нагрузки не создают вовсе, там сорок потоков занимают меньше гигабайта, поэтому смешанные прогоны я развожу по разным задачам с разным числом потоков.
Потоки задачи и лимит потоков со стороны пула
Потоки в ZennoPoster считаются на двух уровнях. Первый уровень это сама программа: у задачи есть поле максимального числа потоков, и общая сумма по всем активным задачам ограничена лицензией. Второй уровень это пул: доступ выдаётся с лимитом одновременных подключений, и обычный пакет держит 1000 потоков, корпоративный поднимает планку до 3000.
Между этими двумя числами лежит третье, самое практичное: сколько потоков я сажаю на одну строку списка. Для браузерных сценариев моя цифра равна единице, для HTTP-задач тройке. Дальше арифметика простая. Четырнадцать строк по три потока дают 42 потока задачи, и это ровно то число, которое я вписываю в поле программы. Когда шаблон браузерный, число потоков совпадает с числом строк в файле.
| Параметр | Обычный пакет | Корпоративный пакет |
|---|---|---|
| Потоков одновременно | 1000 | до 3000 |
| Привязок адресов в пакете | 2 | 2 |
| Потоков на привязку при двух активных | 500 | до 1500 |
| Сколько занимает браузерный прогон на 40 потоков | 4 процента | около 1,3 процента |
| Сколько занимает HTTP-прогон на 200 потоков | 20 процентов | около 7 процентов |
Две вещи про лимит стоит держать в голове заранее. Пакеты по потокам между собой не складываются: два подряд оформленных пакета оставляют планку на прежней отметке в 1000 потоков. И при двух активных привязках лимит делится между ними пополам, по 500 на каждую сторону. Для одной машины с ZennoPoster это цифры с огромным запасом: даже тяжёлый браузерный прогон на сорок экземпляров занимает от них считанные проценты, и упирается такая машина в память задолго до любых сетевых пределов.
Запас я расходую иначе, и вот тут он действительно пригождается. На одной привязке у меня работает ZennoPoster с браузерными задачами, на второй сервер с HTTP-шаблонами и парсерами, и обе стороны живут в своих 500 потоках без взаимного влияния. Трафик на всех пакетах безлимитный, поэтому загрузка страниц с картинками и выгрузка файлов внутри сценариев на расчёт вообще не влияет. Считать нужно только одновременность.
Отдельная строка расхода это чекер менеджера. Он ходит по списку своими запросами, и при большом списке с частой проверкой эти обращения тоже занимают подключения. Периодичность в 15 минут при списке на несколько десятков строк даёт нагрузку, которую в общем лимите не видно. При проверке каждую минуту картина уже другая, и я такой темп не ставлю.
Статистика отказов: счётчики прямо из кубиков шаблона
Журнал ZennoPoster показывает ход каждой задачи построчно, и на прогоне в несколько тысяч задач читать его целиком невозможно. Поэтому счётчики я веду сам, из шаблона, в отдельный файл с разделителями. Функция записи занимает шесть строк и вызывается из каждой ветки отказа.
void Note(string reason, string line) {
string row = string.Join(";", new [] {
DateTime.Now.ToString("HH:mm:ss"),
project.Variables["threadId"].Value,
line.Split(':')[0],
reason,
project.Variables["step"].Value });
lock (SyncObjects.FileSyncer)
System.IO.File.AppendAllText(project.Variables["fails_csv"].Value, row + "\n");
}
Пять полей в строке подобраны так, чтобы утренний разбор занимал минуты. Время даёт распределение по часам, номер потока связывает запись с профилем, первая часть строки списка показывает точку выхода, причина группирует, шаг показывает место сценария. Переменную шага я обновляю на входе в каждый крупный блок схемы, одним кубиком присвоения.
Разбор последнего прогона выглядел так. Задач в очереди было 4200, отказов набралось 226, что составляет 5,4 процента. Таймауты соединения дали 96 записей и распределились по всем потокам ровно, без перекоса на конкретные строки. Код 407 дал 54 записи, и все они пришлись на первые двенадцать минут прогона: файл списка на диске оказался старше обновления пары логина в кабинете. Капча на выходе дала 38 записей, разрывы SOCKS5-сессии 21, отказ 403 от принимающей стороны 17.
| Причина в файле счётчиков | Что за ней стоит | Что я правлю |
|---|---|---|
timeout | Потоков на строку больше, чем канал отдаёт | Снижаю threads_per_ip на единицу |
407 | Файл списка разошёлся с настройкой доступа | Перекачиваю список по ссылке и рестартую задачу |
captcha | Темп обращений с точки выхода выше порога | Добавляю паузу в блок навигации |
socks_drop | Сессия оборвалась внутри длинного сценария | Возвращаю поток к своей строке из общей памяти |
403 | Принимающая сторона отказала по отпечатку | Пересобираю профиль потока с нуля |
Сводка по файлу собирается любой таблицей за минуту, я группирую по причине и по первой части строки. Ценность цифр появляется на второй неделе наблюдений: видно, какая причина растёт, а какая держится фоном. Ровный фон из таймаутов в один-два процента я считаю нормой прогона и не трогаю. Скачок доли одной причины за сутки означает изменение на принимающей стороне, и разбираться нужно именно там.
Есть ещё один счётчик, который я завёл позже остальных: число задач, завершённых с первой попытки. Отказ сам по себе мало о чём говорит, потому что повтор часто проходит успешно и результат прогона не страдает. Доля успеха с первой попытки показывает настоящее качество настройки, и держу я её выше 92 процентов. Опускается ниже, значит пора смотреть в файл причин глазами.
Ночной прогон на 4200 задач от старта до утреннего отчёта
Соберу всё в один порядок на примере обычного ночного прогона. Шаблон браузерный, сценарий на восемь шагов, очередь на 4200 задач, четырнадцать строк списка и по одному потоку на строку.
Вечером я проверяю три вещи: свежесть файла списка, наличие папок профилей и пустой файл счётчиков с заголовком. Стартовый кубик раздаёт пары «поток и строка» и пишет их в лог, поэтому первые полминуты прогона я смотрю на экран: четырнадцать разных точек выхода в логе означают, что блокировка вокруг чтения файла работает. Дальше я запускаю очередь целиком и ухожу.
Ночью работает автоматика. Пара в общей памяти держит поток на своей строке через все повторы, профиль поднимается с диска перед каждой задачей и сохраняется после неё, счётчики копятся в файле. Утром я открываю файл причин, группирую по двум полям и смотрю распределение по часам. Пятнадцать минут на разбор, и картина ясна целиком.
Что даёт такой порядок на длинной дистанции. Прогон перестаёт быть непредсказуемым: причины отказов известны поимённо, их доли известны в процентах, а любое отклонение видно на следующее утро. Настройки шаблона при этом почти не меняются месяцами, правится обычно число потоков на строку и пауза в блоке навигации. Под постоянные ночные задачи я держу месячный доступ к пулу: прогоны идут каждый день, и разовое оформление доступа под каждую ночь только добавляло бы работы.
Пул на 12 000 активных строк позволяет держать список шире расчёта, и подмена сбойной строки посреди прогона занимает секунды. Автоматическая ротация внутри пула при этом работает в мою пользу: между заходами точка выхода подбирается сама, обращения размазываются по разным адресам, и доля капчи в счётчиках держится у нижней границы. Когда я разворачиваю шаблон на новой машине, я беру адреса для прогонов в ZennoPoster списком в двух форматах сразу, подкладываю файл рядом с проектом и запускаю первую задачу тем же вечером.
Смежные материалы вики дополняют картину с других сторон: разбор Dolphin Anty и подмены отпечатка пригодится, когда сценарий водит внешний браузер, Key Collector и съём частотности показывает ту же арифметику потоков на сборе семантики, а A-Parser и многопоточный сбор закрывает работу без браузера вовсе. Из раздела о сборе данных ближе всего страница про ротацию адресов: там разобрано, как меняется точка выхода между заходами и что это даёт длинным сценариям.