Pull to refresh

Yandex Search API для .Net

Reading time 10 min
Views 24K
Добрый день!
В процессе работы над одним .Net проектом появилась необходимость получать результаты поиска Яндекса по определённому запросу. В этой статье мне хотелось бы рассказать об опыте работы с API Яндекса из .Net среды.
Конечно, изначально мне не хотелось самому реализовывать обёртку API Яндекса (я надеялся на то, что кто-то уже сделал это до меня), поэтому где-то минут 20 я потратил на то, чтоб выяснить, что готовых решений, которые бы мне подходили, нет. В итоге мне ничего не оставалсоь, как писать её самому, благо документация API Яндекса в открытом доступе и довольно подробная.

Для моих нужд идеально подходил сервис Яндекс.XML, который принимает поисковый запрос и возвращает ответ в формате XML с результатами поиска. Для того, чтобы им пользоваться, необходимо иметь стандартную регистрацию на Яндексе и получить ключ доступа. Необходимо заметить, что ключ строго привязан к IP пользователя, на которого был получен. Ограничение количества запросов в сутки изначально 10, но после подтверждения телефонного номера возрастает до 1000.
После получения ключа, задача разделилась на 2 этапа: отправка запроса и получение ответа от Яндекс.XML, и парсинг этого ответа.

Структура запроса


Чтобы составить запрос к Яндекс.XML необходимо понять из каких элементов этот запрос состоит.

<query> - текст запроса.
<sortby> - тип сортировки результатов: 
      <rlv> - по релевантности,
      <tm>- по времени последнего изменения документа.
<maxpassages> - число пассажей текста с найденными словами (максимум - 5, по умолчанию -2).
<groupings> - в этом элементе с помощью элемента <groupby> задаются необходимые группировки результатов поиска.
<groupby> - способ группировки, описывается следующими аттрибутами:
      <attr=> - имя атрибута, по которыму происходит группировка:
         <d> - группировка по сайтам.
         <> - без группировки.
      <mode=> - режим группировки:
         <flat> - плоский.
         <deep> - глубокий.
      <groups-on-page=> - количество результатов на странице выдачи (не более 100).
      <docs-in-group=> - число документов в группе.

* This source code was highlighted with Source Code Highlighter.


Со структурой запроса разобрались, теперь самое время его отправлять. Делать это можно двумя способами: POST-методом и GET- методом. Расскажу о каждом в отдельности.

POST-метод


Суть пост метода в том, что запрос формируется в XML формате, записывается в поток и отсылается сервису. Чтоб не быть голословным, приведу код, в котором происходит вышеописанное:

      ServicePointManager.Expect100Continue = false;
      
      /* Адрес для совершения запроса, полученный при регистрации IP,
      в него уже забит логин и ключ API.*/
      string url = @"http://xmlsearch.yandex.ru/xmlsearch?
             user=**********&
             key=**********************************"
;
      
      // Текст запроса в формате XML
      string command =
        @"<?xml version=""1.0"" encoding=""UTF-8""?>  
          <request>  
           <query>какой-то запрос</query>
           <groupings>
             <groupby attr="
"d""
                    mode="
"deep""
                    groups-on-page="
"10""
                    docs-in-group="
"1"" />  
           </groupings>  
          </request>"
;

      byte[] bytes = Encoding.UTF8.GetBytes(command);
      // Объект, с помощью которого будем отсылать запрос и получать ответ.
      HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
      request.Method = "POST";
      request.ContentLength = bytes.Length;
      request.ContentType = "text/xml";
      // Пишем наш XML-запрос в поток
      using (Stream requestStream = request.GetRequestStream())
      {
        requestStream.Write(bytes, 0, bytes.Length);
      }
      
      // Получаем ответ
      HttpWebResponse response =(HttpWebResponse)request.GetResponse();

* This source code was highlighted with Source Code Highlighter.


Первая строка необходима, так как объект HttpWebRequest при отправке запроса POST-методом добавляет к нему HTTP заголовок «Expect: 100-Continue», что вводит в заблуждение некоторые сервисы (Яндекс.XML в том числе) и вызывает ошибку “(417) Expectation Failed”.

GET-метод


GET-метод отличается от POST-метода тем, что запрос представляет собой простую строку, а не строку XML-формата. Из текста запроса сервис при его получении сам формирует XML (преобразовывая каждый элемент строкового запроса в XML-аттрибут) и отсылает ответ (в XML формате). Пример кода:

      //Ключ, полученный при регистрации IP.
      string key = "***********************************";
      //Логин на Яндексе.
      string user = "*****************";
      //Шаблон запроса.
      string url = @"http://xmlsearch.yandex.ru/xmlsearch?
              query={0}&
              groupby=attr%3Dd.
              mode%3Ddeep.
              groups-on-page%3D10.
              docs-in-group%3D1&
              user={1}&
              key={2}"
