Поиск по сайту с помощью Яндекс.XML на PHP
Новая задача с которой я недавно столкнулся - это поиск по сайту. Самому писать поиск было лень, поэтому решил воспользоваться каким-нибудь сервисом, стал искать что-нибудь подходящее и нашел Яндекс.XML.
Первое, что пришлось сделать - это зарегистрировать IP адрес, с которого будут делаться поисковые запросы, на странице этого сервиса (http://xml.yandex.ru). Там же я обнаружил несколько примеров на Perl и и руководство по составлению XML запросов - с этим проблем не возникло. Могу только сказать, что при поиске на одном сайте не должно быть группировок, а после строки запроса должен указываться адрес сайта на котором нужно произвести поиск. Вот пример запроса, который я использовал.
<?xml version="1.0" encoding="windows-1251"?>
<request>
<query>строка запроса << host="myhost.ru"</query>
<page>номер страницы</page>
</request>
Еще один момент, в строке запроса рекомендуется использовать спецсимволы, для этого я воспользовался функцией htmlspecialchars(). Сразу возник вопрос, как передать этот запрос, а еще важнее, как получить ответ. Выяснилось, что передавать нужно хтмэльный POST запрос , недолго поковыряясь в google, я нашел, как его можно сделать вручную, вот пример:
POST /xmlsearch/ HTTP/1.1 /* это протокол по которуму мы его посылаем (указать 1.1 было большой ошибкой, почему, я объясню позже) */
Host: xmlsearch.yandex.ru /* это адрес куда мы его посылаем */
Content-Type: text/plain Content-length: /* здесь идет длинна запроса в байтах */
/* а здесь идет наш XML запрос */
Для передачи запроса и получения ответа я использовал сокеты (первое, что пришло в голову), а дальше, если есть под рукой PHP справочник, все дело техники. Полученный XML ответ я парсил спомощью DOM, функцией domxml_open_mem(), методом get_elements_by_tagname() получал нужные тэги (мне пригодились title, url и passage, которые содержат заголовок страницы, ее урл и краткая выдержка с найдеными словами, соответственно) и вытаскивал содержание методом get_content().
Тестируя получившейся скрипт, в функции domxml_open_mem() стала возникать синтаксическая ошибка XML. Изучив поподробнее код XML-ответа я обнаружил там посторонние символы в самых неподходящих местах. Над этой проблемой в силу своего невежества я бился дольше всего, так и ненайдя ответа, я обратился к службе поддержки Яндекса, где меня и просветили, конечно не сразу, похоже я был первый такой пытливый.
Выяснилось, что протокол HTTP 1.1 при передаче данных использует систему Transfer-chunked (за подробностями отправляю вас к RFC 2616), короче, при передаче данных делит содержание ответа на куски и в промежутках между кусками вставляет те самые загадочные символы. Проблема решилась просто, в запросе я заменил строку POST /xmlsearch/ HTTP/1.1 на POST /xmlsearch/ HTTP/1.0
Вот такая веселая история, если вы дочитали эту нуднятину до этого места, то выражаю вам свое почтение и перехожу к коду скрипта.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Поиск по сайту с помощью Яндекс.XML на PHP - pgood.fatal.ru</title>
</head>
<body>
<?
$host = 'myhost.ru'; //адрес сайта на котором производим поиск
$search_string = isset($_POST['search_string']) ? $_POST['search_string'] : 'тест'; //строка запроса
/*
* Вспомогательная функция для получения содержания из XML тэгов
*/
function getFirst($parent,$tag_name){
$e = $parent->get_elements_by_tagname($tag_name);
if(isset($e[0])) return $e[0]->get_content();
else return '';
}
/*
* Эта функция разбирает XML и формирует конечный HTML
*/
function makeSearchList(&$out,&$dd){
$docs = $dd->get_elements_by_tagname('doc');
if(count($docs) < 1){
$out = '<p>По вашему запросу ничего не найдено</p>';
return;
}
foreach($docs as $doc){
$out.= '<p><a href="'.getFirst($doc,'url').'">'.getFirst($doc,'title').'</a><br />'.getFirst($doc,'passage').'</p>';
}
}
// XML запрос
$request = '<?xml version="1.0" encoding="utf-8"?>
<request>
<query>
'.htmlspecialchars($search_string.' << host="'.$host.'"').'
</query>
</request>';
// POST заголовок
$request_header = '
POST /xmlsearch/ HTTP/1.0
Host: xmlsearch.yandex.ru
Content-Type: text/plain
Content-length: '.strlen($request).'
';
$request = $request_header.$request;
//устанавливаем соединение
$fp = fsockopen('xmlsearch.yandex.ru',80,$errno,$errstr,30);
if($fp){
//отправляем запрос
fwrite($fp,$request);
//получаем ответ
$xml_respose = '';
while(!feof($fp)){
$xml_respose.= fgets($fp,128);
}
fclose($fp);
//отрезаем заголовок ответа, чтобы остался только XML
$xml_respose = substr($xml_respose,strpos($xml_respose,'<?xml'));
// Создаем DOM Document
$dd = domxml_open_mem($xml_respose);
}
//парсим запрос и делаем HTML
makeSearchList($out,$dd);
?>
<form action="" method="post">
<img src="http://ya.ru/logo.gif" width="76" height="44" /><br />
<input name="search_string" type="text" value="<?=$search_string?>" />
<input type="submit" value="Искать в Яндекс XML" />
</form>
<?=$out?>
</body>
</html>
Имейте ввиду, ответ приходит в UTF-8.
Вот немного переработанный пример для PHP5 с использованием DOM вместо domxml - пример (ZIP 1,4 Kb).
