あなたのコードの問題は、この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 が渡されます。
MS Dom パーサーと対話するために、「遅延バインディング」、つまりバリアントを使用してきました。先ほど引用したコードのように事前バインディングを使用するよりも少し遅い場合は問題ありませんが、ある種のエラーを隠したり隠したりすることができます。
私のコードは、解析された HTML へのアクセスをいくつかの段階に分割し、Assert() を使用して DOM オブジェクトが実際に存在することを確認します。それらはすべて Assert テストに合格しますが、All コレクションの長さがゼロではないという最後の Assert は失敗します。
上記のコードを実行して、Body オブジェクトの OuterHtml プロパティを調べてみてください。'' にいくつかの埋め込み CRLF を追加しただけです。(この回答の元のバージョンはここで停止しました)。
更新:もう少し掘り下げると、問題の原因が明らかになりました。それを確認するには、問題の Web ページをローカルに保存してから、新しい VCL プロジェクトを作成し、そのフォームに TWebBrowser、2 つの TMemo、および TButton を追加してから、次のコードを貼り付けます (明らかに、フォームを調整する必要があります。ページのローカル コピーをロードするために作成します):
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 を使用していると仮定しますが、以前のバージョンで発生する可能性があります)、つまり 7 つの Javascript エラー ポップアップのカスケードが表示されます。[はい] をクリックすると、2 番目のメモがコードのわずかに変更されたバージョンの出力を受け取ることがわかります。
したがって、コードの問題は、GUI を使用せずに IHTMLDocument オブジェクトを作成していたため、スクリプト エラーが発生する方法がなかったことだと思います。COMオブジェクトのMS仕様であるIIRCでは、例外がCOMホストとそのクライアントの間の境界を越えて伝播しないことが要求されているため、GUI以外のDocオブジェクトでは問題が隠されていると思います。そのため、エラーについて知ることはできません。明らかな回避策は、ページを TWebBrowser にロードし、そこから Doc オブジェクトを使用することです。
更新 #2:この回答を最初に書いたときに気付いていなかったのは、IHtmlDocument に JavaScript エラーのポップアップを試行しないように指示して、拒否する代わりにロードすることができるということです。あなたがする必要があるのは置くだけです
Doc.DesignMode := 'On';
たとえば、その .Write メソッドを呼び出して、何かをロードしようとする前に。TWebBrowser の Silent プロパティを True に設定すると、同様のことができます。
ところで、テーブルを解析してデータを取得しようとしている場合は、私の以前の回答をご覧ください。
Delphi: この HTML テーブルを解析するためのヒントはありますか?