Как мне сделать простой краулер на PHP?

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

Кто-нибудь делал это с PHP? В качестве ответа достаточно общих рекомендаций и ошибок.


person KJ Saxena    schedule 22.02.2010    source источник


Ответы (15)


Мех. Не анализируйте 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);

Изменить: я исправил некоторые ошибки в версии Тату (теперь работает с относительными URL-адресами).

Изменить: я добавил новую функциональность, которая не позволяет ему дважды переходить по одному и тому же URL-адресу.

Изменить: теперь вывод выводится в STDOUT, чтобы вы могли перенаправить его в любой файл, который хотите.

Изменить: исправлена ​​ошибка, указанная Джорджем в его ответе. Относительные URL-адреса больше не будут добавляться в конец пути URL-адреса, а будут перезаписывать его. Спасибо Джорджу за это. Обратите внимание, что ответ Джорджа не учитывает ни https, ни пользователя, ни пароль, ни порт. Если у вас загружено расширение http://us3.php.net/manual/en/book.http.php PECL, это довольно просто сделать с помощью http://us3.php.net/manual/en/function.http-build-url.php. В противном случае мне придется вручную склеивать с помощью parse_url. Еще раз спасибо, Джордж.

person hobodave    schedule 22.02.2010
comment
Могу ли я порекомендовать использовать curl для получения страницы, а затем для управления / перемещения с помощью библиотеки DOM. Если вы делаете это часто, завиток - гораздо лучший вариант. - person Ben Shelock; 18.03.2010
comment
Я получаю сообщение об ошибке SSL: DOMDocument :: loadHTMLFile (): операция SSL завершилась неудачно с кодом 1. DOMDocument :: loadHTMLFile (): не удалось включить шифрование в /var/www/7Cups.com/parser.php в строке 10. не удалось открытый поток: операция не удалась. DOMDocument :: loadHTMLFile (): предупреждение ввода-вывода: не удалось загрузить внешний объект - person Zoka; 25.05.2018

Вот моя реализация на основе приведенного выше примера / ответа.

  1. Это класс на основе
  2. использует Curl
  3. поддержка HTTP Auth
  4. Пропустить URL, не принадлежащий базовому домену
  5. Вернуть код ответа заголовка Http для каждой страницы
  6. Время возврата для каждой страницы

КЛАСС CRAWL:

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();
person WonderLand    schedule 04.06.2014
comment
это только у меня или он неправильно считает глубину? - person TheCrazyProfessor; 21.04.2019

Ознакомьтесь с PHP Crawler

http://sourceforge.net/projects/php-crawler/

Посмотрим, поможет ли это.

person GeekTantra    schedule 22.02.2010
comment
Ссылки на ссылки лучше делать в виде комментариев. - person mickmackusa; 18.02.2018
comment
Похоже, это больше не поддерживается. Последнее обновление: 2013-04-15 - person PiTheNumber; 26.10.2018

В простейшей форме:

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». Функции принимают второй параметр, глубину, который определяет, как долго следует переходить по ссылкам. Передайте 1, если хотите анализировать только ссылки с данной страницы.

person Tatu Ulmanen    schedule 22.02.2010
comment
-1: Я использую регулярные выражения. Не работает с относительными URL-адресами. Также использует неправильный URL-адрес в file_put_contents (). - person hobodave; 22.02.2010
comment
Что это должно делать? Я просмотрел веб-сайт, и он дал мне кучу дерьма. Похоже, он получает контент откуда-то еще, но теперь с моего сайта. - person erdomester; 09.05.2015

Зачем использовать PHP для этого, если вы можете использовать wget, например

wget -r -l 1 http://www.example.com

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

