应用程序在尝试访问TWebBrowser的HTML时锁定。

3

编辑 将其缩小到此1行,

(注:本段内容已经是中文,无需翻译)
HTML := wb.OleObject.Document.documentElement.innerHTML;

这段代码会在我的应用程序尝试访问页面的HTML时挂起1-2秒钟(Delphi XE)。如何加快速度?

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(标准操作)呢? - crefird
当javascript处理完成后,我需要获取HTML,而且正是由于javascript的原因,我需要使用TWebbrowser,因为Indy、ICS等无法运行javascript。 - Wizzard
你尝试使用outerHtml而不是innerHtml了吗?也许会有所不同。另外,如果你正在托管TWebBrowser控件,使用FEATURE_BROWSER_EMULATION来强制TWebBrowser使用最新版本进行显示(截至本文撰写时为9;托管控件的默认模式为7)可能会有所帮助。也许这样做会有速度优势,但我没有测试过。 - Heinrich Ulbricht
3个回答

4
请记住,在导航页面时,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;

source:

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
@Wizzard - .Busy会在TWebBrowser完成页面加载之前返回True,然后.Busy变为False。由于JavaScript的原因,TWebBrowser可能会继续执行某些操作,但HTML已经被加载。 - crefird
使用wb.Busy读取繁忙状态有时会阻塞整个应用程序一段时间。看起来wb.Busy不是一个简单的标志。 - Mehmet Fide

0

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

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

1)创建一个全局变量或者您表单的私有成员,例如 "FBrowserBusy: Boolean"(别忘了在调用 ".Navigate" 之前将其初始化为 TRUE) 2)像 @crefird 在他的回答中演示的那样,使用一个 "while" 循环,只是将 "wb.Busy" 替换为 "FBrowserBusy"。 3)为您的 TWebBrowser 实例添加 OnDocumentComplete 事件,并将其设置为 FBusy := False;

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

希望您会发现这个有用!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接