【问题标题】:Application locks while trying to access TWebbrowsers HTML尝试访问 TWebbrowsers HTML 时应用程序锁定
【发布时间】:2011-05-08 22:14:23
【问题描述】:

编辑 将其缩小到这一行,

HTML := wb.OleObject.Document.documentElement.innerHTML;

消耗时间...如何加快速度?

使用以下代码,我的应用程序在尝试访问页面的 HTML (Delphi XE) 时可能会挂起 1-2 秒。

function Button1Click(Sender : TObject);
begin
   wb.navigate('http://10.0.0.154/stats');
   // Use a timer to poll the page - dont wait and process app messages
   timer1.enabled := true;
end;

procedure Timer1Timer(Sender : TObject);
var
  HTML : WideString;
begin
   If GetHTML(HTML) = true then
   begin
      Timer1.enabled := false;
      { do something }
   end;
end;


function GetHTML(var HTML : WideString) : boolean;
var
  Document : IHTMLDocument2;
begin
  HTML := '';
  Result := false;

  Document := wb.DOcument as IHTMLDocument2;
  If Assigned(Document) then
  begin
    try
      HTML := wb.OleObject.Document.documentElement.innerHTML;
      Result := true;
    except
      Result := false;
    end;
  end;
end;

但是我注意到在我的 GetHTML 方法中可能需要 1-2 秒才能返回某些内容,并且它会锁定 UI。用 Delphi XE 查看 AQTime,它说方法调用很慢(1-2 秒)。它是零星的,我想知道当页面仍在加载中时它是否会失败。

我正在加载的页面是一个内部页面,充满了 javascript 和 500k 大,我不能使用 OnDocumentComplete 因为它在页面准备好之前触发,即使我检查 ReadyState 它仍然会触发早点。

谁能解释一下,如果他们有更快的方法可以访问 TWebbrowser 的 HTML?

【问题讨论】:

  • wb.Navigate 立即返回,因此不是应用程序挂起的原因。 TWebBrowser 必须下载页面,然后将其呈现以显示,这就是获取 HTML 的延迟发生的地方。如果您想要的只是原始 HTML,为什么不使用 Indy、ICS 或 TDownLoadURL(标准操作)?
  • 一旦 javascript 完成处理,我就在 HTML 之后,javascript 也是我需要使用 TWebbrowser 的原因,因为 Indy、ICS 等不会运行 javascript。
  • 您是否尝试使用outerHtml 而不是innerHtml?也许它会有所作为。此外,如果您托管 TWebBrowser 控件,使用 FEATURE_BROWSER_EMULATION 强制 TWebBrowser 使用最新版本进行显示可能会有所帮助(撰写本文时为 9;托管控件的默认模式为 7)。也许这有速度优势,虽然我没有测试。

标签: delphi profiling delphi-xe twebbrowser


【解决方案1】:

请记住,在导航页面时 OnDocumentComplete 可以触发多次(帧)。

如何正确实现 OnDocumentComplete:

procedure YourForm.OnDocumentComplete(
  Sender: TObject;
  const pDisp: IDispatch;
  var URL: OleVariant);
var
  currentBrowser: IWebBrowser;
  topBrowser: IWebBrowser;
  document: OleVariant;
  windowName: string;
begin
  currentBrowser := pDisp as IWebBrowser;
  topBrowser := (Sender as TWebBrowser).DefaultInterface;
  if currentBrowser = topBrowser then
    ShowMessage('Complete document was loaded')
  else
  begin
    document := currentBrowser.Document;
    windowName := document.ParentWindow.Name;
    ShowMessage(Format('Frame "%s" was loaded', [windowName]));
  end;
end;

来源:

http://www.cryer.co.uk/brian/delphi/twebbrowser/twebbrowser_events.htm#OnDocumentComplete

【讨论】:

    【解决方案2】:

    您的问题似乎是在您尝试获取 HTML 之前,您不允许 TWebBrowser 完成页面的加载。这只是一个猜测,因为您没有显示调用 wb.Navigate 的代码的方式,并且您必须处理获取 InnerHTML 的异常。

    您应该尝试以下方法:

    procedure TForm1.GetHTML(URL: string; var HTML: string);
    begin
      wb.Navigate(URL);
      Application.ProcessMessages;
      while wb.Busy do
        Application.ProcessMessages;
      HTML := wb.OleObject.Document.documentElement.innerHTML;
    end;
    

    【讨论】:

    • 谢谢,我更新了我的示例。我发现 .Busy 和 .ReadyState 即使页面可能还没有准备好,它也会返回 true/complete。
    • @Wizzard - .Busy 将返回 True,直到 TWebBrowser 完成页面加载,然后 .Busy 更改为 False。由于 JavaScript,TWebBrowser 可能会继续执行操作,但 HTML 将已加载。
    • 使用 wb.Busy 读取忙碌状态有时会阻塞整个应用程序一段时间。看来 wb.Busy 不是一个简单的标志。
    【解决方案3】:

    与@crefird 的回答一样,我怀疑您正试图在浏览器完成其工作之前访问 InnerHTML...

    如果 ReadState/Busy 没有返回 TWebBrowser 忙碌状态的准确表示,您可以这样做:

    1) 创建一个全局变量或表单的私有成员...例如“FBrowserBusy: Boolean”(不要忘记在调用“.Navigate”之前将其初始化为真) 2)正如@crefird 在他的回答中展示的那样,使用“while”循环,只用“wb.Busy”代替“FBrowserBusy”。 3) 将 OnDocumentComplete 事件添加到您的 TWebBrowser 实例,并设置 FBusy := False;

    这将消除任何冲突,并确保 TWebBrowser 对象在您的外部例程继续询问文档之前已完成加载文档。

    希望对您有所帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-14
      • 1970-01-01
      • 1970-01-01
      • 2015-12-11
      • 1970-01-01
      相关资源
      最近更新 更多