person Gordon    schedule 22.02.2010
comment
Некоторые конкретные поля необходимо проанализировать и удалить. Мне нужно будет написать код. - person KJ Saxena; 22.02.2010
comment
@Crimson, это требование, которое вы должны указать в вопросе;) - person Gordon; 22.02.2010
comment
@Gordon: Как мне сделать простой краулер на PHP? :-П - person hobodave; 22.02.2010
comment
@hobodave Я имел в виду часть о необходимости разбирать и извлекать определенные поля: P Если бы не это, использование wget было бы самой простой вещью, которую я мог представить для этой цели. - person Gordon; 22.02.2010
comment
Это может быть лучший способ достичь сканирования [курсив май], но он никоим образом не дает ответа на поставленный вопрос. - person Lightness Races in Orbit; 04.05.2011
comment
@Tomalak см. Мой комментарий выше. Вопрос в лучшем случае расплывчатый. Кроме того, вопрос о том, как разбирать HTML, уже неоднократно задавался, и нет смысла повторять очевидное. Я обновил свой ответ ссылкой на дополнительную информацию. Этого должно хватить. - person Gordon; 04.05.2011
comment
На это уже был дан ответ - тоже не ответ. - person Lightness Races in Orbit; 04.05.2011
comment
@Tomalak, если вы считаете, что мой ответ не является ответом, отметьте его как таковой. Если OP спрашивает, как разобрать HTML, то это дубликат, и на него не следует отвечать вообще, а нужно закрывать голосование. Я ответил, как сканировать страницу, о чем спрашивается в заголовке. Не буду снова отвечать, как разбирать. - person Gordon; 04.05.2011
comment
@Gordon: Вы не ответили, как сканировать страницу в PHP. - person Lightness Races in Orbit; 04.05.2011
comment
@Tomalak серьезно, сначала вы комментируете (цитируете). Это может быть лучшим способом достижения сканирования, тогда вы говорите мне, что не ответили, как сканировать страницу в php. Буду признателен, если бы вы приняли решение , прежде чем действовать другим людям на нервы. Это возможно? kthxbai. - person Gordon; 04.05.2011
comment
@ Гордон: Ты не умеешь читать? Это действительно может быть лучший способ добиться ползания. Это не способ обеспечить сканирование в PHP. Извините за грубость, но мне кажется, что я упускаю что-то очевидное ... ?? Прочтите: это может быть лучший способ достичь сканирования в более широком, общем случае, но он никоим образом не отвечает на поставленный вопрос о PHP. - person Lightness Races in Orbit; 04.05.2011
comment
@Tomalak Возможно, вы действительно упускаете что-то очевидное. Да, я не ответил, как сканировать страницу с помощью PHP. Если вы посмотрите на мой ответ, вы увидите, что я на самом деле заявляю об этом в первую очередь. Я дал альтернативу, которую считаю более практичной, и я ожидаю, что кто-то, кто утверждает, что соблюдает баланс между ответом на фактический вопрос и предоставлением OP решение, которое ему действительно нужно для понимания. Я также дал две ссылки на информацию о том, как разбирать HTML на предмет данных. Если этого недостаточно, оставьте свой dv и / или отметьте его. Мне все равно. - person Gordon; 04.05.2011

С некоторыми небольшими изменениями в коде 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);
?>

Я объяснил это руководство в этом руководстве по скрипту сканера

person Team Webgalli    schedule 30.08.2012

Хободаве, ты был очень близок. Единственное, что я изменил, - это оператор if, который проверяет, начинается ли атрибут href найденного тега привязки с http. Вместо того, чтобы просто добавлять переменную $ url, которая будет содержать переданную страницу, вы должны сначала разделить ее до хоста, что можно сделать с помощью функции php parse_url.

<?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);
?>
person George    schedule 03.05.2011
comment
Спасибо, что указал на мою ошибку, Джордж! В вашем решении не учитываются https, user, pass и port. Я обновил свой ответ, чтобы исправить обнаруженную вами ошибку, а также ошибки, введенные вами. Еще раз спасибо! - person hobodave; 04.05.2011

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

Очистка ссылок: http://www.phpro.org/examples/Get-Links-With-DOM.html

Сохранение результатов в файл: http://www.tizag.com/phpT/filewrite.php < / а>

person Jens Roland    schedule 22.02.2010

Я использовал код @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();
  ...

Затем вы также можете опустить строку $parts = parse_url($url); в цикле for.

person pasqal    schedule 16.09.2015

