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

В первой части мы рассмотрели простой пример использование регулярных выражений в Delphi XE и научились получать список городов и их идентификаторов с tv.mail.ru. Теперь рассмотрим следующий шаг работы с сервисом – получение актуального списка каналов для заданного города (региона) и их распределение по категориям: метровые, спутниковые, региональные и т.д.

2. Работа с каналами

Первым делом разберемся с тем как получать список каналов актуальных для заданного региона. Для этого вспомним JavaScript-функцию, на которую я обращал внимание в прошлом посте:

function changezone() {
location.href='/setup.html?zone='+
d.setupzone.zone.options[d.setupzone.zone.selectedIndex].value+
'&channels=5,108,209,235,241,244,252,256,276,290,330,566&types=1,30,30,30,30&tosave=1'
}

Разберемся с тем, что передает эта функция. Если Вы хотя бы немного знакомы с JavaScript, то понять принцип действия функции не составит для вас никакого труда, ну, а для тех кто JavaScript никогда не серьезно не использовал есть вариант провести простой эксперимент.

 

  d.setupzone.zone.options[d.setupzone.zone.selectedIndex].value

 

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

http://tv.mail.ru/setup.html?zone=74

Вставляем его в браузер и смотрим, что получается:

region

Мы оказались на странице настройки каналов для Екатеринбурга. 74 – идентификатор Екатеринбурга. Измените значение параметра zone и получите настройки для другого региона. При этом обратите внимание, что мы не воспользовались другими параметрами URL, в том числе и параметром channels, который содержит идентификаторы каналов, которые при загрузке страницы будут автоматически выбраны.

Теперь мы уже можем, не заходя на страницу сервиса, загрузить страницу, содержащую список каналов телепередач для необходимого города. Для этого допишем наше Delphi-приложение следующим образом:

1. Объявим следующие типы данных:

type
  TRegion = record
    City: string;
    ID  : integer;
end;
 
type
  TRegions = array of TRegion;

 

Смысл понятен — храним информацию по городам в виде динамического массива, каждый элемент которого представляет собой запись record. Если хотите — используйте списки, классы, хоть дженерики — сейчас это не принципиально, главное чтоб Вы смогли организовать доступ к элементам. Теперь изменяем наше приложение следующим образом: добавим на форму TComboBox и при нажатии на кнопку «Загрузить» будем без каких-либо промежуточных действий сразу заполнять список названиями городов. Внешний вид программы:

new_prog

Теперь объявляем глобальную переменную:

Regions: TRegions;

И пишем обработчик кнопки «Загрузить»:

 

procedure TForm1.Button1Click(Sender: TObject);
const
  Pattern = 'value=\S(\d{1,3})\S>\S{1}МСК\s{1}\S?\d{1,2}:\d{2}\S{1}\s?(.*)$';
var
  Input: RawByteString;
  i: integer;
begin
  try
    with THTTPSend.Create do
    begin
      Regions := nil;
      ComboBox1.Items.Clear;
      if HTTPMethod('GET', 'http://tv.mail.ru/setup.html') then
      begin
        Document.Position := 0;
        SetLength(Input, Document.Size);
        Document.Read(PAnsiChar(Input)^, Document.Size);
        if TRegEx.IsMatch(Input, Pattern, [roIgnoreCase, roMultiLine]) then
          with TRegEx.Matches(Input, Pattern, [roIgnoreCase, roMultiLine]) do
          begin
            SetLength(Regions, Count);
            for i := 0 to Count - 1 do
            begin
              Regions[i].City := Item[i].Groups[2].Value;
              Regions[i].ID := StrToInt(Item[i].Groups[1].Value);
              ComboBox1.Items.Add(Regions[i].City);
            end;
          end
        else
          ComboBox1.Text := 'Список городов не найден';
      end
      else
        ComboBox1.Items.Add('Загрузка не удалась')
    end;
  except
    Memo1.Lines.Add('Ошибка загрузки данных с mail.ru')
  end;
end;

 

Приведенный выше код процедуры по большому счёту не отличается от того, который мы использовали в прошлй раз, когда проверяли регулярку (кстати, обратите внимание, что она немного сменилась, но работает так же). Единственное различие заключается в том, что сейчас мы воспользовались исключительно классовыми методами TRegEx. Теперь можете запустить приложение и убедиться, что ComboBox исправно заполняется названиями городов.

Следующий шаг – получение списка каналов. Для этого нам необходимо сделать правильный запрос (см. выше) и составить регулярные выражения отдельно для метровых каналов и спутниковых.

Начнем с составления регулярных выражений.

Находим в исходнике страницы настроек код для checkbox’ов каналов. Для метрового канала он выглядит так:

metr

Для спутникового канала так:

sputnik

