уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Давненько я ничего не писал про Google API в Delphi, всё-таки Google Analytics API довольно громоздкая штука и в пару недель разобраться с таким API мне пока не представляется возможным…но я продолжаю над этим работать, а пока, решил продемонстрировать Вам ещё один небольшой пример из серии «Google API в Delphi«, а именно — быструю загрузку и обработку RSS-каналов.
В принципе тема получения информации из RSS-каналов довольно избитая.

Введение

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

  1. Отправили GET на URL канала
  2. Получили XML, сохранили каким-либо образом
  3. Обработали XML, используя библиотеки, начиная от того, что есть в Delphi и, заканчивая бблиотеками сторонних разработчиков типа NativeXML, SuperObjectXMLParser и т.д.
  4. Вывели результат.

Кто-то при этом может использовать дополнительно регулярные выражения и т.д., но, тем не менее смысл от этого не меняется — «тянем» из сети XML. А как нам быть в том случае, если, например, не требуется всё текущее содержимое канала, а скажем только последняя запись? Получается, что бОльшая часть того XML-документя, который мы качаем — лишняя информация, которая может «весить» достаточно, чтобы заметить процесс закачки. И тут нам на помощь приходит «AJAX API загрузки фидов Google».

Немного информации по API.

Несмотря на то, что API рассчиан на использование JavaScript, он также предоставляет достаточно возможносей и для разработчиков, пишущих на других языках, например, нам — пищущим на Delphi. В принципе, единственная «сложность» при этом заключается в том, что в результате запроса Google будет нам всегда возвращать результат ответа в формате JSON.

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

  • в формате XML — 17,843 kb
  • в формате JSON — 9,579 kb

Разница ощутима даже при том, что в мой канал транслируются только части сообщений, а не весь пост целиком.

Теперь, что касается загрузки канала. Базовый URL для загрузки выглядит следующим образом:

http://ajax.googleapis.com/ajax/services/feed/load

При этом в URL можно включать различные параметры запроса, обязательными параметрами при этом будут:
v — версия протокола (единственное действительное значение — 1.0)
q — параметр поиска. В нашем случае — экранированый URL канала.

Как можно увидеть по приведенному выше линку — дополнительно мы можем при загрузке использовать ещё три параметра: num, output и scoring. Output нам сегодня не понадобится, а вот с помощью num и scoring мы будем регулировать объем принимаемых данных — оба этих параметра регулируют количество загружаемых элементов фида.

Пишем приложение Delphi

Так как данные мы будем принимать в формате JSON, то воспользуемся для обработки таких документов замечательной библиотекой SuperObject, о которой я уже упоминал и скачать которую можно здесь.

Теперь создаем новое приложение Delphi и сразу определим константу:

const
cBaseURL = 'http://ajax.googleapis.com/ajax/services/feed/load?v=1.0&q=%s&num=%d&callback=response';

Для загрузки данных мы можем воспользоваться любой библиотекой (Indy, Synapse, ICS и т.д.) или воспользоваться способом, который я кратко описывал здесь, т.е. с использованием интерфейса IXMLHttpRequest из MSXML. Для сокращения исходного кода я и воспользуюсь MSXML.
Подключаем в uses два модуля:

  uses msxml, superobject;

Главную форму приложения сделаем такой:

Теперь напишем следующий обработчик кнопки «Go»:

procedure TForm8.Button1Click(Sender: TObject);
var
  request: IXMLHttpRequest;
  JSON: ISuperObject;
  iCounterPerSec: TLargeInteger;
  T1, T2: TLargeInteger; //значение счётчика ДО и ПОСЛЕ операции
