Страница 1 из 2

Авторедактирование списка url-ов на основе HTTP статус кодов

Добавлено: Чт янв 26, 2017 21:11
Hjkma
Хочется иметь скрипт, который будет брать список url-ов из буфера обмена, проверять их на http статус коды, отсеивать url-ы, возвращающие статусы 404, и помещать отредактированный список url-ов обратно в буфер обмена. Нужно чтобы очистить список веб-адресов от неработающих сайтов и веб-страниц. И делать это нужно будет регулярно, поэтому требуется кнопка.
Буду очень признателен за скрипт, если кто-то из скриптописателей это сможет реализовать. Спасибо.

Добавлено: Чт янв 26, 2017 22:03
MVV
Батник (внутри вызывает повершелл) фильтрует урлы в буфере по валидным кодам возврата (2хх, 3хх - другие коды вызывают исключение внутри GetResponse):

Код: Выделить всё

@echo off
set "ARG_0=%~dpnx0"
powershell.exe -NoProfile -STA "iex (([IO.File]::ReadAllText($Env:ARG_0)) -replace '^(.*\n)*?.*<::::>.*\n', '')" & goto :EOF


Add-Type -Assembly System.Windows.Forms;


function testUrl($url, $timeout) {
	try {
		$webReq = [Net.WebRequest]::Create($url);
		$webReq.Method = 'HEAD';
		$webReq.Timeout = $timeout;
		$webRsp = $webReq.GetResponse();
		$webRsp.Close();
		Write-Host "``$url``: $([int]$webRsp.StatusCode)";
		$true;
	}
	catch {
		Write-Host "``$url``: $_";
	}
}


$urls = [Windows.Forms.Clipboard]::GetText() -split '\r?\n';
$goodUrls = @($urls | ? { $_ -match '^\w+://' -and (testUrl $_ $timeout) });
if ([Windows.Forms.MessageBox]::Show("Please click OK to copy $($goodUrls.Length) of $($urls.Length) urls to clipboard.", 'Url Filter', 1) -eq 1) {
	[Windows.Forms.Clipboard]::SetText(($goodUrls -join "`n") + "`n");
}
(добавлены установка метода HEAD и окошко сообщения в конце)

Добавлено: Чт янв 26, 2017 22:21
Hjkma
MVV
Спасибо за скрипт. А есть ли возможность как-то оптимизировать или ускорить скрипт? Просто нужно обрабатывать тысячи ссылок, а скрипт обрабатывает их со скоростью 1 url за 1-2 секунды. За 1000 ссылок выйдет работы примерно на 16 минут, так что это долго.

Добавлено: Чт янв 26, 2017 22:30
MVV
Попробуй использовать метод HEAD, чтобы не грузить данные:

Код: Выделить всё

		$webReq = [Net.WebRequest]::Create($url);
		$webReq.Method = 'HEAD';
		...
Также можно уменьшить значение $timeout, чтобы меньше ждать ответа от мертвых серверов, но при маленьком значении живые адреса могут тоже быть отсеяны.

Добавлено: Чт янв 26, 2017 22:35
Hjkma
MVV
А где это в скрипте прописать?
Вставить нужно выше или ниже этого куска кода?

Код: Выделить всё

      $webReq = [Net.WebRequest]::Create($url);
      $webReq.Timeout = $timeout;
      $webRsp = $webReq.GetResponse();
      $webRsp.Close();
      Write-Host "``$url``: $([int]$webRsp.StatusCode)";
      $true;

Добавлено: Чт янв 26, 2017 22:44
MVV
Вставить нужно 1 новую строку (установка метода) после 1 уже имеющейся (создание объекта).

Ещё можно перед последней строкой (копирование в буфер) добавить отображение окошка, чтобы забивать буфер после долгой операции только по команде (писать можно и по-русски, но тогда необходимо сохранить файл в кодировке UTF-8 без BOM):

Код: Выделить всё

