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

Не так давно я рассказывал о том, что в Delphi XE появилась новая возможность — использование Perl-подобных регулярных выражения в своих программах без использования каких-либо сторонних компонентов. Для этих целей используется новый тип данных — TRegEx из модуля RegularExpressions.pas. Рассмотрим пример работы с регулярными выражениями в Delphi XE и попробуем создать вместе небольшую программу, которая будет предоставлять нам какую-нибудь полезную информацию из Сети. Скажем, пусть наша программа будет получать программу телепередач с какого-нибудь сервиса. Почему именно программу TV, а не статистику запросов в Директ или позицию сайта в Яндексе? Дело в том, что в нашем случае, нам придётся выполнить не одну (как обычно), а сразу несколько задач: 1. Сделать запрос именно той программы, которая нам нужна, а именно — настроить город и список каналов. Для этого потребуется, как минимум, с помощью TRegExp вытащить с сервиса списки городов и каналов. 2. Сохранить настройки и перейти на страницу с программой ТВ. 3. Выбрать то время, за которое нам нужна программа (час, день, только вечером и т.д.) 4. Используя TRegExp пропарсить программу тв и показать пользователю, т.е. нам. Пока будем рассматривать все шаги работы заодно и научимся работе с TRegExp в Delphi XE и посмотрим как можно «вслепую» работать с несколькими разделами сайта. Для новичков, полагаю, пример в самый раз.

Погуглив на тему программ телепередач можно найти массу всевозможных сайтов, сервисов и т.д., обычно, небольшие сайты предоставляют программы по одному-двум города. Мы же будем мыслить более масштабно — воспользуемся тем, что нам предоставляют крупные сервисы, например, сервис на mail.ru. Здесь есть всё, что нам необходимо для тренировки навыков — и настройки, и перенаправления с кодом 3хх и «мешающие» элементы кода типа картинок, которые надо будет отсеивать при парсинге названий телепередач. Начнем с простой задачи: получить список городов и список каналов. При парсинге каналов пока будем учитывать те, которые есть практически во всех городах, т.е. метровые и спутниковые, а региональными займемся позже, т.к. для них, нам потребуется дополнительно программно выполнять JavaScript на страничке.

Изучение объекта

Как я уже не раз говорил и, наверное ещё не одну сотню раз скажу — всегда следует начинать решение задачи с изучения объекта, а не кидаться слать непонятно куда непонятно какие запросы. Так как с некоторых пор я использую браузер Chrome, то все поясняющие изображения будут касаться работы с Хромом, Вы же можете легко повторить все действия и в FireFox, используя плагин типа Live HTTP Headers или подобный. Открываем tv.mail.ru и переходим в раздел настроек:

настройки

Перед нами откроется страница, содержащая список городов и доступных для них каналов телепередач. Открываем исходный код страницы и переходим на строку 196 — с неё начинается описание списка городов. Смотрим, что там написано:

города

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

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'
}

Пока нам эта информация ни о чем конкретном не говорит, но помнить о том, что она есть — надо. В дальнейшем мы этой функцией воспользуемся. А пока переходим к этапу составления регулярного выражения для парсинга списка городов. Смотрим как выглядят записи городов в списке:

  (МСК 0:00)Москва
  (МСК 0:00)Воронеж
  (МСК 0:00)Смоленск

Первое, что следует сделать перед написанием регулярного выражения — это задать себе простой вопрос: что общего у этих трех строк? Такая загадка для детей типа «Найди 10 отличий». Общее в строках это:

  1. подстроки option value=
  2. все элементы с названиями городов имеют подстроки вида (МСК d:dd)

Это, в принципе и отличает запись города от других записей на всей странице. Осталось определить, что для нас будет важно сохранить в программе. Важными будут:

  1. Значение атрибута value, т.к. по нему подбирается список каналов.
  2. Название города.

Приступим к программированию. Открываем Delphi XE. создаем новое приложение и подключаем в uses модули:

  1. HTTPSend.pas – для  отпрвки запросов (библиотека Synapse)
  2. RegularExpressions.pas – для работы с регулярными выражениями в Delphi XE.

Так как сегодня мы только “готовим почву” для будущего нашего приложения, то внешний вид приложения будет несколько аскетичен:

main

Клик по кнопке “Скачать” будет использоваться для загрузки страницы настроек города и каналов, а на кнопку “Проверить” будем проверять регулярное выражение из Edit1.

Вначале пишем обработчик onClick у кнопки “Скачать”:

procedure TForm1.Button1Click(Sender: TObject);
begin
  with THTTPSend.Create do
    begin
      if HTTPMethod('GET', 'tv.mail.ru/setup.html') then
        Memo1.Lines.LoadFromStream(Document)
      else
        Memo1.Lines.Add('Загрузка не удалась')
    end;
