У меня есть веб-страница с кучей ссылок. Я хочу написать сценарий, который будет выгружать все данные, содержащиеся в этих ссылках в локальном файле.
Кто-нибудь сделал это с PHP? В качестве ответа было бы достаточно общих рекомендаций и исправлений.
Мех. Не анализируйте HTML с регулярными выражениями .
Вот версия DOM, вдохновленная Тату:
<?php function crawl_page($url, $depth = 5) { static $seen = array(); if (isset($seen[$url]) || $depth === 0) { return; } $seen[$url] = true; $dom = new DOMDocument('1.0'); @$dom->loadHTMLFile($url); $anchors = $dom->getElementsByTagName('a'); foreach ($anchors as $element) { $href = $element->getAttribute('href'); if (0 !== strpos($href, 'http')) { $path = '/' . ltrim($href, '/'); if (extension_loaded('http')) { $href = http_build_url($url, array('path' => $path)); } else { $parts = parse_url($url); $href = $parts['scheme'] . '://'; if (isset($parts['user']) && isset($parts['pass'])) { $href .= $parts['user'] . ':' . $parts['pass'] . '@'; } $href .= $parts['host']; if (isset($parts['port'])) { $href .= ':' . $parts['port']; } $href .= dirname($parts['path'], 1).$path; } } crawl_page($href, $depth - 1); } echo "URL:",$url,PHP_EOL,"CONTENT:",PHP_EOL,$dom->saveHTML(),PHP_EOL,PHP_EOL; } crawl_page("http://hobodave.com", 2);
Изменить: я исправил некоторые ошибки из версии Tatu (теперь работает с относительными URL-адресами).
Изменить: я добавил новый бит функциональности, который не позволяет ему дважды следовать одному URL.
Изменить: повторить вывод в STDOUT, чтобы вы могли перенаправить его в любой файл, который вы хотите
Изменить: Исправлена ошибка, отмеченная Джорджем в его ответе. Относительные URL-адреса больше не будут добавляться к концу URL-адреса, но перезаписывают его. Спасибо Джорджу за это. Обратите внимание, что ответ Джорджа не учитывает ни одного из: https, user, pass или port. Если у вас загружено расширение HTTP PECL, это довольно просто делается с помощью http_build_url . В противном случае мне придется вручную склеивать, используя parse_url. Еще раз спасибо Джордж.
Здесь моя реализация основана на приведенном выше примере / ответе.
КРАСНЫЙ КЛАСС:
class crawler { protected $_url; protected $_depth; protected $_host; protected $_useHttpAuth = false; protected $_user; protected $_pass; protected $_seen = array(); protected $_filter = array(); public function __construct($url, $depth = 5) { $this->_url = $url; $this->_depth = $depth; $parse = parse_url($url); $this->_host = $parse['host']; } protected function _processAnchors($content, $url, $depth) { $dom = new DOMDocument('1.0'); @$dom->loadHTML($content); $anchors = $dom->getElementsByTagName('a'); foreach ($anchors as $element) { $href = $element->getAttribute('href'); if (0 !== strpos($href, 'http')) { $path = '/' . ltrim($href, '/'); if (extension_loaded('http')) { $href = http_build_url($url, array('path' => $path)); } else { $parts = parse_url($url); $href = $parts['scheme'] . '://'; if (isset($parts['user']) && isset($parts['pass'])) { $href .= $parts['user'] . ':' . $parts['pass'] . '@'; } $href .= $parts['host']; if (isset($parts['port'])) { $href .= ':' . $parts['port']; } $href .= $path; } } // Crawl only link that belongs to the start domain $this->crawl_page($href, $depth - 1); } } protected function _getContent($url) { $handle = curl_init($url); if ($this->_useHttpAuth) { curl_setopt($handle, CURLOPT_HTTPAUTH, CURLAUTH_ANY); curl_setopt($handle, CURLOPT_USERPWD, $this->_user . ":" . $this->_pass); } // follows 302 redirect, creates problem wiht authentication // curl_setopt($handle, CURLOPT_FOLLOWLOCATION, TRUE); // return the content curl_setopt($handle, CURLOPT_RETURNTRANSFER, TRUE); /* Get the HTML or whatever is linked in $url. */ $response = curl_exec($handle); // response total time $time = curl_getinfo($handle, CURLINFO_TOTAL_TIME); /* Check for 404 (file not found). */ $httpCode = curl_getinfo($handle, CURLINFO_HTTP_CODE); curl_close($handle); return array($response, $httpCode, $time); } protected function _printResult($url, $depth, $httpcode, $time) { ob_end_flush(); $currentDepth = $this->_depth - $depth; $count = count($this->_seen); echo "N::$count,CODE::$httpcode,TIME::$time,DEPTH::$currentDepth URL::$url <br>"; ob_start(); flush(); } protected function isValid($url, $depth) { if (strpos($url, $this->_host) === false || $depth === 0 || isset($this->_seen[$url]) ) { return false; } foreach ($this->_filter as $excludePath) { if (strpos($url, $excludePath) !== false) { return false; } } return true; } public function crawl_page($url, $depth) { if (!$this->isValid($url, $depth)) { return; } // add to the seen URL $this->_seen[$url] = true; // get Content and Return Code list($content, $httpcode, $time) = $this->_getContent($url); // print Result for current Page $this->_printResult($url, $depth, $httpcode, $time); // process subPages $this->_processAnchors($content, $url, $depth); } public function setHttpAuth($user, $pass) { $this->_useHttpAuth = true; $this->_user = $user; $this->_pass = $pass; } public function addFilterPath($path) { $this->_filter[] = $path; } public function run() { $this->crawl_page($this->_url, $this->_depth); } }
ПРИМЕНЕНИЕ:
// USAGE $startURL = 'http://YOUR_URL/'; $depth = 6; $username = 'YOURUSER'; $password = 'YOURPASS'; $crawler = new crawler($startURL, $depth); $crawler->setHttpAuth($username, $password); // Exclude path with the following structure to be processed $crawler->addFilterPath('customer/account/login/referer'); $crawler->run();
В простейшей форме:
function crawl_page($url, $depth = 5) { if($depth > 0) { $html = file_get_contents($url); preg_match_all('~<a.*?href="(.*?)".*?>~', $html, $matches); foreach($matches[1] as $newurl) { crawl_page($newurl, $depth - 1); } file_put_contents('results.txt', $newurl."\n\n".$html."\n\n", FILE_APPEND); } } crawl_page('http://www.domain.com/index.php', 5);
Эта функция получит содержимое со страницы, затем сканирует все найденные ссылки и сохраняет содержимое в 'results.txt'. Функции принимают второй параметр, depth, который определяет, как долго должны соблюдаться ссылки. Пропустите 1, если вы хотите разобрать только ссылки с данной страницы.
Зачем использовать PHP для этого, когда вы можете использовать wget , например
wget -r -l 1 http://www.example.com
Инструкции по анализу содержимого см. В разделе « Лучшие методы для анализа HTML» и используйте функцию поиска для примеров . Как разобрать HTML, ответ был получен несколько раз.
С некоторыми небольшими изменениями в коде hobodave, вот код, который вы можете использовать для сканирования страниц. Для этого необходимо, чтобы расширение curl было включено на вашем сервере.
<?php //set_time_limit (0); function crawl_page($url, $depth = 5){ $seen = array(); if(($depth == 0) or (in_array($url, $seen))){ return; } $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_TIMEOUT, 30); curl_setopt($ch, CURLOPT_RETURNTRANSFER,1); $result = curl_exec ($ch); curl_close ($ch); if( $result ){ $stripped_file = strip_tags($result, "<a>"); preg_match_all("/<a[\s]+[^>]*?href[\s]?=[\s\"\']+"."(.*?)[\"\']+.*?>"."([^<]+|.*?)?<\/a>/", $stripped_file, $matches, PREG_SET_ORDER ); foreach($matches as $match){ $href = $match[1]; if (0 !== strpos($href, 'http')) { $path = '/' . ltrim($href, '/'); if (extension_loaded('http')) { $href = http_build_url($href , array('path' => $path)); } else { $parts = parse_url($href); $href = $parts['scheme'] . '://'; if (isset($parts['user']) && isset($parts['pass'])) { $href .= $parts['user'] . ':' . $parts['pass'] . '@'; } $href .= $parts['host']; if (isset($parts['port'])) { $href .= ':' . $parts['port']; } $href .= $path; } } crawl_page($href, $depth - 1); } } echo "Crawled {$href}"; } crawl_page("http://www.sitename.com/",3); ?>
Я объяснил этот учебник в этом учебном пособии для искателя
Как уже упоминалось, инфраструктура искателя готова для настройки там, но если то, что вы делаете, так же просто, как вы упомянули, вы можете сделать это с нуля довольно легко.
Скремблирование ссылок: http://www.phpro.org/examples/Get-Links-With-DOM.html
Сбрасывание результатов в файл: http://www.tizag.com/phpT/filewrite.php
Хободав, ты был очень близок. Единственное, что я изменил, – это выражение if, которое проверяет, начинает ли атрибут href найденного тега привязки «http». Вместо того, чтобы просто добавлять переменную $ url, которая содержала бы пропущенную страницу, вы должны сначала разбить ее на хост, который можно выполнить с помощью функции parse_url php.
<?php function crawl_page($url, $depth = 5) { static $seen = array(); if (isset($seen[$url]) || $depth === 0) { return; } $seen[$url] = true; $dom = new DOMDocument('1.0'); @$dom->loadHTMLFile($url); $anchors = $dom->getElementsByTagName('a'); foreach ($anchors as $element) { $href = $element->getAttribute('href'); if (0 !== strpos($href, 'http')) { /* this is where I changed hobodave's code */ $host = "http://".parse_url($url,PHP_URL_HOST); $href = $host. '/' . ltrim($href, '/'); } crawl_page($href, $depth - 1); } echo "New Page:<br /> "; echo "URL:",$url,PHP_EOL,"<br />","CONTENT:",PHP_EOL,$dom->saveHTML(),PHP_EOL,PHP_EOL," <br /><br />"; } crawl_page("http://hobodave.com/", 5); ?>
Вопрос в том, как получить исходный код ajax-вызовов? это не сканируется, например, как сканировать изображения по ссылке вроде этого? http://www.tiendeo.nl/Catalogi/amsterdam/16558&subori=web_sliders&buscar=Boni&sw=1366
PHPCrawl – очень хорошая и продуманная структура искателя.
Я использовал код @ hobodave с этой небольшой настройкой, чтобы предотвратить повторное сканирование всех вариантов фрагмента одного URL:
<?php function crawl_page($url, $depth = 5) { $parts = parse_url($url); if(array_key_exists('fragment', $parts)){ unset($parts['fragment']); $url = http_build_url($parts); } static $seen = array(); ...
не<?php function crawl_page($url, $depth = 5) { $parts = parse_url($url); if(array_key_exists('fragment', $parts)){ unset($parts['fragment']); $url = http_build_url($parts); } static $seen = array(); ...
Затем вы также можете опустить $parts = parse_url($url);
линии внутри цикла for.
Вы можете попробовать это, это может помочь вам
$search_string = 'american golf News: Fowler beats stellar field in Abu Dhabi'; $html = file_get_contents(url of the site); $dom = new DOMDocument; $titalDom = new DOMDocument; $tmpTitalDom = new DOMDocument; libxml_use_internal_errors(true); @$dom->loadHTML($html); libxml_use_internal_errors(false); $xpath = new DOMXPath($dom); $videos = $xpath->query('//div[@class="primary-content"]'); foreach ($videos as $key => $video) { $newdomaindom = new DOMDocument; $newnode = $newdomaindom->importNode($video, true); $newdomaindom->appendChild($newnode); @$titalDom->loadHTML($newdomaindom->saveHTML()); $xpath1 = new DOMXPath($titalDom); $titles = $xpath1->query('//div[@class="listingcontainer"]/div[@class="list"]'); if(strcmp(preg_replace('!\s+!',' ', $titles->item(0)->nodeValue),$search_string)){ $tmpNode = $tmpTitalDom->importNode($video, true); $tmpTitalDom->appendChild($tmpNode); break; } } echo $tmpTitalDom->saveHTML();