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

Ещё один пост об использовании библиотеки MSHTML и размышления на тему того, что лучше использовать – регулярные выражения или DOM? Вчера я рассказывал о том, как получить адрес RSS-канала для сайта с использованием библиотеки MSHTML. В работе использовались, как и в большинстве случаев с DOM в Delphi, IHTMLDocument2, IHTMLElement и IHTMLElementCollection. Для обработки тегов HTML-документа часто больше ничего и не требуется использовать. Однако для работы с мета-тегами трех этих интерфейсов может и не хватить. Например, если Вы решите с помощью них определить, что находится в атрибуте Content мета-тега, содержащего кодировку сайта, то ничего не выйдет. Рассмотрим простой пример

Для примера возьмем любой сайт в Сети, например, про webdelphi.ru Какая разница, что проверять? Мета-тег с кодировкой имеется и выглядит следующим образом:

meta http-equiv=»Content-Type» content=»text/html; charset=UTF-8″

Наша задача – вытянуть в программу значение атрибута content. Пишем приложение для работы с мета-тегами. Пусть оно будет выглядеть так:

metatags

В начале нам необходимо скачать содержимое страницы и создать экземпляр IHTMLDocument2:

uses
  ... Variants, ..., MSHTML, ActiveX, hTTPSend {synapse};
 
function Tfmain.GetDOM(URL: string): IHTMLDocument2;
var
  V: OleVariant;
  Content: TStringStream;
begin
  with THTTPSend.Create do
  begin
    if HTTPMethod('GET', URL) then
    begin
      Content := TStringStream.Create;
      Result:=CoHTMLDocument.Create as IHTMLDocument2;
      try
        Content.LoadFromStream(Document);
        V := VarArrayCreate([0, 0], varVariant);
        V[0] := Content.DataString;
        Result.write(PSafeArray(TVarData(V).VArray))
      finally
        Content.Free
      end;
    end;
  end;
end;

В случае успешной загрузки контента метод вернет нам экземпляр IHTMLDocument2 с которым мы будем дальше работать. Теперь пишем обработчик OnClick кнопки в котором будем заполнять наш ListBox значениями атрибутов content всех имеющихся на странице мета-тегов, а также искать мета-тег, содержащий кодировку. Что отличает этот мета-тег от, например, мета-тега с ключевыми словами или описанием?  Отличает его отсутствие атрибута name, вместо этого используется атрибут http-equiv. Вроде бы ничего особенного нету – попробуем поискать по значению этого атрибута. Пишем:

procedure Tfmain.btnGetClick(Sender: TObject);
var
  DOC: IHTMLDocument2;
  TagCollection: IHTMLElementCollection;
  Tag: IHTMLElement;
  i:integer;
begin
  DOC := GetDOM(edURL.Text);
  if DOC=nil then Exit;
  ListTags.Clear;
  TagCollection:=DOC.all.tags('meta')as IHTMLElementCollection;
  for I := 0 to TagCollection.length-1 do
    begin
      Tag:=TagCollection.item(i,0) as IHTMLElement;
      ListTags.Items.Add(Tag.getAttribute('content',0));
      {проверяем атрибут http-equiv}
      if Tag.getAttribute('http-equiv',0)<>null then
        ListTags.Items.Add('Тег с кодировкой найден. Значение content = '+Tag.getAttribute('content',0))
    end;
end;

Казалось бы, что может быть проще — найти атрибут и вывести сообщение в ListBox. Результат работы программы представлен на рисунке ниже (на кодировку текста не обращайте внимание):

metatags2

Как видите – сообщения, которое мы хотели бы увидеть – нет, хотя content из тега с кодировкой прекрасно выписался в ListBox (4 строка сверху). WTF? – спросите Вы. Ничего криминального не произошло, просто в нашем листинге допущена не сказать, что ошибка, но некорректность. Дело в том, что библиотека MSHTML достаточно большая и многофункциональная и практически для каждого элемента на странице в библиотеке можно обнаружить свой интерфейс, наиболее удобно его (элемент) описывающий. Давайте подкорректируем наш код таким образом, чтобы программа заработала как надо. Для этого воспользуемся ранее не рассматриваемом в моем блоге интерфейсом IHTMLMetaElement.

procedure Tfmain.btnGetClick(Sender: TObject);
var
  DOC: IHTMLDocument2;
  TagCollection: IHTMLElementCollection;
  Tag: IHTMLMetaElement;
  i:integer;
begin
  DOC := GetDOM(edURL.Text);
  if DOC=nil then Exit;
  ListTags.Clear;
  TagCollection:=DOC.all.tags('meta')as IHTMLElementCollection;
  for I := 0 to TagCollection.length-1 do
    begin
      Tag:=TagCollection.item(i,0) as IHTMLMetaElement;
      ListTags.Items.Add(Tag.content);
      {проверяем атрибут http-equiv}
      if LowerCase(Tag.httpEquiv)='content-type' then
        ListTags.Items.Add('Тег с кодировкой найден. Значение content = '+Tag.content)
    end;
end;

Проверяем работу:

metatags3

Как видите, все оказалось довольно просто и, даже в какой-то мере, более красиво. Какие выводы можно сделать на основании приведенного выше примера работы с DOM и MSHTML?

Во-первых, приведенный выше пример – это лишь частный случай, показывающий, что эта библиотека очень удобна в плане работы с html – не надо писать регулярные выражения, а потом надеяться на то, что регулярка не “слетит” после обновления сайта.

Во-вторых, при разборе DOM необходимо как можно точнее описывать в программе то, что мы хотим разобрать и получить и, соответственно, подбирать необходимый интерфейс. Приведенный выше пример тому подтверждение – программа не глючила, не выбрасывала ошибки, но она не выполняла возложенную на неё задачу. Это не значит, что мы должны на каждый вид тега определять свой интерфейс. Например, используя IHTMLElement удобно получать значения различных тегов внутри тела документа – парсить ссылки на странице, проверять атрибуты рисунков и т.д., для мета-тегов правильнее использовать, рассмотренный выше IHTMLMetaElement, для анализа CSS – свои интерфейсы в зависимости от задачи и т.д. Может показаться, что работа в этом случае окажется запутанной, а код программы раздуется до непомерных размеров, но могу сказать, что в итоге исходник программы станет намного читабельнее и понятнее, а программа будет ещё более устойчивой ко всяким “неожиданностям” при парсинге HTML.

Скачать исходник: Исходники —> Интернет и Сети —> Прочие

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

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

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

Сергей
Сергей
23/06/2011 13:48

У меня есть вопрос. Как используя IHTMLDocument2 найти ссылки, которые размещены в комментариях? Пробую просто найти теги комментариев (tags(‘!—‘)), но он их не видит…