Как сделать бота-паука на PHP?

Опубликовано: 5 Января, 2022

В этой статье мы увидим, как сделать простой и относительно продвинутый веб-сканер (или бот-паук) на PHP. Более простой будет просто выводить все ссылки, которые он находит на веб-странице, в то время как расширенный добавит заголовки, ключевые слова и описания в концептуальную базу данных (концептуальная означает, что в этой статье не используется база данных SQL)! По сравнению с Google даже наш продвинутый веб-сканер на самом деле является простым веб-сканером, так как наш сканер не использует никакого AI-агента! Прежде чем завершить статью, мы пройдем в общей сложности 3 итерации - каждая с пояснением.

Примечание. В этой статье я буду использовать слова «бот-паук» и «веб-сканер» как синонимы. Некоторые люди могут использовать их в другом контексте, но в этой статье оба слова означают, по сути, одно и то же.

Есть много вещей, которые вы можете сделать, чтобы улучшить этих ботов-пауков и сделать их более продвинутыми - добавить функции, такие как поддержание индекса популярности, а также реализовать некоторые функции защиты от спама, такие как наказание веб-сайтов без контента или веб-сайтов с помощью «клик-приманки». такие стратегии, как добавление ключевых слов, не имеющих ничего общего с содержанием страницы! Кроме того, вы можете попытаться сгенерировать ключевые слова и описание со страницы, что GoogleBot делает все время. Ниже приведен список соответствующих статей, которые вы можете найти, если хотите улучшить этого бота-паука.

Простой SpiderBot: простая версия будет нерекурсивной и будет просто печатать все ссылки, которые она находит на веб-странице. Обратите внимание, что вся наша основная логика будет followLinks функции followLinks!

  • Программа:
    <?php function followLink( $url ) {
    // We need this options when creating context
    $options = array (
    'http' => array (
    'method' => "GET" ,
    'user-agent' => "gfgBot/0.1 "
    )
    );
    // Create context for communication
    $context =stream_context_create( $options );
    // Create a new HTML DomDocument for web-scraping
    $doc = new DomDocument();
    @ $doc -> loadHTML( file_get_contents ( $url , false, $context ) );
    // Get all the anchor nodes in the DOM
    $links = $doc -> getElementsByTagName( 'a' );
    // Iterate through all the anchor nodes
    // found in the document
    foreach ( $links as $i )
    echo $i ->getAttribute( 'href' ) . '<br/>' ;
    }
    followLink( " http://example.com " );
    ?>
  • Результат: Это было бесполезно - мы получили только одну ссылку - это потому, что у нас есть только одна ссылка на сайте example.com и, поскольку мы не повторяемся, мы не переходим по полученной ссылке. Вы можете запустить followLink("http://apple.com") если хотите увидеть его в действии. Однако, если вы используете geeksforgeeks.com, вы можете получить некоторую ошибку, поскольку GeeksforGeeks заблокирует наш запрос (конечно, из соображений безопасности).
     https://www.iana.org/domains/example