Опять первое с чего начинаем – это ищем, чем различаются эти записи. Самое главное отличие заключается в том, что в HTML-коде для спутникового канала не определяется событие onChange, которое выводит на странице предупреждение о том, что настройки следует сохранять. И именно этим различием мы и воспользуемся для того, чтобы различать метровый и спутниковый канал.

Регулярные выражения у меня получились такие. Для метрового канала:

name=\S{1}channelsetup.(\d{1,3})\S{1}.*onClick=.*onchange=\S{1}show_message.{4}(.*)<

Для спутникового:

name=\S{1}channelsetup.(\d{1,3})\S{1}.*onClick=\S{1}show_message.{5}label.*channelsetup.{2,5}>(.*)<

Здесь, как и в случае работы с городами регулярные выражения содержат по две группы: для сохранения идентификатора канала и его названия. Теперь приступим к програмированию. Объявлем следующие типы данных:

 

type
  TChannelType = (ctAll, ctMeter, ctSputnik);
 
type
  TChannel = record
    ChannelType: TChannelType;
    ID  : integer;
    ChannelName: string;
end;
 
type
  TChannels = array of TChannel;

 

Значение ctAll у TChannelType будем использовать при сортировке каналов в списке. Теперь добавим на главную форму ещё два элемента: ComboBox — в котором будем хранить типы каналов для их сортировки и CheckListBox для вывода списка каналов:

new_prog_2

Для парсинга списка каналов напишем следующую процедуру:

procedure TForm1.ParseChannels(Input: string; ChanType: TChannelType);
const
   Pattern_Meter = 'name=\S{1}channelsetup.(\d{1,3})\S{1}.*onClick=.*onchange=\S{1}show_message.{4}(.*)&lt;';
   Pattern_Sputnik = 'name=\S{1}channelsetup.(\d{1,3})\S{1}.*onClick=\S{1}show_message.{5}label.*channelsetup.{2,5}&gt;(.*)&lt;';
var
   i:integer;
   Pattern: string;
begin
  if ChanType=ctAll then Exit;
 
  case ChanType of
    ctMeter: Pattern:=Pattern_Meter;
    ctSputnik: Pattern:=Pattern_Sputnik;
  end;
 
  if TRegEx.IsMatch(Input, Pattern, [roIgnoreCase, roMultiLine]) then
     with TRegEx.Matches(Input, Pattern, [roIgnoreCase, roMultiLine]) do
        begin
          SetLength(Channels, Length(Channels)+Count);
          for i := 0 to Count - 1 do
            begin
              Channels[Length(Channels)-Count+i].ChannelType:=ChanType;
              Channels[Length(Channels)-Count+i].ChannelName:=Item[i].Groups[2].Value;
              Channels[Length(Channels)-Count+i].ID:=StrToInt(Item[i].Groups[1].Value);
            end;
         end
end;

 

Т.е. в зависимости от типа канала выбираем необходимое регулярное выражение, парсим список и заносим данные в массив. Список каналов будем получать сразу после выбора города, т.е. по событию OnChange у ComboBox1:

procedure TForm1.ComboBox1Change(Sender: TObject);
var
  Input: RawByteString;
  i: integer;
begin
 Channels:=nil;
 CheckListBox1.Items.Clear;
 with THTTPSend.Create do
   begin
     if HTTPMethod('GET','http://tv.mail.ru/setup.html?zone='+
                          IntToStr(Regions[ComboBox1.ItemIndex].ID)) then
       begin
         Document.Position := 0;
         SetLength(Input, Document.Size);
         Document.Read(PAnsiChar(Input)^, Document.Size);
         ParseChannels(Input,ctMeter);
         ParseChannels(Input,ctSputnik);
         CheckListBox1.Items.BeginUpdate;
         for i:=0 to Length(Channels)-1 do
            CheckListBox1.Items.Add(Channels[i].ChannelName);
         CheckListBox1.Items.EndUpdate;
       end;
   end;
end;

Ну и, наконец, сортировать каналы по их типу будем при срабатывании OnChange у ComboBox2:

procedure TForm1.ComboBox2Change(Sender: TObject);
var i:integer;
    typ: TChannelType;
begin
  CheckListBox1.Items.Clear;
  typ:=TChannelType(ComboBox2.ItemIndex);
  CheckListBox1.Items.BeginUpdate;
  for i:=0 to Length(Channels)-1 do
    if (Channels[i].ChannelType=typ)OR (typ=ctAll) then
      CheckListBox1.Items.Add(Channels[i].ChannelName);
  CheckListBox1.Items.EndUpdate;
end;

 

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

Work_Prog

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

Так как есть подозрение, что при публикации регулярные выражения даже заключенный в теги pre и code могу с течением времени “покарежится” (хз почему так, но в некоторых постах они реально “сломались”).

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

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

Без строки
[code]
Memo1.Lines.LoadFromStream(Document);
[/code]
после

[code]
Document.Position := 0;
[/code]
Работать не хочет..список городов не парсит