X-Scripts

Power by humanemulator

НАШИ КОНТАКТЫ:
ICQ My ICQ 625657402: 625657402
Наш скайп: igor_sev2
Email : order@x-scripts.com

Сообщество программистов и манимейкеров


Human Emulator Free!
Бесплатная версия программы Human Emulator!
Скачать программу можно тут!

Скрипт Human Emulator парсинга HTML-страницы с использованием XPath

В этой статье мы рассмотрим один из примеров написания скрипта для парсингаHTML-страниц с использованием XPath на примере сайта bing.com.


Сперва определимся с тем, что такое XPath и зачем оно нужно, если есть регулярные выражения?

XPath (XML Path Language) - это язык запросов к элементам XML-подобного документа (далее для краткости просто XML).
XPath призван реализовать навигацию по DOM в XML.

Regexp - формальный язык поиска и осуществления манипуляций с подстроками в тексте, основанный на использовании метасимволов.
По сути это строка-образец (шаблон), состоящая из символов и метасимволов и задающая правило поиска.

Итак, главная разница в том, что XPath специализируется на XML, а Regexp - на любом виде текста.

В: Зачем использовать XPath, если есть regexp, в котором можно сделать тоже самое?
О: Простота поддержки.

Синтаксис у regexp такой, что уже через неделю может быть проще всё переписать, чем вносить изменения,
а с XPath можно спокойно работать. И синтаксис у xpath довольно компактный,xml'ё-фобы могут быть спокойны.

Простой пример для вдохновления - получим значение аттрибута "href" у, например, тега "a".

Yohoho!

Regexp:



XPath:

"string(//a/@href)"

XPath + PHP:

$dom = new DOMDocument;

// "@" Это, конечно, плохо. Но HTML не обязан быть
// валидным, в отличие от XML.
@$dom->loadHTML("Yohoho!");

$xpath = new DOMXpath($dom);
    
$res = $xpath->query("//a");
    
echo $res->item(0)->getAttribute("href") . PHP_EOL;

Быстро (несколько небольших страниц) пробежаться по основам XPath можно в туториале от W3Schools.

Как использовать XPath в PHP можно почитать в документации на php.net.
И в небольшом тутораильчике от IBM DeveloperWorks.

Теперь определимся с необходимым функционалом скрипта:

* Возможность указывать произвольный поисковый запрос

* Парсим только первую страницу поисковой выдачи

* Из поисковой выдачи нам нужно:

* заголовок

* ссылка

* номер в выдаче

Исходя из нашего ТЗ составляем примерный алгоритм работы скрипта:

1) Заходим на bing.com

2) Вводим поисковую фразу

3) Получаем со страницы необходимый результат

Приступим к написанию парсера поисковой выдачи http://bing.com.
Для начала, создадим базовый каркас скрипта.

    // coding: windows-1251
    // Настройка HumanEmulator
    // -----------------------------------------------
    // Где запущен XHE
    $xhe_host = "127.0.0.1:7010";
    // HumanEmulator lib
    require "../../Templates/xweb_human_emulator.php";
    // Our tools
    require "tools/functions.php";
    
    // Настройки скрипта
    // -----------------------------------------------
    
    // Скрипт
    // -----------------------------------------------
    
    // Quit
    $app->quit();

В настройки добавим переменную для хранения поискового запроса.

// Поисковый запрос
$text = "ХуманЭмулятор";

Заходим на сайт.

// Базовый URL
$base_url = "https://www.bing.com/?setlang=en";
$browser->navigate($base_url);

Вводим поисковую фразу.

$input->set_value_by_attribute("name", "q", true, $text);
sleep(1);
$element->click_by_attribute("type", "submit");
sleep(5);

Сохраним в переменную содержимое страницы.

// Получаем содержимое страницы
$content = $webpage->get_body();

Настроим xpath-объект:

$dom = new DOMDocument;
@$dom->loadHTML($content);
$xpath = new DOMXpath($dom);