Объяснение:

  • Строка 3: Мы создаем массив $options Вам не нужно много разбираться в этом, кроме того, что это потребуется при создании контекста. Обратите внимание, что имя user-agent - gfgBot - вы можете изменить его на то, что вам нравится. Вы даже можете использовать GoogleBot, чтобы заставить веб-сайт думать, что ваш сканер является роботом-пауком Google, если он использует этот метод для обнаружения бота.
  • Строка 10: Мы создаем контекст для общения. Для всего, что вам нужно, контекст - чтобы рассказать историю, вам нужен контекст. Чтобы создать окно в OpenGL, вам нужен контекст - такой же для HTML5 Canvas и такой же для сетевого взаимодействия PHP! Извините, если я вышел из «контекста», но мне пришлось это сделать.
  • Строка 13: Создайте DomDocument, который в основном представляет собой структуру данных для обработки DOM, обычно используемую для файлов HTML и XML.
  • Строка 14: Мы загружаем HTML, предоставляя содержимое документа! Этот процесс может создавать некоторые предупреждения (поскольку он устарел), поэтому мы подавляем все предупреждения.
  • Строка 17: Мы создаем в основном массив всех узлов привязки, которые мы находим в DOM.
  • Строка 21: мы печатаем все ссылки, на которые ссылаются эти узлы привязки.
  • Строка 24: Мы получаем все ссылки на сайте example.com ! Выводится только одна ссылка.

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

  • Программа:
    <?php
    // List of all the links we have crawled!
    $crawledLinks = array ();
    function followLink( $url , $depth = 0){
    global $crawledLinks ;
    $crawling = array ();
    // Give up to prevent any seemingly infinite loop
    if ( $depth >5){
    echo "<div style='color:red;'>The Crawler is giving up!</div>" ;
    return ;
    }
    $options = array (
    'http' => array (
    'method' => "GET" ,
    'user-agent' => "gfgBot/0.1 "
    )
    );
    $context = stream_context_create( $options );
    $doc = new DomDocument();
    @ $doc -> loadHTML( file_get_contents ( $url , false, $context ));
    $links = $doc ->getElementsByTagName( 'a' );
    foreach ( $links as $i ){
    $link = $i ->getAttribute( 'href' );
    if (ignoreLink( $link )) continue ;
    $link = convertLink( $url , $link );
    if (!in_array( $link , $crawledLinks )){
    $crawledLinks [] = $link ;
    $crawling [] = $link ;
    insertIntoDatabase( $link , $depth );
    }
    }
    foreach ( $crawling , $crawlURL ){ as $crawlURL ){
    echo ( "<span style='color:grey;margin-left:" .(10* $depth ). ";'>" .
    "[+] Crawling <u>$crawlURL</u></span><br/>" );
    followLink( $crawlURL , $depth +1);
    }
    if ( count ( $crawling )==0)
    echo ( "<span style='color:red;margin-left:" .(10* $depth ). ";'>" .
    "[!] Didn't Find any Links in <u>$url!</u></span><br/>" );
    }
    // Converts Relative URL to Absolute URL
    // No conversion is done if it is already in Absolute URL
    function convertLink( $site , $path ){
    if ( substr_compare ( $path , "//" , 0, 2) == 0)
    return parse_url ( $site )[ 'scheme' ]. $path ;
    elseif ( substr_compare ( $path , " http:// " , 0, 7) == 0 or
    substr_compare ( $path , " https:// " , 0, 8) == 0 or
    substr_compare ( $path , "www." , 0, 4) == 0)
    return $path ; // Absolutely an Absolute URL!!
    else
    return $site . '/' . $path ;
    }
    // Whether or not we want to ignore the link
    function ignoreLink( $url ){
    return $url [0]== "#" or substr ( $url , 0, 11) == "javascript:" ;
    }
    // Print a message and insert into the array/database!
    function insertIntoDatabase( $link , $depth ){
    echo (
    "<span style='margin-left:" .(10* $depth ). "'>" .
    "Inserting new Link:- <span style='color:green'>$link" .
    "</span></span><br/>"
    );
    $crawledLinks []= $link ;
    }
    followLink( " http://guimp.com/ " )
    ?>
  • Выход:
     Вставка новой ссылки: - http://guimp.com//home.html
    [+] Сканирование http://guimp.com//home.html
      Вставка новой ссылки: - http://www.guimp.com
        Вставка новой ссылки: - http://guimp.com//home.html/pong.html
        Вставка новой ссылки: - http://guimp.com//home.html/blog.html
          [+] Сканирование http://www.guimp.com
            Вставка новой ссылки: - http://www.guimp.com/home.html
            [+] Сканирование http://www.guimp.com/home.html
              Вставка новой ссылки: - http://www.guimp.com/home.html/pong.html
              Вставка новой ссылки: - http://www.guimp.com/home.html/blog.html
              [+] Сканирование http://www.guimp.com/home.html/pong.html
                [!] Не нашел ссылок в http://www.guimp.com/home.html/pong.html!
              [+] Сканирование http://www.guimp.com/home.html/blog.html
                [!] Не нашел ссылок в http://www.guimp.com/home.html/blog.html!
          [+] Сканирование http://guimp.com//home.html/pong.html
            [!] Не нашел ссылок в http://guimp.com//home.html/pong.html!
          [+] Сканирование http://guimp.com//home.html/blog.html
            [!] Не нашел ссылок в http://guimp.com//home.html/blog.html!

