問題
サーバー (ブラウザー) 側ではなく、クライアント (Python) 側でページの実質的な処理を実行する必要がある場合があります。たとえば、何らかの機械学習システムが既に Python で記述されていて、アクションを実行する前にページ全体を分析する必要がある場合、一連のfind_element
呼び出しでそれを実行することは可能ですが、それぞれが非常に高価になるため、非常にコストがかかります。呼び出しは、クライアントとサーバー間の往復です。また、ブラウザで動作するように書き直すと、コストがかかりすぎる可能性があります。
Selenium の識別子がそうしない理由
ただし、 Selenium 独自の識別子と一緒に DOM をシリアル化する効率的な方法がわかりません。Selenium は、呼び出し時、または呼び出しから DOM ノードが返される(またはスクリプトに与えるコールバックに渡される) ときに、必要に応じてこれらの識別子を作成します。しかし、各要素の識別子を取得するために呼び出すと、振り出しに戻ります。ブラウザーで必要な情報を使用して DOM を装飾することは想像できますが、なんらかの事前割り当てを要求するパブリック API はありません。find_element
execute_script
execute_async_script
find_element
WebElement
ID。実際のところ、これらの識別子は不透明になるように設計されているため、ソリューションが必要な情報を取得するために何らかの方法で管理されたとしても、クロスブラウザーの実行可能性と継続的なサポートが懸念されます.
解決策
ただし、両側で機能するアドレス指定システムを取得する方法があります: XPath. クライアント側で DOM シリアライゼーションをツリーに解析し、関心のあるノードの XPath を取得し、これを使用して対応する WebElement を取得するという考え方です。find_element
したがって、クリックを実行する必要がある単一の要素を決定するために何十回ものクライアント サーバー ラウンドトリップを実行する必要がある場合は、これをページ ソースの最初のクエリとXPath を使用した単一の呼び出しに減らすことができます。あなたが必要です。
これは非常に単純な概念実証です。Google フロント ページのメイン入力フィールドをフェッチします。
from StringIO import StringIO
from selenium import webdriver
import lxml.etree
#
# Make sure that your chromedriver is in your PATH, and use the following line...
#
driver = webdriver.Chrome()
#
# ... or, you can put the path inside the call like this:
# driver = webdriver.Chrome("/path/to/chromedriver")
#
parser = lxml.etree.HTMLParser()
driver.get("http://google.com")
# We get this element only for the sake of illustration, for the tests later.
input_from_find = driver.find_element_by_id("gbqfq")
input_from_find.send_keys("foo")
html = driver.execute_script("return document.documentElement.outerHTML")
tree = lxml.etree.parse(StringIO(html), parser)
# Find our element in the tree.
field = tree.find("//*[@id='gbqfq']")
# Get the XPath that will uniquely select it.
path = tree.getpath(field)
# Use the XPath to get the element from the browser.
input_from_xpath = driver.find_element_by_xpath(path)
print "Equal?", input_from_xpath == input_from_find
# In JavaScript we would not call ``getAttribute`` but Selenium treats
# a query on the ``value`` attribute as special, so this works.
print "Value:", input_from_xpath.get_attribute("value")
driver.quit()
ノート:
driver.page_source
Selenium のドキュメントには、返される内容の鮮度について保証がないと記載されているため、上記のコードは使用しません。現在の DOM の状態、またはページが最初に読み込まれたときの DOM の状態である可能性があります。
find_element
この解決策には、動的コンテンツに関する問題とまったく同じ問題があります。分析の実行中に DOM が変更された場合、DOM の古い表現で作業しています。
分析の実行中に JavaScript イベントを生成する必要があり、これらのイベントによって DOM が変更される場合は、DOM を再度取得する必要があります。(これは前のポイントと似ていますが、呼び出しを使用するソリューションは、呼び出しのシーケンスを慎重に並べることによって、このfind_element
ポイントで話している問題をおそらく回避できます。)
lxml
のツリーは、 から取得した XPathが DOM 内の対応する要素をアドレス指定しないという点で、DOM ツリーとは構造的に異なる可能性があります。ブラウザーに渡された HTML のクリーンアップされたシリアル化されたビューとはlxml
どのようなプロセスですか。lxml
したがって、ポイント 2 と 3 で述べた問題を防ぐようにコードが書かれている限り、これはありそうなシナリオではないと思いますが、不可能ではありません。