サイトのコンテンツを取得するために Selenium WebDriver を使用しています。(注: このサイトには API がありません。できればよかったのに。) サイトは AJAX を使用して、ユーザーがスクロールするときにコンテンツを動的にロードします。そのコンテンツを取得するために、私は Javascript を使用して下にスクロールし、findElements() を使用してコンテンツにアクセスしようとしました。
設定を明確にするために、ページにはいくつかのネストされた要素が含まれており、そのうちの 1 つは「GridItems」クラス (名前または ID なし) を持つ div です。この div には、"Item" クラスを持つ多くの子要素が含まれています (ここでも、名前や ID はなく、クラスのみです)。divでクラス「Item」を持つすべての要素を取得したい。ページが最初に読み込まれたときに約 25 の項目にアクセスでき (必ずしも現在のウィンドウに表示されているとは限りませんが、DOM で利用可能です)、下にスクロールするとさらに多くの項目が読み込まれます。
私の主な問題は次のとおりです。最初に、一番下に到達したときにスクロールを停止したいです。ただし、使用する停止条件がわかりません。ページの一番下に到達したことを確認するにはどうすればよいですか? Window.scrollheight は機能しません。これは、コンテンツの追加が完了した後の高さではなく、既存のウィンドウの高さを与えるためです。ページの下部にある要素が表示/クリック可能かどうかをテストすることを考えましたが、そうでない場合は、まだ読み込まれていないためではなく、まだ読み込まれていない可能性があります。Wait を使用しても機能しない場合があります。タイムアウトした場合、それが底に達していないためなのか、単にロードに時間がかかっているためなのかわかりません。
2 つ目の問題は、下にスクロールするとさらにいくつかの要素が読み込まれることですが、最終的には、下にスクロールすると下からさらに読み込まれ、DOM の一番上の要素が削除されます。つまり、一番下までスクロールしてから findElements() を使用してすべてのアイテムを取得することはできません。最初のアイテムの多くがなくなってしまうからです。予想されるアイテムの数はわかっているので、現在、次のことを行っています。
int numitems = 135;
List<WebElement> newitems;
List<WebElement> allitems = new ArrayList<WebElement>(50);
do {
//scroll down the full length of the visible window three times
for(int i=0; i < 3; i++)
{
//scroll down
js.executeScript("window.scrollTo(0, document.body.offsetHeight)");
}
//check how many items are now available
//if it runs too fast, it may get to the next line before it finishes scrolling;
//make it wait until the desired div is visible
WebElement cont = (new WebDriverWait(driver, 100))
.until(ExpectedConditions.presenceOfElementLocated(By.className("GridItems")));
//get all Items in the div
newitems = cont.findElements(By.className("Item"));
//add all the items extracted after scrolling 3 times to the list
allitems.addAll(newitems);
//repeat until there are more items in the general list than are expected
//to be found. This is hacky; I wish there was a better stopping condition
}while(numitems > allitems.size());
つまり、ページを 3 回スクロールし、スクロール後に使用可能なすべての要素を取得して、それらをリストに追加します。リスト内の要素が予想以上に多くなるまで、これを繰り返します。
これに関する問題は、スクロールによって毎回異なる数の項目が DOM に追加されるため、反復ごとに allitems リストに追加される項目がしばしば重複することです。Elements は一意の ID を持つ単なるオブジェクトであり、実際の HTML に関する情報は含まれていないため、それらが重複しているかどうかを確認することはできません。スクロールが完全にオーバーラップしないと、一部のアイテムが失われることもあります。また、下にスクロールしたため、一番上に落ちたリスト内の前の項目は DOM への接続を失い、それらを処理しようとすると StaleElementReferenceException が発生します。
コードがぎこちなくなりますが、取得した各アイテムを処理できると思います。これにより、実際のコンテンツを確認して重複を見つけることもできます。これでスキップしないことが確実になるかどうかはわかりません。
これを行う最善の方法について何か提案はありますか? ここで非常に重要/明白な何かが欠けていますか? AJAX コンテンツの読み込みに関する SO に関するその他の質問は、多少異なる問題に対処しています。(たとえば、Wait を含めましたが、通常、コンテンツが読み込まれずに待たなければならないという問題はありません。) これを行うためのより良い方法があるはずです - ありますか?
長々とした投稿で申し訳ありません。明確だったと思います。
どうもありがとう、bsg
編集:
受け入れられた回答は、質問の一部にしか回答していないことを認識しています。残りの部分では、一度に 1 画面ずつ下にスクロールして、毎回すべての新しい要素を取得することで、何も失うことがないことがわかりました。各スクロールの後、すべての要素をロードし、それぞれのコンテンツを保存するための処理を行いました。これにより多くの冗長性が生じますが、これを排除するために HashSet を使用しました。受け入れられた回答のコードによって決定されるように、一番下に到達するとスクロールを停止します。お役に立てれば。