;
      //Готовый текст запроса.
      string completeUrl = String.Format(url, searchQuery, user, key);
      //Объект, отсылающий запрос.
      HttpWebRequest request = (HttpWebRequest)WebRequest.Create(completeUrl);
      //Получение ответа.
      HttpWebResponse response = (HttpWebResponse)request.GetResponse();

* This source code was highlighted with Source Code Highlighter.


Итак, ответ от сервиса получен. Теперь необходимо выудить из него всю необходимую информацию о результатах поиска. В среде .Net существует мощный механизм работы с XML-документами, так что с этим особых проблем не возникло. Для начала создадим объект, через который будем работать с XML-ответом на основе полученного HttpWebResponse ответа:

      XmlReader xmlReader = XmlReader.Create(response.GetResponseStream());
      XDocument xmlResponse = XDocument.Load(xmlReader);

* This source code was highlighted with Source Code Highlighter.


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

•  <url> - URL документа
•  <title> - Заголовок документа
•  <headline> -Краткое описание документа
•  <modtime> - Время последнего обновление документа
•  <saved-copy-url> - Ссылка на сохранённую копию документа

* This source code was highlighted with Source Code Highlighter.


Сначала была заготовлена структура, в которую бы записывались результаты поиска:

  public struct YaSearchResult
  {
    //url
    public string DisplayUrl,
    //saved-copy-url
    CacheUrl,
    //title
    Title,
    //headline
    Description,
    //modtime
    IndexedTime;

    public YaSearchResult(string url,
               string cacheUrl,
               string title,
               string description,
               string indexedTime)
    {
      this.DisplayUrl = url;
      this.CacheUrl = cacheUrl;
      this.Title = title;
      this.Description = description;
      this.IndexedTime = indexedTime;
    }
  }

* This source code was highlighted with Source Code Highlighter.


В самом XML-документе каждый результат поиска называется group и имеет следующую структуру:

<group>
 <categ attr="" name="" />
 <doccount> </doccount>
 <relevance priority="" />
-<doc id="">
   <relevance priority="" />
   <url>  </url>
   <domain> </domain>
   <title> </title>
   <modtime> </modtime>
   <size> </size>
   <charset> </charset>
   +<passages>
   +<properties>
   <mime-type> </mime-type>
   <saved-copy-url> </saved-copy-url>
 </doc>
</group>

* This source code was highlighted with Source Code Highlighter.


И, наконец, сам метод парсинга (+ в начале, вспомогательный метод выдирания значений их XML-я):

//все нужные нам данные о результате поиска лежат в его элементе doc,
//метод GetValue по имени того, что нам нужно, возвращает строчку
public static string GetValue(XElement group, string name)
{
try
{
  return group.Element(«doc»).Element(name).Value;
}
//это если в результате нету элемента с каким то именем,
//то будет вместо значащей строчки возвращаться пустая.
catch
{
  return string.Empty;
}
}


* This source code was highlighted with Source Code Highlighter.


public static List<YaSearchResult> Search(string searchQuery)
{

//Лист структур YaSearchResult, который метод в итоге возвращает.
List<YaSearchResult> ret = new List<YaSearchResult>();

//из полученного XML'я выдираем все элементы с именем "group" - это результаты поиска
 var groupQuery = from gr in response.Elements().
               Elements("response").
               Elements("results").
               Elements("grouping").
               Elements("group")
          select gr;

//каждый элемент group преобразовывается в объект SearchResult
      for (int i = 0; i < groupQuery.Count(); i++)
      {
        string urlQuery = GetValue(groupQuery.ElementAt(i), "url");
        string titleQuery = GetValue(groupQuery.ElementAt(i), "title");
        string descriptionQuery = GetValue(groupQuery.ElementAt(i), "headline");
        string indexedTimeQuery = GetValue(groupQuery.ElementAt(i), "modtime");
        string cacheUrlQuery = GetValue(groupQuery.ElementAt(i),
                        "saved-copy-url");
        ret.Add(new YaSearchResult(urlQuery, cacheUrlQuery, titleQuery, descriptionQuery, indexedTimeQuery));
      }

      return ret;
}

* This source code was highlighted with Source Code Highlighter.


Лист с результатами поиска получен!

Заключение


Если кому-то интересна тема работы с Яндекс АПИ из .Net, то в следующей статье могу написать, как работать с АПИ Яндексовского геокодинга/обратного геокодинга.

Напоследок хотелось бы сказать, что в планах написать красивую полную обёртку Яндекс АПИ для .Net, не только Яндекс.XML но и остальных сервисов
Tags:
Hubs:
+27
Comments 8
Comments Comments 8

Articles