end;

Теперь приступим к составлению регулярного выражения. Для города у меня получилась следующая регулярное выражение Delphi:

option value=\S(\d{1,3})\S>\S{1}МСК\s{1}\S?\d{1,2}:\d{2}\S{1}\s?(.*)\r

То есть ищем подстроку, которая начинаеся с option value=. Затем в строке встречается 1 непробельный символ (\S{1}), после которого следует от 1 до 3 цифр (\d{1,3}) — идентификатор города. После чего опять 1 непробельный символ и подстрока МСК. Затем скобка (\S{1}) также может (но не обязательно) встретиться непробельный символ (знак минус), затем одна или две цифры, двоеточие, две цифры, скобка, обозначенная в регулярном выражении как непробельный символ (/S{1}), пробел (\s?) и только после этого любое количество любых символов (.*), которое содержит название города и, наконец перевод каретки (\r).

Таким образом в нашем регулярном выражении встречаются две группы:

  1.  (\d{1,3}) – сохраняет идентификатор города
  2. (.*) – сохраняет название города.

Проверим наше регулярное выражение. Пишем обработчик кнопки “Проверить”:

procedure TForm1.Button2Click(Sender: TObject);
var
  Reg: TRegEx;
  i:integer;
  M: TMatchCollection;
begin
  Reg := TRegEx.Create(Edit1.Text,[roIgnoreCase, roMultiLine]);
  if Reg.IsMatch(Memo1.Lines.Text) then
  begin
    M:=Reg.Matches(Memo1.Lines.Text);
    Memo1.Clear;
    for i := 0 to M.Count-1 do
       Memo1.Lines.Add(M[i].Groups.Item[1].Value+' '+M[i].Groups.Item[2].Value)
  end;
end;

Разберемся с тем, что мы здесь написали. Итак, вначале мы создаем экземпляр TRegEx, задав в конструкторе регулярное выражение и установив опции: roIgnoreCase — игнорировать регистр символов roMultiLine — текст является многострочным Затем, используя метод IsMatch делаем проверку того есть ли какие-либо совпадения в принципе в тексте из Memo1. Если совпадение найдено, то получаем всю коллекцию совпадений в переменную M:TMatchCollection и выводим в Memo1 необходимые нам для дальнейшей работы группы, т.е. идентификаторы и названия городов.

Обратите внимание: при выводе значение групп мы используем индексы 1 и 2.

Дело в то, что если Вы проверите, сколько групп содержит один объект TMatch из полученной коллекции TMatchCollection, то увидите, что их три, а не две как задано в регулярном выражении. Группа с индексом 0 всегда содержит найденное совпадение, а не текст сохраненной группы, т.е. по сути дублирует значение:

M[i].Value;

Теперь запускаем приложение, записываем в Edit регулярное выражение и последовательно жмем кнопки «Скачать» и «Проверить». Результат должен быть как показано на рисунке: result Можете убедиться, что в результате мы получили только ту информацию, которая нам была необходима и ничего лишнего, следовательно регулярное выражение было составлено верно. Если Вам интересна тема работы с регулярными выражениями, то теперь можете немного потренироваться и изменить составленную мной регулярку так, чтобы результат остался прежним. Вариантов изменения несколько так что есть где развернуться :). Ну, а мы в следующий раз приступим к формированию списка каналов для выбранного города и научимся правильно выбирать регулярные выражения так, чтобы на первый взгляд похожий текст разбивался на правильные группы.

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

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

 

0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
4 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
Nonstopich
Nonstopich
19/08/2012 03:37

Уважаемый Vlad, а как распарсить блок строк? т.е. имею:

[code]
Cost:

1WW

[/code]
Разброс тегов, уйма пробелов, табуляций, и \r. Составляю регулярку:
[code]Cost:.*?(.*?)[/code] с roMultiLine, а в ответ получаю «null»

Nonstopich
Nonstopich
19/08/2012 03:38

где то между всем этим ещё встречается тег или

Nonstopich
Nonstopich
19/08/2012 03:40

Движок блога обрабатывает html теги в сообщениях. забавно. Вобщем в тексте для парсинга ещё встречаются теги. Прошу прощения за флуд.

flegont
flegont
10/05/2015 03:42

Всё это очень интересно. Но еще интересней то, что в дельфийской регулярке метасимволы \w \W и, что самое главное: \b \B — НЕ РАБОТАЮТ С КИРИЛЛИЦЕЙ! А это просто ж… когда надо парсить текст по границам слов