begin
  QueryPerformanceFrequency(iCounterPerSec);
  QueryPerformanceCounter(T1);
  request := CoXMLHTTP.Create;
  if not CheckBox1.Checked then
    request.open('GET', Format(cBaseURL,[Edit1.Text,SpinEdit1.Value]), false, EmptyParam, EmptyParam)
  else
    request.open('GET', Format(cBaseURL,[Edit1.Text,-1])+'&scoring=h', false, EmptyParam, EmptyParam);
  request.send(EmptyParam);
  JSON := so;
  JSON.M['response'] := response;
  JSON[request.responseText];
  QueryPerformanceCounter(T2);
  label9.Caption:=FormatFloat('0.0000', (T2 - T1) / iCounterPerSec) + ' сек.';
end;

Здесь мы засекаем время выполнения операции, скачиваем данные фида, используя IXMLHttpRequest, передаем данные в ISuperObject как метод, разбираем данные и выводим данные фида вместе с общим временем выполнения операции.
Сейчас, у вас Delphi должна ругаться вот на эту строку:

  JSON.M['response'] := response;

Это потому, что метод response у нас ещё не определен. Выглядит метод следующим образом:

procedure response(const This, Params: ISuperObject; var Result: ISuperObject);
var
  obj: ISuperObject;
  itr:integer;
  Arr:TSuperArray;
  Keywords: TstringList;
begin
    try
      Keywords:=TStringList.Create;
      Keywords.Delimiter:=',';
      case Params.I['responseStatus'] of
        200:
           begin
             Arr:=TSuperArray.Create;
             with Form8 do
               begin
                 with Params['responseData.feed'] do
                   begin
                     Label4.Caption:=S['title'];
                     Label6.Caption:=S['author'];
                     Label8.Caption:=S['type'];
                     ListBox1.Clear;
                     ListBox1.Items.BeginUpdate;
                     for obj in O['entries'] do
                       begin
                         Keywords.Clear;
                         Arr.Clear(true);
                         Arr:=obj['categories'].AsArray;
                         for itr:= 0 to Arr.Length-1 do
                           Keywords.Add(Arr.S[itr]);
                         ListBox1.Items.Add(obj.Format('%title% - %link%'));
                         ListBox1.Items.Add(' Тэги: '+Keywords.DelimitedText);
                       end;
                     ListBox1.Items.EndUpdate;
                   end;
               end;
           end
        else
          Form8.ListBox1.Items.Add(Params.S['responseDetails']);
      end;
    finally
      FreeAndNil(Keywords);
    end;
end;

Разберемся, что здесь происходит. Во-первых, после того как мы делаем запрос, Google возвращает нам документ следующего содержания:

{"responseData" : {
    "feed" : { "feedUrl":"...",
                  "title":"...",
                  "link":"...",
                  "author":"",
                  "description":"...",
                  "type":"...",
                  "entries":[{"title":"...",
                                 "link":"...",
                                 "author":"...",
                                 "publishedDate":"...",
                                 "contentSnippet":"...",
                                 "content":"...",
                                 "categories":["...","...",..]},
                                {....}
                              ]}
                 }
  },
  "responseDetails" : null | string-on-error,
  "responseStatus" : 200 | error-code
}

Чтобы представить этот объект в виде метода мы ввели в запрос необязательный параметр &callback=response и в результате Google выполнил ответный вызов функции JavaScript, название которой соответствует значению параметра callback и в итоге мы получили документ, содержащий:

response(JSON-объект)

Воспользовавшись этим обстоятельством мы в итоге и смогли сделать так:

  JSON.M['response'] := response;

Конечно так делать во всех случаях работы с JSON не обязательно, а иногда и невозможно (не все же сервера(-ы) поддерживают callbak’и), но в качестве примера я воспользовался такой «фишкой» SuperObject. А можно было бы действовать без дополнительного метода response и сразу разобрать объект, что, собственно мы в итоге и сделали.