Вы можете попробовать это, это может вам помочь

$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();
person Niraj patel    schedule 02.02.2016

Спасибо @hobodave.

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

original url   :  http://example.com/game/index.html
href in <a> tag:  highscore.html
author's intent:  http://example.com/game/highscore.html  <-200->
crawler result :  http://example.com/highscore.html       <-404->

исправьте это, сломав последнюю косую черту, а не первую

вторая не связанная с этим ошибка заключается в том, что $depth на самом деле не отслеживает глубину рекурсии, а отслеживает ширину первого уровня рекурсии.

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

person Dov Jacobson    schedule 23.02.2018

Я придумал следующий код паука. Я немного адаптировал его из следующего: PHP - Есть ли безопасный способ выполнить глубокую рекурсию? это кажется довольно быстрым ....

    <?php
function  spider( $base_url , $search_urls=array() ) {
    $queue[] = $base_url;
    $done           =   array();
    $found_urls     =   array();
    while($queue) {
            $link = array_shift($queue);
            if(!is_array($link)) {
                $done[] = $link;
                foreach( $search_urls as $s) { if (strstr( $link , $s )) { $found_urls[] = $link; } }
                if( empty($search_urls)) { $found_urls[] = $link; }
                if(!empty($link )) {
echo 'LINK:::'.$link;
                      $content =    file_get_contents( $link );
//echo 'P:::'.$content;
                    preg_match_all('~<a.*?href="(.*?)".*?>~', $content, $sublink);
                    if (!in_array($sublink , $done) && !in_array($sublink , $queue)  ) {
                           $queue[] = $sublink;
                    }
                }
            } else {
                    $result=array();
                    $return = array();
                    // flatten multi dimensional array of URLs to one dimensional.
                    while(count($link)) {
                         $value = array_shift($link);
                         if(is_array($value))
                             foreach($value as $sub)
                                $link[] = $sub;
                         else
                               $return[] = $value;
                     }
                     // now loop over one dimensional array.
                     foreach($return as $link) {
                                // echo 'L::'.$link;
                                // url may be in form <a href.. so extract what's in the href bit.
                                preg_match_all('/<a[^>]+href=([\'"])(?<href>.+?)\1[^>]*>/i', $link, $result);
                                if ( isset( $result['href'][0] )) { $link = $result['href'][0]; }
                                // add the new URL to the queue.
                                if( (!strstr( $link , "http")) && (!in_array($base_url.$link , $done)) && (!in_array($base_url.$link , $queue)) ) {
                                     $queue[]=$base_url.$link;
                                } else {
                                    if ( (strstr( $link , $base_url  ))  && (!in_array($base_url.$link , $done)) && (!in_array($base_url.$link , $queue)) ) {
                                         $queue[] = $link;
                                    }
                                }
                      }
            }
    }


    return $found_urls;
}    


    $base_url       =   'https://www.houseofcheese.co.uk/';
    $search_urls    =   array(  $base_url.'acatalog/' );
    $done = spider( $base_url  , $search_urls  );

    //
    // RESULT
    //
    //
    echo '<br /><br />';
    echo 'RESULT:::';
    foreach(  $done as $r )  {
        echo 'URL:::'.$r.'<br />';
    }
person Ian    schedule 12.01.2018

Стоит помнить, что при сканировании внешних ссылок (я ценю, что OP относится к собственной странице пользователя), вы должны знать о файле robots.txt. Я нашел следующее, которое, надеюсь, поможет http://www.the-art-of-web.com/php/parse-robots/.

person Antony    schedule 05.02.2018

Я создал небольшой класс для получения данных с предоставленного URL-адреса, а затем извлечения элементов HTML по вашему выбору. Класс использует CURL и DOMDocument.

класс php:

class crawler {