$null = [Windows.Forms.MessageBox]::Show("Please click OK to copy $($goodUrls.Length) good urls to clipboard.", 'Url Filter');
В общем, отредактировал скрипт в своём посте.

Добавлено: Чт янв 26, 2017 23:03
Hjkma
MVV
Сделал все это, скорость теперь примерно 1 url за одну секунду.
С таймаутом можно поэкспериментировать, но в принципе большая часть ссылок - и так живые, так что они отвечают в пределах 1-2 секунд, так что уменьшение таймаута не особо поможет.
А можно ли сделать так, чтобы батник обрабатывал ссылки в несколько проходов, то есть не по одной ссылке за один раз, а чтобы было 10 процессов и соединений, которые обрабатывают ссылки? Я не программист и не знаю как корректно это описать. Приведу пример. Есть downloader manager-ы, которым можно скормить тонну url-ов и можно там в настройках прописать количество соединений, например 10, тогда они скачивают по 10 файлов за один раз. Так и здесь нельзя применить тот же принцип?

Добавлено: Чт янв 26, 2017 23:16
Flasher
Hjkma
Этот способ пингования называется асинхронным.
Темповый файл стоит на дозаписи, по достижению лимита счётчика содержимое выбрасывается в буфер.

Добавлено: Пт янв 27, 2017 00:47
Hjkma
От MVV что-то нету ответа.
Может ли кто-нибудь ввести в скрипт метод асинхронного пингования с потоками от 10 раз? Чтобы скорость получилась на 10 раз быстрее.

Добавлено: Пт янв 27, 2017 00:57
MVV
Может, MVV устал и ушел спать? :roll:

С асинхронностью будет как-то так:

Код: Выделить всё

@echo off
set "ARG_0=%~dpnx0"
powershell.exe -NoProfile -STA "iex (([IO.File]::ReadAllText($Env:ARG_0)) -replace '^(.*\n)*?.*<::::>.*\n', '')" & goto :EOF


Add-Type -Assembly System.Windows.Forms;


function testUrls($urls, $timeout, $workerCount = 2) {
	$job = @{
		queue = [Collections.Queue]::Synchronized((New-Object Collections.Queue (, $urls)));
		list = [Collections.ArrayList]::Synchronized((New-Object Collections.ArrayList));
		timeout = $timeout;
	};
	Write-Host "Checking $($urls.Length) urls using $workerCount workers...";
	$workerScript = {
		$job = $args[0];
		try {
			while (1) {
				$url = $job.queue.Dequeue();
				try {
					$webReq = [Net.WebRequest]::Create($url);
					$webReq.Method = 'HEAD';
					$webReq.Timeout = $job.timeout;
					$webRsp = $webReq.GetResponse();
					$webRsp.Close();
					$null = $job.list.Add($url);
					"$url`: $($webRsp.StatusCode)";
				}
				catch {
					"$url`: $_";
				}
			}
		}
		catch {}
	};
	$workers = @((1 .. $workerCount) | % { $w = [PowerShell]::Create().AddScript($workerScript).AddArgument($job); @{ w = $w; r = $w.BeginInvoke() } });
	$workers | % { Write-Host ($_.w.EndInvoke($_.r) -join "`n") };
	$job.list;
}

$timeout = 10000;
$workerCount = 10;
$urls = @([Windows.Forms.Clipboard]::GetText() -split '\r?\n' | ? { $_ -match '^\w+://' });
$startTime = [DateTime]::Now;
$goodUrls = @(testUrls $urls $timeout $workerCount);
$workTime = [int]([DateTime]::Now - $startTime).TotalSeconds;
if ([Windows.Forms.MessageBox]::Show("Approved $($goodUrls.Length) of $($urls.Length) urls in $workTime seconds.`nPlease click OK to copy urls to clipboard.", 'Url Filter', 1) -eq 1) {
	[Windows.Forms.Clipboard]::SetText(($goodUrls -join "`n") + "`n");
}
Таймаут и число потоков можно поменять, но если скрипт исчерпает лимит полуоткрытых соединений, могут быть проблемы.

