はじめに
簡単なスクレイピングツールを書いていたら、不可解な現象に遭遇してハマってしまいました。
同じような現象に遭遇した人が無為な時間を過ごさないように共有します。
スクレイピングツールを書くときのお話
「この部分の内容を取得したい!」と思ったら、該当部分を右クリックして「要素の詳細を表示」(Safariの場合)からHTMLを眺めて、要素のattributeなどをチェックします。
そして、こうやれば取得できるかなというコマンドを、試しにconsoleに入力してみます。
document.querySelector('a[href^="/start_with"]) // => <a href="/start_with/12345">...</a>
無事に結果が返ってきました!
通常はこのような手順でうまくいったコマンドをスクリプトにまとめていくと思います。ところが……
スクリプトを実行するとnullが返ってくる
???
あれ?コピペしたのにおかしいな。このコマンドにたどり着くまでのページ遷移がおかしいのかな?いや、そもそも……という感じで1時間くらい哀しい時間を過ごしました。
iframeの中の要素だった
色々試すうちに気がつきました。あれ?取得しようとしている要素がiframeの中にあるぞ。
ページ上から要素の詳細を表示すると、対象の要素が見える状態まで展開されるのでなかなか気がつかなかったのです。
iframeの中の要素を取得する
というわけで結論です。
以下のように、iframe.contentDocument
から検索することでiframe内の要素を取得できます。
const iframe = document.querySelector('#iframe-selector')
// iframe.contentWindow.document === iframe.contentDocument
const targetElement = iframe.contentDocument.querySelector('a[href^="/start_with"]')
// XPathで取得するときは、2つめの引数にiframe.contentDocumentを渡せばOK
const targetElementByXPath = document.evaluate(
'//a[contains(@href, "/start_with")]',
iframe.contentDocument,
null,
XPathResult.FIRST_ORDERED_NODE_TYPE,
null
).singleNodeValue
どうやら、Webインスペクタやデベロッパーツールからiframe内の要素を手動で表示した時点で、iframe内の要素が、通常のdocument
内に展開されるようです。そのため、ブラウザのconsoleからは取得できてしまっていたのでした……(かなしみ)