Intereting Posts
Как безопасно отправлять баллы из приложения, которое использует только вход в facebook? Экспорт PHP в Excel приводит к неправильному формату и нежелательным знакам как я могу сопоставить две строки, даже если они отличаются 1 символом? Отчет, подготовленный PDO для обновления, не работает должным образом PHP – если / else, for, foreach, while – без фигурных скобок? Числа округления PHP Создание изображения из URL-адреса данных PHP-код не позволяет отправлять несколько входных значений через мою форму как проверить, работает ли ruby-скрипт в фоновом режиме из PHP-скрипта? Как установить значение по умолчанию для столбца временной метки в текущую временную метку с миграциями Laravel? PHP – подсчет загрузок прерывистый сервер MySQL ушел с ошибкой CodeIditor base url не recongnized в javascript WordPress Получить идентификатор страницы вне цикла Сортировка массива по последним символам пары значений?

Лучший способ прочесть сайт?

Я пытаюсь создать программу, которая захватывает данные с сайта на несколько раз, и я ищу способ сделать это без огромных задержек в этом процессе.

В настоящее время я использую следующий код, и он довольно медленный (хотя он только захватывает имена четырех народов, я ожидаю, что вы сделаете около 100 за раз):

$skills = array( "overall", "attack", "defense", "strength", "constitution", "ranged", "prayer", "magic", "cooking", "woodcutting", "fletching", "fishing", "firemaking", "crafting", "smithing", "mining", "herblore", "agility", "thieving", "slayer", "farming", "runecrafting", "hunter", "construction", "summoning", "dungeoneering" ); $participants = array("Zezima", "Allar", "Foot", "Arma150", "Green098", "Skiller 703", "Quuxx");//explode("\r\n", $_POST['names']); $skill = isset($_GET['skill']) ? array_search($skills, $_GET['skill']) : 0; display($participants, $skills, array_search($_GET['skill'], $skills)); function getAllStats($participants) { $stats = array(); for ($i = 0; $i < count($participants); $i++) { $stats[] = getStats($participants[$i]); } return $stats; } function display($participants, $skills, $stat) { $all = getAllStats($participants); for ($i = 0; $i < count($participants); $i++) { $rank = getSkillData($all[$i], 0, $stat); $level = getSkillData($all[$i], 1, $stat); $experience = getSkillData($all[$i], 3, $stat); } } function getStats($username) { $curl = curl_init("http://hiscore.runescape.com/index_lite.ws?player=" . $username); curl_setopt ($curl, CURLOPT_CONNECTTIMEOUT, $timeout); curl_setopt ($curl, CURLOPT_USERAGENT, sprintf("Mozilla/%d.0", rand(4, 5))); curl_setopt ($curl, CURLOPT_HEADER, (int) $header); curl_setopt ($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt ($curl, CURLOPT_VERBOSE, 1); $httpCode = curl_getinfo($curl, CURLINFO_HTTP_CODE); $output = curl_exec($curl); curl_close ($curl); if (strstr($output, "<html><head><title>")) { return false; } return $output; } function getSkillData($stats, $row, $skill) { $stats = explode("\n", $stats); $levels = explode(",", $stats[$skill]); return $levels[$row]; } 

Когда я сравнивал это, ушло около 5 секунд, что не так уж плохо, но представьте, если бы я делал это еще 93 раза. Я понимаю, что это будет не мгновенно, но я хочу снимать менее 30 секунд. Я знаю, что это возможно, потому что я видел веб-сайты, которые делают что-то подобное, и они действуют в течение 30 секунд.

Я читал об использовании кеширования данных, но это не сработает, потому что, просто, оно будет старым. Я использую базу данных (далее я еще не получил эту часть) для хранения старых данных и получения новых данных, которые будут в реальном времени (что вы видите ниже).

Есть ли способ сделать что-то подобное без значительных задержек (и, возможно, перегружать сервер, который я читаю)?

