Проблема с вашим кодом, который вы не могли бы восстановить в этом вопросе, связана со строкой:
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