Разбор HTML в Delphi с помощью HtmlP

Я пытаюсь разобрать html-файл, чтобы извлечь данные из таблицы.
Итак, я немного погуглил и оказался здесь, где был задан аналогичный вопрос.

В этом вопросе они предложили использовать HTMLP для разбора html. поэтому я скачал модули и попробовал.

Это работает, но я что-то упускаю, я думаю. Я совершенно не знаю, как получить фактический текст из элемента.

Я просмотрел источник, но ничего не нашел по этому поводу. Так что я надеялся, что кто-то здесь знает ответ.

Заранее спасибо.

Редактировать

По запросу: данные, которые я пытаюсь получить, найдены здесь
Я хочу получить эти данные и превратить каждую строку в объект, который будет храниться, чтобы я мог сравнивать различные практики, квалификации и гонки.


person Teun Pronk    schedule 22.08.2014    source источник
comment
можете ли вы опубликовать хотя бы фактический html, который вы пытаетесь разобрать, и какого результата вы хотите достичь?   -  person whosrdaddy    schedule 22.08.2014
comment
@whosrdaddy Я добавил больше информации об этом.   -  person Teun Pronk    schedule 22.08.2014


Ответы (1)


Проблема с вашим кодом, который вы не могли бы восстановить в этом вопросе, связана со строкой:

for i:=0 to doc.body.all.length-1 do

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

procedure GetTable2(FSource : TStrings);
var
  Doc : IHtmlDocument2;
  Body : IHtmlElement;
  All : IHtmlElementCollection;
begin
  Doc := coHTMLDocument.Create as IHTMLDocument2;
  Doc.Write(PSafeArray(FSource.Text));
  Doc.Close;
  Assert(Doc <> Nil);
  Body := Doc.body;
  Assert(Body <> Nil);
  All := Body.All as IHtmlElementCollection;
  Assert(All <> Nil);
  Assert(All.Length <> 0);
end;

Этому передается TStringlist, который был загружен с локально сохраненной копией вашей страницы результатов гонок.

Вы использовали «позднее связывание», то есть варианты, для взаимодействия с MS Dom Parser. Это нормально, хотя и немного медленнее, чем использование раннего связывания, такого как код, который я только что процитировал, но он может скрыть или скрыть некоторые виды ошибок.

Мой код разбивает доступ к проанализированному HTML на несколько этапов и использует Assert() для проверки существования объектов DOM. Все они проходят тесты Assert, но последнее утверждение о том, что длина коллекции All не равна нулю, не проходит.

Вы можете запустить мой код выше и проверить свойство OuterHtml объекта Body. Это всего лишь '' плюс несколько встроенных CRLF. (Исходная версия этого ответа остановилась здесь).

Обновление: еще немного копания выявило причину вашей проблемы. Чтобы увидеть это, сохраните свою проблемную веб-страницу локально, затем создайте новый проект VCL, добавьте в его форму TWebBrowser, два TMemos и в TButtons, затем вставьте в него следующий код (очевидно, вам нужно настроить файл Form. Создайте, чтобы загрузить локальную копию страницы):

procedure GetTable(All : IHtmlElementCollection; Output : TStrings);
var
  el:OleVariant;
  i,tdc,mc:integer;
  tst,v:string;
begin
  v:='';
  mc:=4;
  tdc:=0;
  for i:=0 to all.length -1 do
  begin
    el:= All.item(i, '');
    if el.tagname='TD' then
    begin
      inc(tdc);
      if tdc>mc then
      begin
        Output.Add(v);
        v:='';
        tdc:=1;
      end;
      if v='' then v:=el.InnerText
      else v:=v+'^'+el.InnerText;
    end;
  end;
end;

procedure ProcessDoc(Doc : IHtmlDocument2; Output : TStrings);
var
  Body : IHtmlElement;
  All : IHtmlElementCollection;
  V : OleVariant;
begin
  Assert(Doc <> Nil);
  Body := Doc.Body;
  Assert(Body <> Nil);
  All := Body.All as IHtmlElementCollection;
  Assert(All <> Nil);
  Assert(All.Length <> 0);
  GetTable(All, Output);
end;


procedure TForm1.FormCreate(Sender: TObject);
begin
  Memo1.Lines.LoadFromFile('D:\aaad7\html\race.htm');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  V : OleVariant;
begin
  WebBrowser1.Navigate('about:blank');  //  This line is so that the WebBrowser
    // has a Doc object
  Doc := WebBrowser1.Document as IHTMLDocument2;
  V := VarArrayCreate([0, 0], varVariant);
  V[0] := Memo1.Lines.Text;
  try
    Doc.Write(PSafeArray(TVarData(V).VArray));
  finally
    Doc.Close;
  end;  
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  ProcessDoc(Doc, Memo2.Lines);
end;

Когда вы нажмете Button1, вы вскоре увидите причину проблемы (при условии, что вы, как и я, используете IE11, но вы можете получить их и в более ранних версиях), а именно каскад из семи всплывающих окон с ошибками Javascript. Если вы нажмете «Да», вы увидите, что вторая заметка получает вывод слегка адаптированной версии вашего кода.

Итак, я думаю, что проблема с вашим кодом заключалась в том, что, поскольку вы создавали объект IHTMLDocument без графического интерфейса, ошибки сценария не могли проявиться. Я думаю, что проблема скрыта с вашим объектом Doc без графического интерфейса пользователя, потому что IIRC, спецификация MS для объектов COM, требует, чтобы исключения никогда не распространялись через границу между хостом COM и его клиентом, поэтому вы никогда не получите узнать об ошибках. Очевидным обходным решением является загрузка страницы в TWebBrowser и использование оттуда объекта Doc.

Обновление № 2: Когда я впервые написал этот ответ, я не понял, что вы можете указать своему IHtmlDocument не пытаться всплывать ошибки JavaScript, чтобы он загружался, а не отказывался. Все, что вам нужно сделать, это поставить

Doc.DesignMode := 'On';

прежде чем пытаться что-либо загрузить в него, например. вызвав его метод .Write. Fwiw, вы можете сделать то же самое, используя свойство Silent TWebBrowser в True.

Кстати, если вы пытаетесь проанализировать свою таблицу, чтобы получить данные, вы можете взглянуть на этот мой более ранний ответ:

Delphi: какой-нибудь совет по разбору этой HTML-таблицы?

person MartynA    schedule 22.08.2014
comment
Большое спасибо за обнаружение проблемы, а также за правильное ее объяснение :) Большое спасибо. - person Teun Pronk; 25.08.2014
comment
Не уверен, что SO уведомит вас автоматически, но на всякий случай это просто для того, чтобы вы знали, что я добавил в свой ответ упоминание о том, как заставить объект IHtmlDocument2 загружать веб-страницу, несмотря на ошибки JavaScript. - person MartynA; 29.08.2014