PS: Веб-сайт, который я читаю, это просто текст, у него нет HTML-анализа, что должно сократить время загрузки. Вот пример того, как выглядит страница (они все одинаковые, просто разные числа):
69,2496,1285458634 10982,99,33055154 6608,99,30955066 6978,99,40342518 12092,99,36496288 13247,99,21606979 2812,99,13977759 926,99,36988378 415,99,153324269 329,99,59553081 472,99,40595060 2703,99,28297122 281,99,36937100 1017,99,19418910 276,99,27539259 792,99,34289312 3040,99,16675156 82,99,39712827 80,99,104504543 2386,99,21236188 655,99,28714439 852,99,30069730 29,99,200000000 3366,99,15332729 2216,99,15836767 154,120,200000000 -1,-1 -1,-1 -1,-1 -1,-1 -1,-1 30086,2183 54640,1225 89164,1028 123432,1455 -1,-1 -1,-1

Мой предыдущий бенчмарк с этим методом против curl_multi_exec :

 function getTime() { $timer = explode(' ', microtime()); $timer = $timer[1] + $timer[0]; return $timer; } function benchmarkFunctions() { $start = getTime(); old_f(); $end = getTime(); echo 'function old_f() took ' . round($end - $start, 4) . ' seconds to complete<br><br>'; $startt = getTime(); new_f(); $endd = getTime(); echo 'function new_f() took ' . round($endd - $startt, 4) . ' seconds to complete'; } function old_f() { $test = array("AET", "Ts Danne", "Funkymunky11", "Fast993", "Fast99Three", "Jeba", "Quuxx"); getAllStats($test); } function new_f() { $test = array("AET", "Ts Danne", "Funkymunky11", "Fast993", "Fast99Three", "Jeba", "Quuxx"); $curl_arr = array(); $master = curl_multi_init(); $amt = count($test); for ($i = 0; $i < $amt; $i++) { $curl_arr[$i] = curl_init('http://hiscore.runescape.com/index_lite.ws?player=' . $test[$i]); curl_setopt($curl_arr[$i], CURLOPT_RETURNTRANSFER, true); curl_multi_add_handle($master, $curl_arr[$i]); } do { curl_multi_exec($master, $running); } while ($running > 0); for ($i = 0; $i < $amt; $i++) { $results = curl_exec($curl_arr[$i]); } } 

Вы можете повторно использовать завитки. Кроме того, я изменил ваш код, чтобы проверить httpCode вместо использования strstr . Быстрее.

Кроме того, вы можете настроить завиток, чтобы сделать это параллельно, что я никогда не пробовал. См. http://www.php.net/manual/en/function.curl-multi-exec.php

Улучшенный getStats() с повторно используемым ручкой завитка.

 function getStats(&$curl,$username) { curl_setopt($curl, CURLOPT_URL, "http://hiscore.runescape.com/index_lite.ws?player=" . $username); $output = curl_exec($curl); if (curl_getinfo($curl, CURLINFO_HTTP_CODE)!='200') { return null; } return $output; } 

Применение:

 $participants = array("Zezima", "Allar", "Foot", "Arma150", "Green098", "Skiller 703", "Quuxx"); $curl = curl_init(); curl_setopt ($curl, CURLOPT_CONNECTTIMEOUT, 0); //dangerous! will wait indefinitely curl_setopt ($curl, CURLOPT_USERAGENT, sprintf("Mozilla/%d.0", rand(4, 5))); curl_setopt ($curl, CURLOPT_HEADER, false); curl_setopt ($curl, CURLOPT_RETURNTRANSFER, 1); curl_setopt ($curl, CURLOPT_SSL_VERIFYPEER, 0); curl_setopt ($curl, CURLOPT_VERBOSE, 1); //try: curl_setopt($curl, CURLOPT_HTTPHEADER, array( 'Connection: Keep-Alive', 'Keep-Alive: 300' )); header('Content-type:text/plain'); foreach($participants as &$user) { $stats = getStats($curl, $user); if($stats!==null) { echo $stats."\r\n"; } } curl_close($curl); 

Когда вы выполняете множество сетевых запросов, подобных этому, вы находитесь во власти сети и удаленного сервера в отношении того, сколько времени требуется для ответа.

