Yandex Search API для .Net

    Добрый день!
    В процессе работы над одним .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 но и остальных сервисов
    • +27
    • 12,7k
    • 8
    Поделиться публикацией
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 8
    • +2
      Обязательно пишите продолжение и либу. Было бы круто если её еще и на CodePlex выложить
      • +2
        Спасибо за статью.
        Про геокодинг было бы тоже очень инетерсно почитать.
        • +1
          Спасибо. В закладки!
          PS. Отправил ошибки в личку.
          • 0
            Благодарю за статью! Интересно реализовано решение.

            Буду ждать обёртку Яндекс АПИ для .Net :)
            • 0
              Спаисибо за статью. Давно хотел озаботиться темой парсинга выдачи Яндекса, да все руки никак не доходили. Сегодня с вашей помощью сделал маленький шаг вперед.
              Ждем продолжения!
              • 0
                Напоследок хотелось бы сказать, что в планах написать красивую полную обёртку Яндекс АПИ для .Net

                сразу скажу, мертвая идея. Дело в том, что Яндекс постоянно обновляет свой API, и то что у вас работает сегодня, может перестать работать через полгодика-год. И второй момент, не все ихние сервисы, без бубна работают в .NET. Скажем тот же самый API Я.Директа. Я не думаю, что вы будете следить за всеми ихними изменениями.
                • 0
                  Спасибо за статью, на прошлой неделе как раз Bing прикрутил к своему сайту, правда там всё было довольно тривиально, достаточно зарегистрироваться на live.com или hotmail.com, после чего скачать мануал «API Basics.pdf» и в течение часа у меня всё работало, т.к. мануал содержит описание работы c API и примеры для C#.
                  • 0
                    А почему не использовали XmlSerializer для разбора результатов?

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.