Хотел было назвать пост как-то типо “Текучка”, но потом подумал, что сие название не отразит суть написанного, поэтому переименовал в то, что есть, но по большому счёту – это обычная текучка.
Родилась у меня тут одна мысль по поводу повышения удобства работы с “Пинговалкой”, а именно сделать возможность автоопределения параметров нового проекта, таких как название сайта, ключевые слова, адрес RSS-канала и т.д., т.е. того, что сейчас вручную заносится пользователем. Смысл просто – задаем только адрес главной страницы, жмем кнопку, программка скачивает главную страничку и парсит её в поисках необходимых значений, что не смогли найти автоматом – вбиваем ручками. В принципе задача достаточно тривиальная. Например, про то, как вытаскивать значения различных тегов из HTML-документа я писал чуть ли не два года назад, но возникла небольшая проблемка с относительными ссылками в элементах link документа. Поясню в чем суть.
Если Ваш сайт использует нормальную CMS (а сейчас редко когда встретишь, что-либо иное), то в html-коде страницы часто могут прописываться связи с внешними документами, например, связи с css-файлами, скриптами, а также пути к rss-каналу, atom’у, rdf и пр. Такие связи описываются в html-тегах LINK. Отличием элемента LINK от A (ссылки) заключается в том, что тег link размещается всегда внутри контейнера head и не создает ссылку. Как может выглядеть адрес RSS-канала, заключенного в теге link? В моем блоге эта связь описывается как:
link rel="alternate" type="application/rss+xml" title="RSS 2.0" href="feed/"
На blogspot’е Гугла эта же связь описывается полностью, то есть, например:
link rel="alternate" type="application/rss+xml" title="Flash-отдых — RSS" href="http://true-swf.blogspot.com/feeds/posts/default?alt=rss
Соответственно, задача заключается в том, чтобы вне зависимости от того как определена ссылка: полностью или относительно выдать пользователю полный URL.
Вообще вопрос о работе с относительными ссылками в Delphi поднимался давным-давно в блоге “Парсинг от А до Я” и Маша (автор блога) привела довольно простое рабочее решение проблемы. Но это решение касается только тегов A и на LINK никак не влияет. Поэтому пришлось делать что-то свое.
Естественно, самое элементарнейшее из элементарных решений – это проверять есть ли в атрибуте href адрес домена и, если нет, то просто подставлять его в результат. В случае, когда мы ищем всего один URL дело пары секунд.
Второй вариант решения задачки – специально для тех, кто во всю использует WinInet. В этой библиотеке есть полезная функция – InternetCombineUrl, которая умеет “склеивать” относительный путь с базовым URL и возвращать полный путь к ресурсу:
function InternetCombineUrl(lpszBaseUrl, lpszRelativeUrl: PWideChar; lpszBuffer: PWideChar; var lpdwBufferLength: DWORD; dwFlags: DWORD): BOOL; stdcall;
где
lpszBaseUrl — базовый адрес, например, http://webdelphi.ru
lpszRelativeUrl – относительный путь, например feed/
lpszBuffer – буфер, в который будет записан итоговый URL
lpdwBufferLength – размер буфера
dwFlags – флаги, определяющие как будет выглядеть итоговый URL (будут ли маскироваться символы и т.д.)
Используя эту функцию можно “склеивать” все относительные ссылки с базовым адресом и получать то, что нам требуется. Теперь, что касается вытягивания с HTML-страницы адреса RSS-канала. Я решил для этого воспользоваться возможностями MSHTML и поразбирать немного элементы DOM. Можно и теме же регулярными выражениями напарсить всё, что необходимо, но, по-момему в данном случае использование MSHTML более предпочтительно. Приведу ту часть кода (подробно расписанную), которая отвечает за получение адреса RSS-канала:
var Content: string;//содержимое страницы Doc: IHTMLDocument2; V: OLEVariant; Link: IHTMLElement; RelAttr, href, linktype: string; links: IHTMLElementCollection; begin {получаем контент странички, используя Synapse, Indy, WinInet и т.д.} Doc := CoHTMLDocument.Create as IHTMLDocument2;//создаем новый документ V := VarArrayCreate([0, 0], varVariant); V[0] := Content; Doc.write(PSafeArray(TVarData(V).VArray));//записываем полученный контент в документ links := Doc.all.tags('link') as IHTMLElementCollection;//получаем коллекцию элементов link for I := 0 to links.Length - 1 do begin Link := links.item(I, 0) as IHTMLElement;//получаем элемент RelAttr := Link.getAttribute('rel', 0);//читаем значение атрибута rel if Length(Trim(RelAttr)) > 0 then //если атрибут есть, то ссылка может содержать путь к RSS begin if pos('alternate', lowercase(RelAttr)) > 0 then //rel совпадает с тем, что нам нужно begin linktype := Link.getAttribute('type', 0);//читаем MIME-тип содержимого ресурса if pos(cRSSlinkType, lowercase(linktype)) > 0 then //тип совпал cRSSlinkType='application/rss+xml' begin href := Link.getAttribute('href', 0);//получаем значение атрибута href RSS := UrlCombine(BASEURL, href);//проверяем URL на "относительность", склеиваем, если надо break; end; end; end; end; {чистим память, возвращаем результаты и т.д.} end; end;
Здесь в UrlCombine() можно использовать любой из предложенных вариантов работы с относительными ссылками, например, использовать InternetCombineUrl:
var I: Cardinal; begin {...} SetLength(Result, 1024); InternetCombineUrl(PChar(ABase), PChar(ARel), @Result[1], I, 0); SetLength(Result, I); {...} end;
После этого результат стал возвращаться так как надо. Что касается чтения других параметров сайта, то тут все просто и легко. О том как вытаскивать анкоры ссылок типа скайлинк отзывы, значения заголовков, мета-тегов и прочих элементов HTML я тоже довольно давно рассказывал в постах "Что можно «вытащить» из DOM’а?" и "MSHTML в Delphi. Анализ ссылок на странице." так что с этим проблем никаких не возникло. Теперь стараюсь как можно сильнее ускорить работу программы как в плане работы с пинг-сервисами, так и в плане более быстрой работы пользователя с новыми проектами. Вроде бы получается не плохо, по крайней мере работа с базой данных ускорилась на несколько порядков. Так что, пока ковыряюсь в коде – можете предлагать какие новые возможности можно добавить в пинговалку, а также отписываться о найденных ошибках, а я их постараюсь исправить до выхода новой версии.
Книжная полка
![]() |
Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
|
![]() |
![]() |
Название: О чем не пишут в книгах по Delphi
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
|
![]() |