Из-за этого лучший способ сделать все ваши запросы завершенными за короткий промежуток времени – это, вероятно, сделать их все сразу. Создайте новую нить для каждого из них. Для размера данных, с которыми вы работаете, возможно, вполне возможно сделать буквально все сразу, но если это проблема, тогда, возможно, попробуйте примерно 20 одновременно.

EDIT: Я только понял, что вы используете PHP, у которого нет потоков. Ну, наверное, плохой выбор языка, для начала. Но вы могли бы эмулировать потоки, открывая новые процессы. Однако это может быть крушение, если PHP работает внутри процесса веб-сервера, так как он клонирует весь сервер. Я рассмотрю, предлагает ли PHP какие-то асинхронные веб-запросы, которые могли бы дать аналогичный эффект.

EDIT 2:

Вот страница, где обсуждается, как запустить HTTP-запрос в фоновом режиме с помощью PHP:

http://w-shadow.com/blog/2007/10/16/how-to-run-a-php-script-in-the-background/

Однако это «огонь и забыть», это не позволяет вам подобрать ответ на ваш запрос и что-то сделать с ним. Один из подходов, который вы могли бы предпринять, заключается в том, чтобы использовать этот метод для устранения многих запросов на другую страницу на вашем собственном сервере, и каждый из этих страниц делает один запрос на удаленный сервер. (Или каждый рабочий запрос может обрабатывать пакет запросов, если вы не хотите запускать слишком много запросов одновременно).

Вам все равно понадобится способ собрать все результаты и способ обнаружить, когда вся процедура будет завершена, чтобы вы могли отображать результаты. Я бы, вероятно, использовал либо базу данных, либо файловую систему для координации между различными процессами.

(Опять же, выбор более мощного языка для этой задачи, вероятно, будет полезен. В области языков, подобных PHP, я знаю, что Perl очень легко справляется с этой проблемой с помощью «использования потоков», и я думаю, что Python или Ruby тоже будут. )

ИЗМЕНИТЬ 3:

Другое решение, использующее оболочку UNIX, чтобы обойти ограничения PHP, выполняя работу в отдельных процессах. Вы можете сделать что-то вроде этого:

 echo '$urlList' | xargs -P 10 -r -n1 wget 

Возможно, вам захочется немного поиграть с параметрами wget , например, явно указать выходной файл, но это вообще идея. Вместо wget вы также можете использовать curl или даже просто вызвать скрипт PHP, который предназначен для запуска из командной строки, если вы хотите полностью контролировать работу по извлечению страниц.

Опять же, с этим решением у вас все еще есть проблема распознавания, когда работа выполнена, чтобы вы могли показать результаты.

Я получил эту идею для этого подхода с этой страницы:

http://www.commandlinefu.com/commands/view/3269/parallel-file-downloading-with-wget

Поскольку вы делаете несколько запросов на один и тот же хост, вы можете повторно использовать ручку curl, и если сайт поддерживает запросы keep-alive, это может ускорить ваш процесс, удачный бит по многим запросам.

Вы можете изменить свою функцию следующим образом:

 function getStats($username) { static $curl = null; if ($curl == null) { $curl = curl_init(); } curl_setopt($curl, CURLOPT_URL, "http://hiscore.runescape.com/index_lite.ws?player=" . $username); curl_setopt ($curl, CURLOPT_HTTPHEADER, array('Connection: Keep-Alive')); //... // remove curl_close($curl) } 

Выполнение этого приведет к тому, что вам не придется закрывать и восстанавливать сокет для каждого запроса пользователя. Он будет использовать одно и то же соединение для всех запросов.

curl – очень хороший способ прочитать содержание веб-сайта – я полагаю, ваша проблема связана с тем, что для загрузки требуется одна страница. Если вы можете получить все 100 страниц в параллель, вы, вероятно, все обработали менее чем за 10 секунд.

Чтобы избежать работы с потоками, замками, семафорами и всеми сложными материалами в потоках, прочитайте эту статью и узнайте, как сделать приложение параллельным почти бесплатно.