Теперь у объекта $xpath есть метод "query" в который мы будемпередавать наше xpath-выражение.
Давайте начнём создавать xpath-выражение.
Открыв исходный код страницы с результатами поисковой выдачи увидим, что сами результаты находятся внутри тега "li".

  • Т.о. наше xpath-выражение выберет со страницы все поисковые результаты.

    $results = $xpath->query("//li[@class=\"b_algo\"]");
    

    На одной странице у нас должно быть 1 или больше результатов, проверим себя:

    if($results === false)
        {
            echo "С нашим xpath-выражением что-то не так." . PHP_EOL;
            $app->quit();
        }
        elseif($results->length === 0)
        {
            echo "Поисковый запрос '{$text}' не принёс результатов." . PHP_EOL:
            $app->quit();
        }
        echo "Нашли {$results->length} совпадений." . PHP_EOL;
    

    Здесь стоит обратить внимание на ветку if, где мы сравниваем кол-во результатов xpath-поиска с нулём.
    Если наше xpath-выражение ничего не нашло, то это может означать две вещи:

    * Bing действительно ничего не нашёл.

    * Bing что-то нашёл, но поменял вёрстку на странице, и наше xpath-выражение необходимо исправлять.

    2-й пункт достаточно коварный, в таких случаях, когда xpath-выражение ничего не находит необходимо дополнительно
    сверятся, чтобы удостоверится, что xpath-выражение не устарело (хотя и это не даст 100% гарантий).
    В нашем случае будем сверяться с тем, что Bing пишет кол-во найденных результатов.

    14 results
    

    А если результатов по поисковому запросу нет, то:

  • No results found for ...

  • Т.о. мы получаем такую конструкцию проверки:

    - Если xpath-запрос ничего не нашёл и поисковый запросничего не нашёл, то на странице будет html-код с "No results found".

    - Если xpath-запрос ничего не нашёл, а поисковый запрос что-то нашёл, то на странице будет html-код с "N results".

    Обновим проверку результата xpath-запроса.

    if($results === false)
        {
            echo "С нашим xpath-выражением что-то не так." . PHP_EOL;
            $app->quit();
        }
        elseif($results->length === 0)
        {
            // Если bing ничего не нашёл
            $check_results1 = $xpath->query("//li[@class=\"b_no\"]");
            // Если bing что-то нашёл
            $check_results2 = $xpath->query("//span[@class=\"sb_count\"]");
            
            if($check_results1 === false or $check_results2 === false)
            {
                echo "С нашим xpath-выражением что-то не так." . PHP_EOL;
                $app->quit();
            }
            
            if($check_results1->length > 0 and $check_results2->length === 0)
            {
                echo "Поисковый запрос '{$text}' не принёс результатов." . PHP_EOL:
                $app->quit();
            }
            else
            {
                echo "С нашим xpath-выражением что-то не так." . PHP_EOL;
                $app->quit();
            }
        }
        else
        {
            echo "Нашли {$results->length} совпадений для '{$text}' на 1-й странице поисковой выдачи." . PHP_EOL;
        }
    

    Если всё хорошо и что-то нашлось, то у нас в $results будет N сущностей с результатами отработки xpath-запроса.

    Каждая сущность будет содержать такой HTML-код

      
    

    Теперь можно приступить непосредственно к выборке интересующих нас данных.

    foreach($results as $num => $item)
        {
            // В $item у нас сущность с одним результатом из поисковой выдачи
        }
    

    Получаем номер в поисковой выдаче.

    echo "Номер в поисковой выдаче: " . ($num + 1) . PHP_EOL;
    

    Получаем заголовок.

    $title = $xpath->query("div[contains(@class, \"title\")]/h2/a", $item);
        if($title === false or $title->length !== 1)
        {
            echo "С нашим xpath-выражением что-то не так." . PHP_EOL;
            $app->quit();
        }
        $title = utf8_decode($title->item(0)->textContent);
        echo "Заголовок: '{$title}'" . PHP_EOL;
    

    В данном случае мы в метод "query" передали вторым параметром текущий $item и в xpath-запросе не указывали "//" (т.е. искать сначала страницы).

    2-й парметр означает контекст поиска для xpath-запроса, т.е.искать будем не по всей странице, а только по маленькому html-кусочку из $item.
    И, наконец-то, получаем ссылку.

    $link = $xpath->query("div[contains(@class, \"title\")]/h2/a", $item);
        if($link === false or $link->length !== 1)
        {
            echo "С нашим xpath-выражением что-то не так." . PHP_EOL;
            $app->quit();
        }
        $link = $link->item(0)->getAttribute("href");
        echo "Ссылка: '{$link}'" . PHP_EOL;
    

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

        // Получаем заголовок и ссылку
        //
        $title_link = $xpath->query("div[contains(@class, \"title\")]/h2/a", $item);
        if($title_link === false or $title_link->length !== 1)
        {
            echo "С нашим xpath-выражением что-то не так." . PHP_EOL;
            $app->quit();
        }
        $title = utf8_decode($title_link->item(0)->textContent);
        echo "Заголовок: '{$title}'" . PHP_EOL;
        $link = $title_link->item(0)->getAttribute("href");
        echo "Ссылка: '{$link}'" . PHP_EOL;
    

    Полезные ссылки:

    Wikipedia XPatch
    Wikipedia Rugular expression
    W3.org XPatch
    W3schools XPatch
    Mozilla.org XPatch
    PHP.net
    Ibm.com x-xpatch
    Ibm.com x-xpatchphp

    Скрипт написан 28.04.2015 в Human Emulator 4.9.18 Advanced.

    скачать скрипт

    << Другие скрипты