PlaywrightとScrapelessを使用してシャドウDOMをスクレイプする方法
Lead Scraping Automation Engineer
TL;DR:
- Shadow DOMはホスト要素のライトDOMには存在しないテキストを表示できます。 JavaScriptはカスタム要素の読み込み後にシャドウルート内に表示されるコンテンツを作成する場合があります。
- PlaywrightのCSSおよびテキストロケーターはオープンシャドウルートを通過できます。 ドキュメントレベルのDOMクエリはシャドウ境界で停止するため、同じセレクターは
document.querySelectorでは失敗します。 - クローズドシャドウルートとXPathは異なる期待が必要です。 Playwrightのロケーターはクローズドルートを貫通せず、XPathセレクターは自動的なShadow DOMの通過をサポートしていません。
- Shadow DOMの抽出ロジックはブラウザのトランスポートとは独立しています。 ワークフローをローカルで検証した後、ホストされたブラウザまたは管理された出口が必要な場合には、同じロケーターをScrapeless CDP接続を通じて再利用できます。
- 無料で開始可能。 新しいScrapelessアカウントには無料のScraping Browserランタイムが含まれています—app.scrapeless.comでサインアップしてください。
シャドウDOMコンポーネントはホスト要素の通常のライトDOMの子要素に存在しないテキストを表示できます。このガイドでは、Playwrightを使用して実際のオープンシャドウルートを遍歴し、ドキュメントレベルのセレクターが何も返さない理由を示し、その抽出ロジックを同じコードをクラウドブラウザで実行するScrapeless Scraping Browser接続から分離します。
シャドウDOMがスクレイパーに与える変更
シャドウDOMはカスタム要素にカプセル化されたサブツリーを提供します。ページのメインドキュメントはホストタグを認識できますが、通常のDOM APIはそのホストからシャドウルートに自動的に移動することはありません。
下記で使用される公開MDNデモでは、ソースにはdata-text属性を持つ<popup-info>ホストが含まれています。遅延スクリプトがそのタグをアップグレードし、attachShadow({ mode: "open" })を呼び出して内部の.infoスパンを作成します。ブラウザはそのスパンからヘルプテキストを表示します。初期のHTMLにはそのスパンは全く含まれていません。
MDNのシャドウDOMガイドはシャドウ境界とオープンおよびクローズドルートの違いを定義しています。この区別は重要です:オープンルートはelement.shadowRootを公開しますが、クローズドルートはそのAPIを通じてnullを返します。
ScrapelessとのPlaywrightの使用理由
Playwrightのロケーターは通常のCSSおよびテキスト検索のためにオープンシャドウルートを貫通します。Playwrightロケーター文書では2つの例外が述べられています:XPathはシャドウルートを貫通せず、クローズドモードのルートはサポートされていません。
遍歴コードはChromiumがどこで実行されるかに依存しません。ローカルブラウザがあれば公共デモには十分です。Scrapeless Scraping Browserは、ターゲットがホストされたブラウザ、管理されたフィンガープリント、居住地の出口、または永続的なクラウドセッションを必要とする場合に役立ちます。Playwrightはconnect_over_cdp APIを通じてそのブラウザにアクセスします。
前提条件
- Python 3.10以降。
playwright1.59.0または現在の互換バージョン。- 完全に実行可能なフォールバック用のローカルChrome/Chromiumランタイム。
- クラウド接続用の資金提供されたScrapelessアカウントと
SCRAPELESS_API_KEY。検証アカウントは新しいブラウザセッションに対してコード14500を返したため、クラウド接続ブロックは成功として表示されるのではなく、前提条件のギャップとしてマークされています。
インストール
bash
python -m pip install "playwright==1.59.0"
検証用ホストには既にGoogle Chromeが/usr/bin/google-chromeに存在します。あなたのマシンになければ、プロジェクトの通常のブラウザセットアップコマンドでPlaywrightが管理するChromiumをインストールしてください。
Scrapeless Scraping Browserに接続する
注:この接続には資金提供されたScrapelessブラウザ残高が必要です。最終検証に使用したアカウントは
insufficient balance, please recharge firstを返しました;自身の資金提供されたキーで実行してください。このセクションの後の遍歴ブロックは、同じ公共ページに対して完全にローカルChromeで実行されました。
python
import os
from urllib.parse import urlencode
from playwright.sync_api import sync_playwright
params = urlencode({
"token": os.environ["SCRAPELESS_API_KEY"],
"sessionTTL": 120,
"proxyCountry": "US",
})
cdp_url = f"wss://browser.scrapeless.com/api/v2/browser?{params}"
with sync_playwright() as p:
browser = p.chromium.connect_over_cdp(cdp_url)
page = browser.contexts[0].pages[0]
# このページで以下の抽出ステップを実行します。
browser.close()
Scrapeless開発者ドキュメントは接続パラメータの現在のソースです。一度pageが存在すれば、シャドウDOMの検索は標準的なPlaywrightとなります。
ホストとそのオープンルートを検査する
MDNコンポーネントデモは小さく、公開されています:
python
TARGET_URL = (
"https://mdn.github.io/web-components-examples/"
"popup-info-box-web-component/"
)
page.goto(TARGET_URL, wait_until="domcontentloaded")
page.wait_for_selector("popup-info .info")
host = page.locator("popup-info")
print("host light-DOM text:", repr(host.inner_text()))
print("shadow mode:", host.evaluate("element => element.shadowRoot.mode"))
text
host light-DOM text: ''
shadow mode: open
空のホストテキストは、コンポーネントがデータを含んでいないという証拠ではありません。それはホストに通常の子テキストがないことを示しています。実際の.infoノードは、カスタム要素がアップグレードされた後に作成されるオープンシャドウルートの下に存在します。
ドキュメントセレクタが境界で止まることを証明する
ドキュメントからのプレーンなブラウザDOMクエリを実行します:
python
plain_match = page.evaluate(
"document.querySelector('popup-info .info') !== null"
)
print("document query found inner node:", plain_match)
text
document query found inner node: False
セレクタは有効なCSSです。失敗はスコープから来ます:document.querySelectorはドキュメントツリーを検索し、ホストのシャドウツリーには降りていきません。
Playwrightロケータがオープンルートを貫通させる
Playwrightの通常のロケーターチェーンはオープンルートを横断します:
python
info = page.locator("popup-info .info")
record = {
"host": "popup-info",
"shadow_mode": host.evaluate("element => element.shadowRoot.mode"),
"text": info.inner_text(),
"source_url": page.url,
}
print("inner matches:", info.count())
print("help text:", record["text"])
print("text chars:", len(record["text"]))
text
inner matches: 1
help text: あなたのカード検証コード(CVC)は追加のセキュリティ機能です。それはカードの裏面にある最後の3または4桁の数字です。
text chars: 118
そのテキストはコンポーネントの本当のdata-text値がシャドウルートスパンにコピーされたものです。文字数には句読点やスペースが含まれているため、空の要素やアイコンの代替テキストを誤って抽出することを防ぐための有用なガードです。
ホストされたブラウザに同じロケータを移動させる準備はできましたか? 無料のScrapelessアカウントを作成し、ブラウザ作成後のすべての行を変更せずに保持します。
完全なローカル検証スクリプト
このスクリプトは、ローカルのChromeで全診断を実行します。launch(...)を上記のCDP接続に置き換えることで、抽出ロジックを変更せずにScrapelessに移動します:
python
from playwright.sync_api import sync_playwright
TARGET_URL = (
"https://mdn.github.io/web-components-examples/"
"popup-info-box-web-component/"
)
with sync_playwright() as p:
browser = p.chromium.launch(
executable_path="/usr/bin/google-chrome",
headless=True,
)
page = browser.new_page()
page.goto(TARGET_URL, wait_until="domcontentloaded")
page.wait_for_selector("popup-info .info")
host = page.locator("popup-info")
info = page.locator("popup-info .info")
host_text = host.inner_text()
shadow_mode = host.evaluate("element => element.shadowRoot.mode")
plain_match = page.evaluate(
"document.querySelector('popup-info .info') !== null"
)
help_text = info.inner_text()
assert shadow_mode == "open"
assert not plain_match
assert "カード検証コード" in help_text
print("page title:", page.title())
print("host light-DOM text:", repr(host_text))
print("shadow mode:", shadow_mode)
print("document query found inner node:", plain_match)
print("inner matches:", info.count())
print("help text:", help_text)
print("text chars:", len(help_text))
browser.close()
実行は境界を証明し、単に成功したロケータを示すだけでなく、ライトDOMテキストは空であり、ドキュメントセレクタは偽で、ルートはopenを報告し、Playwrightは118文字のヘルプ文字列を返します。
オープンルートとクローズドルートの意味
オープンルート
attachShadow({mode: "open"})はhost.shadowRootを利用可能にします。PlaywrightのロケータはCSS/テキストセレクタでそれを横断できます。ネストされたオープンルートも同様に処理されますが、それぞれのコンポーネントは異なるスケジュールで読み込まれることがあります。
クローズドルート
attachShadow({mode: "closed"})はhost.shadowRootがnullを返すようにします。Playwrightの通常のロケータはそれを貫通しません。スクリプト注入やブラウザの計測を普遍的な回避策として提示しないでください:クローズドルートは明示的なカプセル化の境界であり、ページコードを変更すると測定しようとしていた動作が変わる可能性があります。
スロットされたコンテンツ
<slot>はシャドウツリーのレイアウト内でライトDOMの子を表示します。割り当てられたノードはホストのライトDOMに属しているため、その所有権とセレクタの動作はシャドウルート内に作成された要素とは異なります。データの所有者を決定する前にライブDOMを検査してください。
一般的なシャドウDOMの失敗モード
ホストは存在しますが、内部ロケータがタイムアウトする
カスタム要素がまだアップグレードされていない場合、JavaScriptが失敗した場合、またはセレクタが閉じたルートを対象としている場合があります。内部要素に待機して、実際の準備状態のマーカーとし、セレクタを変更する前にコンソールエラーを確認してください。
XPathは何も見つけられません
CSSまたはテキストロケータを使用してください。Playwrightの文書化されたシャドウトラバーサルはXPathセレクタには適用されません。
ネストされたコンポーネントが部分的なテキストを返す
大きなホストのinner_text()を呼び出すのではなく、最も深い安定した要素を特定してください。これにより、スロットテキスト、視覚的に非表示のラベル、ネストされたコンポーネントの出力が混在するのを避けることができます。
ソースHTMLには内部ノードがありません
これはJavaScriptがシャドウツリーを構築する際に普通のことです。ブラウザを使用してください。HTTPパーサーはソースレスポンスしか見ることができず、ロード後に作成されたサブツリーを観察することはできません。
結論
シャドウDOMスクレイピングは、セレクタの問題である前にスコープの問題です。どのツリーがノードを所有しているかを証明し、カスタム要素がアップグレードするのを待ち、PlaywrightのCSS/テキストロケータをオープンルートに使用してください。クローズドルートの境界を正直に保ってください。ブラウザトランスポートは別です:パブリックテストサーフェスのためにローカルで実行し、ターゲットがホストされたアンチ検出ブラウザまたは管理された出口を必要とする場合は、ブラウザ作成をScrapeless CDPエンドポイントに切り替えてください。
Scrapelessを始める、Scrapelessの価格を確認する、およびChrome DevToolsプロトコルが制御するものを読む周囲のCDPアーキテクチャについて。
FAQ
Q: PlaywrightはオープンシャドウDOMルートをスクレイピングできますか?
はい。PlaywrightのCSSおよびテキストロケータはオープンシャドウルートを突破できるため、page.locator("popup-info .info")のようなロケータは内部ノードに到達できます。
Q: なぜdocument.querySelectorが同じセレクタで失敗するのですか?
document.querySelectorはドキュメントツリーを検索し、シャドウホストで停止します。オープンルートを通じて自分でクエリを実行する必要があるか、またはそのトラバーサルを実行するPlaywrightロケータを使用する必要があります。
Q: Playwrightはクローズドシャドウルートを突破できますか?
いいえ、通常のロケータの動作を通じてはできません。クローズドルートはhost.shadowRootからnullを返し、文書化されたサポート外の境界です。
Q: XPathはシャドウDOM内で機能しますか?
いいえ。Playwrightの公式ロケータガイダンスによれば、XPathセレクタはシャドウルートを突破しません。CSSまたはテキストロケータを使用してください。
Q: ネストされたオープンシャドウルートをどのように処理しますか?
安定した内部要素に向けてロケータをチェーンし、その要素を待機します。Playwrightは複数のオープンルートを越えることができますが、各カスタム要素は非同期にアップグレードされる場合があります。
Q: シャドウDOMスクレイピングにはブラウザが必要ですか?
はい、JavaScriptがシャドウツリーを生成する場合は必要です。通常のHTTPレスポンスは、ホストマークアップとスクリプトを含むのみで、実行後に生成されたライブサブツリーは含まれません。
Q: シャドウDOMスクレイピングは合法ですか?
DOMのメカニズムは許可を決定しません。ターゲットの条件とrobotsディレクティブを確認し、公共データを使用し、リクエスト量に上限を設け、センシティブまたは商業的な収集について法的アドバイスを求めてください。
Scrapelessでは、適用される法律、規制、およびWebサイトのプライバシーポリシーを厳密に遵守しながら、公開されているデータのみにアクセスします。 このブログのコンテンツは、デモンストレーションのみを目的としており、違法または侵害の活動は含まれません。 このブログまたはサードパーティのリンクからの情報の使用に対するすべての責任を保証せず、放棄します。 スクレイピング活動に従事する前に、法律顧問に相談し、ターゲットウェブサイトの利用規約を確認するか、必要な許可を取得してください。