Добавлено: Пт янв 27, 2017 01:28
Hjkma
MVV
Новый скрипт обработал 1925 ссылок за 6 минут. То есть, скорость получилась 1 секунда = 5 ссылок. В принципе, приемлемо.
Но есть проблема: старый вариант из тех 1925 ссылок возвращал 1217 работающих ссылок. Новый вариант вернул 1160 ссылок. Я нашел ссылки, которых вернул старый скрипт и не вернул новый скрипт, они все работающие, которые возвращают статус 200 и которые открываются в браузере. Еще раз запустил скрипт, на этот раз из тех же 1925 ссылок он вернул 1124. Что-то не так в скрипте. Могу прислать в ЛС список ссылок для тестирования.
Замечу, что и во время работы старого скрипта, и во время работы нового скрипта у меня все время работает торрент, который на полную скорость качает сериал. Может это влияет?
Upd. В третий раз запустил скрипт, на этот раз поставив число потоков на 5. Вернуло 1205 ссылок. Нормальное значение должно быть - 1217 ссылок. В четвертый раз, на 3 потоков вернуло уже 1217 ссылок. В пятый раз, на 3 потоков выдало цифру в 1215 ссылок. Как-то напрягает в работе скрипта, что не до конца уверен в конечном результате, т.к. скрипт выдает разные цифры. Может есть возможность встроить в работу скрипта какой-то механизм, который бы предотвращал такие ситуации, например в конце повторно бы перепроверял те ссылки, которых скрипт отсеял?

Добавлено: Пт янв 27, 2017 10:24
MVV
Естественно, если возникает ошибка или таймаут, ссылка отсеивается. А что в консоль выдается по тем ссылкам, которые рабочие, но были отсеяны скриптом?

Если в фоне работает торрент, то это может влиять - особенно если он качает на скорости, близкой к тарифному лимиту.

Чтобы детект работал стабильнее, надо увеличивать таймаут, а также следить, чтобы лимит полуоткрытых соединений не превышался (иначе повершелл просто не сможет открывать новые соединения) - например, можно смотреть открытые соединения в Process Hacker.

Добавлено: Пт янв 27, 2017 15:24
Hjkma
MVV
Так я не могу отследить процессы в консоле, в начале долго стоит надпись "Checking 1925 url using 10 workers", а потом как время работы скрипта подходит к концу, то в консоле очень быстро скопом пролистывается список урлов и их коды состояния и затем консоль завершается.
Попробую увеличить таймаут до 100000 с числом потоков на 10.
Вот про лимит полуоткрытых соединений не понял - у меня же Windows 7, вроде там нет ограничения?

Добавлено: Пт янв 27, 2017 18:37
Avada
C учётом специфики поставленного вопроса и обсуждения тема переносится в "Программное обеспечение".

Добавлено: Пт янв 27, 2017 19:16
Hjkma
По итогам тестирования.
Второй вариант скрипта все время выдает разные результаты, то 1165 ссылок, то 1190, то 1203, сколько бы не увеличивал время таймаута и не уменьшал число потоков. Первый вариант скрипта всегда из тех 1925 ссылок выдает те же 1217 ссылок и все они рабочие. Может что-то в работе второго скрипта не так? Кстати, по консоли ошибся, это окно не исчезает, если не нажимать кнопку "ок", когда предлагают скопировать список ссылок в буфер. Но в консоли показываются не все ссылки, только 300 строк и больше ничего. Правда из тех 300 строк нашлись ссылки, которых скрипт отсеял и они рабочие. Вот их коды состояния
(ссылка): Исключение при вызове "GetResponse" с "0" арг
ументами: "Невозможно разрешить удаленное имя: '(ссылка)"
Можно ли что-нибудь прописать в работе скрипта, чтобы сделать его как можно стабильнее? К примеру сделать так, чтобы в конце работы скрипт еще раз обрабатывал ссылки, которые возвращают приведенный выше код ошибки. С одним потоком и увеличенным таймаутом.