Объяснение:

  • Строка 3: Создайте глобальный массив - $crawledLinks который содержит все ссылки, которые мы захватили в сеансе. Мы будем использовать его для поиска, чтобы узнать, есть ли ссылка в базе данных! Поиск в массиве менее эффективен, чем поиск в хеш-таблице. Мы могли бы использовать хеш-таблицу, но она не будет более эффективной, чем массив, поскольку ключи представляют собой очень длинные строки (URL-адрес). Поэтому я считаю, что использование массива будет быстрее.
  • Строка 8: мы сообщаем интерпретатору, что используем только что созданный глобальный массив $crawledLinks А в следующей строке мы создаем новый массив $crawling который просто будет содержать все ссылки, по которым мы в данный момент просматриваем.
  • Строка 31: Мы игнорируем все ссылки, которые не ведут на внешнюю страницу! Ссылка может быть внутренней ссылкой, глубокой ссылкой или системной ссылкой! Эта функция не проверяет каждый случай (это сделало бы его очень длинным), но два наиболее распространенных случая - когда ссылка является внутренней ссылкой и когда ссылка ссылается на код javascript.
  • Строка 33: мы преобразуем ссылку из относительной ссылки в абсолютную ссылку, а также делаем некоторые другие преобразования (например, //wikipedia.org в http://wikipedia.org или https://wikipedia.org в зависимости от схемы исходного URL-адреса. ).
  • Строка 35: Мы просто проверяем, нет ли $link которую мы повторяем, уже в базе данных. Если это так, мы игнорируем его. Если нет, мы добавляем его в базу данных, а также в $crawling чтобы мы могли также переходить по ссылкам в этом URL-адресе.
  • Строка 43: здесь рекурсивный поисковый робот. Он следует по всем ссылкам, по которым он должен перейти (ссылки, которые были добавлены в $crawling ).
  • Строка 83: мы вызываем followLink("http://guimp.com/"), мы используем URL-адрес http://guimp.com/ в качестве отправной точки только потому, что он оказывается (или утверждает, что он) наименьшим сайт в мире.

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

Примечание. Вам решать, какую базу данных вы используете - PostgreSQL, MariaDB и т. Д., Мы будем выводить только вставку URL / текста и т. Д., Поскольку обработка с внешними базами данных выходит за рамки этой статьи!

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

  • Программа:
    <?php
    $crawledLinks = array ();
    const MAX_DEPTH=5;
    function followLink( $url , $depth =0){
    global $crawledLinks ;
    $crawling = array ();
    if ( $depth >MAX_DEPTH){
    echo "<div style='color:red;'>The Crawler is giving up!</div>" ;
    return ;
    }
    $options = array (
    'http' => array (
    'method' => "GET" ,
    'user-agent' => "gfgBot/0.1 "
    )
    );
    $context =stream_context_create( $options );
    $doc = new DomDocument();
    @ $doc ->loadHTML( file_get_contents ( $url , false, $context ));
    $links = $doc ->getElementsByTagName( 'a' );
    $pageTitle =getDocTitle( $doc , $url );
    $metaData =getDocMetaData( $doc );
    foreach ( $links as $i ){
    $link = $i ->getAttribute( 'href' );
    if (ignoreLink( $link )) continue ;
    $link =convertLink( $url , $link );
    if (!in_array( $link , $crawledLinks )){
    $crawledLinks []= $link ;
    $crawling []= $link ;
    insertIntoDatabase( $link , $pageTitle , $metaData , $depth );
    }
    }
    foreach ( $crawling , $crawlURL ) as $crawlURL )
    followLink( $crawlURL , $depth +1);
    }
    function convertLink( $site , $path ){
    if ( substr_compare ( $path , "//" , 0, 2)==0)
    return parse_url ( $site )[ 'scheme' ]. $path ;
    elseif ( substr_compare ( $path , " http:// " , 0, 7)==0 or
    substr_compare ( $path , " https:// " , 0, 8)==0 or
    substr_compare ( $path , "www." , 0, 4)==0)
    return $path ;
    else
    return $site . '/' . $path ;
    }
    function ignoreLink( $url ){
    return $url [0]== "#" or substr ( $url , 0, 11) == "javascript:" ;
    }
    function insertIntoDatabase( $link , $title , & $metaData , $depth ){
    echo (
    "Inserting new record {URL= $link" .
    ", Title = '$title'" .
    ", Description = '" . $metaData ['description'].
    "', Keywords = ' " . $metaData [ 'keywords' ].
    "'}<br/><br/><br/>"
    );
    $crawledLinks []= $link ;
    }
    function getDocTitle(& $doc , $url ){
    $titleNodes = $doc ->getElementsByTagName( 'title' );
    if ( count ( $titleNodes )==0 or !isset( $titleNodes [0]->nodeValue))
    return $url ;
    $title = str_replace ( '' , ' ' , $titleNodes [0]->nodeValue);
    return ( strlen ( $title )<1)? $url : $title ;
    }
    function getDocMetaData(& $doc ){
    $metaData = array ();
    $metaNodes = $doc ->getElementsByTagName( 'meta' );
    foreach ( $metaNodes as $node )
    $metaData [ $node ->getAttribute( "name" )]
    = $node ->getAttribute( "content" );
    if (!isset( $metaData [ 'description' ]))
    $metaData [ 'description' ]= 'No Description Available' ;
    if (!isset( $metaData [ 'keywords' ])) $metaData [ 'keywords' ]= '' ;
    return array (
    'keywords' => str_replace ( '' , ' ' , $metaData [ 'keywords' ]),
    'description' => str_replace ( '' , ' ' , $metaData [ 'description' ])
    );
    }
    followLink( " http://example.com/ " )
    ?>
  • Выход:
     Вставка новой записи {URL = https://www.iana.org/domains/example, 
    Title = 'Пример домена', Description = 'Описание отсутствует',
     Ключевые слова = ''}