Разбор документа мы провели следующим образом. Вначале мы проверили, что пришло в параметре responseStatus, если 200, то все ок — приступили к разбору JSON, иначе — вывели причину ошибки из параметра responseDetails.
Разбор JSON-объекта начали по порядку, начиная от данных по RSS-каналу, а именно вывели в label’ы название фида, автора и тип канала. Здесь стоит обратить внимание на то как выглядит чтение данных в SuperObject. Признаться нигде больше пока не встречал, чтобы название свойств объекта состояло из 1 буквы :). Вот так:

 with Params['responseData.feed'] do
    begin
       Label4.Caption:=S['title'];
    ....

Мы прочитали название канала, которое в документе имеет путь responseData.feed.title и вывели его в Caption. А так:

Params.I['responseStatus']

Мы прочитали значение в виде целого числа. Соответственно M — представляет JSON в виде метода и т.д.
Отдельно уделим внимание вот этой части метода respose:

for obj in O['entries'] do
  begin
   ... 
   Arr:=obj['categories'].AsArray;
   for itr:= 0 to Arr.Length-1 do
      Keywords.Add(Arr.S[itr]);
   ListBox1.Items.Add(obj.Format('%title% - %link%'));
   ...
 end;

Здесь мы каждый элемент, находящийся в entries представляем в виде ISuperObject, затем у каждого такого объекта считываем массив categories (тэги поста), и представляем каждый элемент массива в виде строки. Далее, одной строкой:

ListBox1.Items.Add(obj.Format('%title% - %link%'));

Мы выводим в список название поста и ссылку, используя метод ISuperObject.Format.
В результате программа после чтения канала выглядит примерно так:

Пара слов про скорость

В принципе, общее время выполнения всех операций итак будет меньше, чем при использовании данных в формате XML, т.к. см. выше объемы получаемых данных, но для интереса в программе есть небольшой счётчик, засекающий время, которое затрачивается на всё, начиная от загрузки и заканчивая выводом результатов в список. Среднее время, которое показывал этот счётчик при загрузке всех элементов из моего RSS составило порядка 0,8-0,9 сек.

Единственный пока видимый недостаток этого способа работы с RSS,который я вижу — это URL’ы элементов канала, если RSS «завернут» в FeedBurner. Для получения нормального URL’а придётся отправить дополнительно HEAD.

Книжная полка

Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
купить книгу delphi на ЛитРес
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
купить книгу delphi на ЛитРес
0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
4 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
Андрей
Андрей
04/05/2012 22:37

А как делать проверку на существования строки, числа, массива, объекта или ещё чего нибудь в коде? А то бывает, что какой-либо параметр не передается и в результате программа рушится с АВ.

Андрей
Андрей
05/05/2012 00:03

Не совсем, понял, не могли бы ли Вы пояснить?
Я имел ввиду объект, строку, число и так далее в самом JSON при разборе его через SuperObject. Вот например, http://vk.com/developers.php?oid=-1&p=wall.get тут сервер отвечает массивом из записей со стены, причем в каждом элементе массива могут присутствовать дополнительный массив — attachments, а может его и не быть, как может быть или не быть других параметров в любом из ячеек массива с сообщениями. Хотелось бы сделать универсальную процедуру, которая разбирает этот ответ, а красиво не получается, нужно делать кучу проверок в результате код загромождается очень сильно.

Руслан
Руслан
02/06/2013 20:06

Label6.Caption:=S[‘author’];
Label8.Caption:=S[‘type’];
ListBox1.Clear;
ListBox1.Items.BeginUpdate;
for obj in O[‘entries’] do ////////Здесь делфи ругается
begin
Keywords.Clear;

[Error] main.pas(68): Operator not applicable to this operand type
[Error] main.pas(69): Expression expected but ‘BEGIN’ found
[Error] main.pas(102): Undeclared identifier: ‘CoXMLHTTP’
[Error] main.pas(102): Not enough actual parameters
[Fatal Error] demo.dpr(6): Could not compile used unit ‘main.pas’
//////////////////////////////////////////////////////////////////////////////////////////
Извините, но в чём ошибка?