以前、HTML内の特定のエレメント(要素)を見つけるために、ブラウザの下スクロールを試していました。
一番下までスクロールされれば、ページ全部の要素が見つかるだろうと思っていましたが、
表示されていないと見つからない要素もあった。
なので、該当の部分を飛ばして、一番下まで一気にスクロールしてしまうと、結局見つからないという自体に。
ということで、何回かに分けてスクロールさせてから、見つかったらスクロールを終わるというプログラムをApple Script と Javascriptで作ってみました。
on getXPathBySelector(args)
set {sel_:aSel, text_:aText, xpath_:aXPath, scroll_flg_:aScrollFlg} to ¬
args & {sel_:"", text_:"", xpath_:"", scroll_flg_:false}
tell application "Safari"
set cnt_max to 20
set cnt_current to 0
if aScrollFlg then
do JavaScript "window.scroll(0, 0);" in document 1
end if
repeat cnt_max times
set cnt_current to cnt_current + 1
if aScrollFlg then
do JavaScript "
var element = document.documentElement;
var bottom = element.scrollHeight - element.clientHeight;
var scrollY = 0;
scrollY = (bottom / " & cnt_max & ") * " & cnt_current & ";
window.scroll(0, scrollY);
" in document 1
end if
if aScrollFlg then
delay 0.1
end if
set val to do JavaScript "
var val = '';
if aXPath is "" then
set top_element to "var ele = document;"
else
set top_element to "
var xpaths = document.evaluate('" & aXPath & "', document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );
var ele = xpaths.snapshotItem(0);"
end if
var lists = ele.querySelectorAll(':scope " & aSel & "');
for ( var i = 0; i < lists.length; i++ ) {
var ele = lists[i];
if('" & aText & "' == ''){
val = getXpath(ele);
break;
}
if(ele.innerHTML.indexOf( '" & aText & "') > -1){
val = getXpath(ele);
break;
}
}
function getXpath(element) {
if(element && element.parentNode) {
var xpath = getXpath(element.parentNode) + '/' + element.tagName;
var s = [];
for(var i = 0; i < element.parentNode.childNodes.length; i++) {
var e = element.parentNode.childNodes[i];
if(e.tagName == element.tagName) {
s.push(e);
}
}
if(1 < s.length) {
for(var i = 0; i < s.length; i++) {
if(s[i] === element) {
xpath += '[' + (i+1) + ']';
break;
}
}
}
return xpath.toLowerCase();
} else {
return '';
}
}
res = val;
" in document 1
if val is not "" then
return val
end if
delay 0.1
end repeat
return ""
end tell
end getXPathBySelector
使い方の例:
set xpath_parent to my getXPathBySelector({sel_:"div[data-component=\"entry\"]", text_:"", scroll_flg_:true})
set xpath_grandchild to my getXPathBySelector({sel_:"a", text_:"次へ", xpath_:xpath_parent})
この場合は、全部の縦スクロール量に対して、20分割してスクロールさせている。
見つかったら、要素のXPath を返すようにしている。
探す要素のセレクタを引数にしていて、innerHTMLのテキストでも探せるようにしている。
スクロールして探すか、しないで探すかを設定できる。
親のXPathで範囲を指定できる。
ちなみに、querySelectorAll で、基準となる要素の子孫を取得する時は、「:scope」を付けるとよいらしい。