Объяснение: Ничего революционного изменения нет, но я хотел бы объяснить несколько вещей:

  • Строка 3: мы создаем новую глобальную константу MAX_DEPTH . Раньше мы просто использовали 5 как максимальную глубину, но на этот раз мы используем константу MAX_DEPTH
  • Строка 22 и строка 23: мы в основном получаем заголовок страницы в $pageTitle а также описание и ключевые слова, которые будут храниться в $metaData (ассоциативный массив). Вы можете обратиться к строке 64 и строке 72, чтобы узнать об абстрагированной информации.
  • Строка 31: мы передаем некоторые дополнительные параметры функции insertIntoDatabase

Проблемы с нашим веб-сканером: мы создали этот веб-сканер только для обучения. Внедрение его в производственный код (например, создание из него поисковой системы) может создать некоторые серьезные проблемы. Ниже перечислены некоторые проблемы с нашим веб-сканером:

  1. Он не масштабируется. Наш веб-сканер не может сканировать миллиарды веб-страниц, как GoogleBot.
  2. Это не совсем соответствует стандарту взаимодействия поисковых роботов с веб-сайтами. Он не следует за robots.txt для сайта и будет сканировать сайт, даже если администратор сайта попросит не делать этого.
  3. Это не происходит автоматически . Конечно, он «автоматически» получит все URL-адреса текущей страницы и просканирует каждый из них, но это не совсем автоматический процесс. Он не имеет понятия о частоте сканирования.
  4. Не распространяется . Если работают два робота-паука, то в настоящее время они не могут общаться друг с другом (чтобы узнать, не просматривает ли другой бот-паук ту же страницу).
  5. Разбор слишком прост . Наш бот-паук не будет обрабатывать закодированную разметку (или даже закодированные URL-адреса).
Следующий
Как обнаружить ботов поисковых систем с помощью PHP?
Рекомендуемые статьи
Страница :