1

我正在尝试解析一个 html 文件,以便可以从表中提取数据。
所以我做了一些谷歌魔术,最后在这里问了一个类似的问题。

在那个问题中,他们建议使用 HTMLP 来解析 html。所以我下载了这些单位并试了一下。

它有效,但我想我错过了一些东西。我完全不知道如何从元素中获取实际文本。

我查看了源代码,但我找不到任何关于此的内容。所以我希望这里有人知道答案。

提前致谢。

编辑

根据要求:我想获取的数据在这里
找到 我想获取这些数据并将每一行转换为将被存储的对象,以便我可以比较不同的做法、资格和种族。

4

1 回答 1

4

您的代码的问题(您能否在此 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 进行交互。没关系,如果比使用像我刚刚引用的代码这样的早期绑定慢一点,但它可以隐藏或掩盖某些类型的错误。

我的代码将解析后的 H​​TML 的访问分成几个阶段,并使用 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 表的一些技巧?

于 2014-08-22T15:59:58.230 回答