【问题标题】:Delphi HTML parsing with HtmlPDelphi HTML 解析与 HtmlP
【发布时间】:2014-10-16 10:06:32
【问题描述】:

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

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

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

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

提前致谢。

编辑

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

【问题讨论】:

  • 您能否至少发布您尝试解析的实际 html 以及您想要达到的结果?
  • @whosrdaddy 我添加了更多信息。

标签: delphi html-parsing delphi-xe3 html-parser


【解决方案1】:

您的代码有问题,请您在此 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() 来检查 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: Some tip to parse this html table?

【讨论】:

  • 非常感谢您发现问题并正确解释问题:) 非常感谢。
  • 不确定 SO 是否会自动通知您,但以防万一,这只是为了让您知道我已在答案中添加了有关如何获取 IHtmlDocument2 对象以加载网页,尽管 JavaScript 错误。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-04
  • 2015-08-07
  • 1970-01-01
相关资源
最近更新 更多