您的代码的问题(您能否在此 q 中恢复)在于以下行:
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,该 TStringlist 已加载了您的赛车结果页面的本地保存副本。
您一直在使用“后期绑定”(即变体)与 MS Dom Parser 进行交互。没关系,如果比使用像我刚刚引用的代码这样的早期绑定慢一点,但它可以隐藏或掩盖某些类型的错误。
我的代码将解析后的 HTML 的访问分成几个阶段,并使用 Assert()s 检查 DOM 对象是否确实存在。它们都通过了 Assert 测试,但最后一个 Assert(即 All 集合的长度不为零)失败。
您可能想在上面运行我的代码并检查 Body 对象的 OuterHtml 属性。它只是 '' 加上一些嵌入式 CRLF。(这个答案的原始版本在这里停止)。
更新:多一点挖掘揭示了你的问题的原因。要查看它,请将您的问题网页保存在本地,然后创建一个新的 VCL 项目,在其表单中添加一个 TWebBrowser、两个 TMemo 和 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 错误弹出窗口。如果您通过它们单击“是”,您将看到第二个备忘录接收到您的代码经过稍微修改的版本的输出。
因此,我认为您的代码的问题在于,因为您正在创建一个没有 GUI 的 IHTMLDocument 对象,所以脚本错误无法表现出来。我认为问题隐藏在您的非 gui Doc 对象中,因为 IIRC(COM 对象的 MS 规范)要求异常永远不会跨越 COM 主机与其客户端之间的边界传播,因此您永远不会发现错误。显而易见的解决方法是将页面加载到 TWebBrowser 并使用其中的 Doc 对象。
更新#2:当我第一次写这个答案时我没有意识到的是,你可以告诉你的 IHtmlDocument 不要尝试弹出 JavaScript 错误,这样它就会加载而不是拒绝。你需要做的就是把
Doc.DesignMode := 'On';
在您尝试将任何内容加载到其中之前,例如通过调用其 .Write 方法。Fwiw,当使用 TWebBrowser 的 Silent 属性为 True 时,您可以做类似的事情。
顺便说一句,如果您尝试解析表格以获取数据,您可能想看看我之前的答案:
Delphi:解析这个 html 表的一些技巧?