Не так давно я рассказывал о том, что в 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 отличий». Общее в строках это:
- подстроки option value=
- все элементы с названиями городов имеют подстроки вида (МСК d:dd)
Это, в принципе и отличает запись города от других записей на всей странице. Осталось определить, что для нас будет важно сохранить в программе. Важными будут:
- Значение атрибута value, т.к. по нему подбирается список каналов.
- Название города.
Приступим к программированию. Открываем Delphi XE. создаем новое приложение и подключаем в uses модули:
- HTTPSend.pas – для отпрвки запросов (библиотека Synapse)
- RegularExpressions.pas – для работы с регулярными выражениями в Delphi XE.
Так как сегодня мы только “готовим почву” для будущего нашего приложения, то внешний вид приложения будет несколько аскетичен:
Клик по кнопке “Скачать” будет использоваться для загрузки страницы настроек города и каналов, а на кнопку “Проверить” будем проверять регулярное выражение из 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).
Таким образом в нашем регулярном выражении встречаются две группы:
- (\d{1,3}) – сохраняет идентификатор города
- (.*) – сохраняет название города.
Проверим наше регулярное выражение. Пишем обработчик кнопки “Проверить”:
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 необходимые нам для дальнейшей работы группы, т.е. идентификаторы и названия городов.
Дело в то, что если Вы проверите, сколько групп содержит один объект TMatch из полученной коллекции TMatchCollection, то увидите, что их три, а не две как задано в регулярном выражении. Группа с индексом 0 всегда содержит найденное совпадение, а не текст сохраненной группы, т.е. по сути дублирует значение:
M[i].Value;
Теперь запускаем приложение, записываем в Edit регулярное выражение и последовательно жмем кнопки «Скачать» и «Проверить». Результат должен быть как показано на рисунке: Можете убедиться, что в результате мы получили только ту информацию, которая нам была необходима и ничего лишнего, следовательно регулярное выражение было составлено верно. Если Вам интересна тема работы с регулярными выражениями, то теперь можете немного потренироваться и изменить составленную мной регулярку так, чтобы результат остался прежним. Вариантов изменения несколько так что есть где развернуться :). Ну, а мы в следующий раз приступим к формированию списка каналов для выбранного города и научимся правильно выбирать регулярные выражения так, чтобы на первый взгляд похожий текст разбивался на правильные группы.
Книжная полка
Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
|
||
Название: О чем не пишут в книгах по Delphi
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
|
Уважаемый Vlad, а как распарсить блок строк? т.е. имею:
[code]
Cost:
1WW
[/code]
Разброс тегов, уйма пробелов, табуляций, и \r. Составляю регулярку:
[code]Cost:.*?(.*?)[/code] с roMultiLine, а в ответ получаю «null»
где то между всем этим ещё встречается тег или
Движок блога обрабатывает html теги в сообщениях. забавно. Вобщем в тексте для парсинга ещё встречаются теги. Прошу прощения за флуд.
Всё это очень интересно. Но еще интересней то, что в дельфийской регулярке метасимволы \w \W и, что самое главное: \b \B — НЕ РАБОТАЮТ С КИРИЛЛИЦЕЙ! А это просто ж… когда надо парсить текст по границам слов