   public static $timeout = 2;
   public static $agent   = 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)';


   public static function http_request($url) {
      $ch = curl_init();
      curl_setopt($ch, CURLOPT_URL,            $url);
      curl_setopt($ch, CURLOPT_USERAGENT,      self::$agent);
      curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, self::$timeout);
      curl_setopt($ch, CURLOPT_TIMEOUT,        self::$timeout);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
      $response = curl_exec($ch);
      curl_close($ch);
      return $response;
   }


   public static function strip_whitespace($data) {
      $data = preg_replace('/\s+/', ' ', $data);
      return trim($data);
   }


   public static function extract_elements($tag, $data) {
      $response = array();
      $dom      = new DOMDocument;
      @$dom->loadHTML($data);
      foreach ( $dom->getElementsByTagName($tag) as $index => $element ) {
         $response[$index]['text'] = self::strip_whitespace($element->nodeValue);
         foreach ( $element->attributes as $attribute ) {
            $response[$index]['attributes'][strtolower($attribute->nodeName)] = self::strip_whitespace($attribute->nodeValue);
         }
      }
      return $response;
   }


}

пример использования:

$data  = crawler::http_request('https://stackoverflow.com/questions/2313107/how-do-i-make-a-simple-crawler-in-php');
$links = crawler::extract_elements('a', $data);
if ( count($links) > 0 ) {
   file_put_contents('links.json', json_encode($links, JSON_PRETTY_PRINT));
}

пример ответа:

[
    {
        "text": "Stack Overflow",
        "attributes": {
            "href": "https:\/\/stackoverflow.com",
            "class": "-logo js-gps-track",
            "data-gps-track": "top_nav.click({is_current:false, location:2, destination:8})"
        }
    },
    {
        "text": "Questions",
        "attributes": {
            "id": "nav-questions",
            "href": "\/questions",
            "class": "-link js-gps-track",
            "data-gps-track": "top_nav.click({is_current:true, location:2, destination:1})"
        }
    },
    {
        "text": "Developer Jobs",
        "attributes": {
            "id": "nav-jobs",
            "href": "\/jobs?med=site-ui&ref=jobs-tab",
            "class": "-link js-gps-track",
            "data-gps-track": "top_nav.click({is_current:false, location:2, destination:6})"
        }
    }
]
person TURTLE    schedule 05.02.2018

Это старый вопрос. С тех пор произошло много хорошего. Вот мои два цента по этой теме:

  1. Чтобы точно отслеживать посещаемые страницы, вам необходимо сначала нормализовать URI. Алгоритм нормализации включает несколько шагов:

    • Sort query parameters. For example, the following URIs are equivalent after normalization: GET http://www.example.com/query?id=111&cat=222 GET http://www.example.com/query?cat=222&id=111
    • Преобразуйте пустой путь. Пример: http://example.org → http://example.org/

    • Используйте процентное кодирование с заглавной буквы. Все буквы в тройке процентного кодирования (например, "% 3A") нечувствительны к регистру. Пример: http://example.org/a%c2%B1b → http://example.org/a%C2%B1b

    • Удалите ненужные точечные сегменты. Пример: http://example.org/../a/b/../c/./d.html → http://example.org/a/c/d.html

    • Возможно какие-то другие правила нормализации

  2. Атрибут href имеет не только тег <a>, но и тег <area> https://html.com/tags/area/. Если вы не хотите ничего пропустить, вам также нужно очистить тег <area>.

  3. Отслеживайте прогресс сканирования. Если сайт небольшой, это не проблема. И наоборот, это может быть очень неприятно, если вы просканируете половину сайта, и это не удастся. Рассмотрите возможность использования базы данных или файловой системы для хранения прогресса.

  4. Будьте добры к владельцам сайтов. Если вы когда-нибудь собираетесь использовать свой сканер за пределами своего веб-сайта, вам придется использовать задержки. Без задержек скрипт работает слишком быстро и может значительно замедлить работу некоторых небольших сайтов. С точки зрения системных администраторов это похоже на DoS-атаку. Статическая задержка между запросами поможет.

Если вы не хотите этого делать, попробуйте Crawlzone и поделитесь своим мнением. Также ознакомьтесь со статьей, которую я написал некоторое время назад https://www.codementor.io/zstate/this-is-how-i-crawl-n98s6myxm

person zstate    schedule 03.01.2019