SeleniumでHTMLの要素を特定するCSSセレクターのサンプルです。ID名やClass名で要素を取得するメソッドがありますが、WEBページのデザイン変更時の変更が厄介です。コード量の増加は、不具合が入り込む可能性が高くなるからです。そこで、できるだけCSSセレクターで制御して備えようという主旨のサンプルです。
スクレイピングにおいて、HTMLの要素特定は厳密した方が、WEBページのデザイン変更をいち早く知ることができます。要素特定は、XPathやPyQueryが使われますが、XPathは複雑になる傾向があるため、CSSセレクターがしばしば推奨されます。WEBデザイナーが使うため情報量が豊富で、GoogleChromeの機能で要素を特定しやすいのがメリットです。
CSSセレクターによる要素取得のサンプル
下の「~」に設定する文字列で、欲しい要素を特定します。
driver = webdriver.Chrome() driver.get('http://localhost/index.html') time.sleep(3) tags = driver.find_elements_by_css_selector("~")
要素特定の基本
要素が持つ情報から特定します。実践では、後述する方法と組み合わせて利用することになります。
要素全部を取得
<div><p>0123<p><p>4567<p></div>
tags = driver.find_elements_by_css_selector("*")
タグ名で取得
<div><p>0123<p><p>4567<p></div>
タグpだけを取得したい場合
tags = driver.find_elements_by_css_selector("p")
ID名で取得
<div id="num-area"><p class="num-a">0123<p><p>4567<p></div>
tags = driver.find_elements_by_css_selector("#num-area") tags = driver.find_elements_by_css_selector("div#num-area")
class名で取得
<div><p class="num-a">0123<p><p>4567<p></div>
tags = driver.find_elements_by_css_selector(".num-a") tags = driver.find_elements_by_css_selector("p.num-a")
属性で取得
属性を持っている要素が得られます。
<a href="https://www.google.com/?hl=ja" title="">Google</a>
tags = driver.find_elements_by_css_selector('a[title]')
属性の値が完全一致していたら得られます。
<input id="name" type="text" />
tags = driver.find_elements_by_css_selector('input[type="text"]')
属性の値が前方一致していたら得られます
<a href="https://www.google.com/?hl=ja">Google</a>
tags = driver.find_elements_by_css_selector('a[href^="https://"]')
属性の値が後方一致していたら得られます。
<img src="https://s.yimg.jp/c/logo/f/2.0/news_r_34_2x.png" />
tags = driver.find_elements_by_css_selector('img[src$=".png"]')
属性の値が部分一致していたら得られます
<a href="https://www.google.com/?hl=en">Google</a>
tags = driver.find_elements_by_css_selector('a[href*="hl=e"]')
要素の関係性から特定
要素同士のつながりを指定することで、欲しい情報が特定できます。
要素Aの子孫の要素Bを取得
<div>
<span>0123</span>
<span>4567</span>
<p>
<span>89</span>
</p>
</div>
tags = driver.find_elements_by_css_selector("div span") # len(tags) == 3
要素Aの子の要素Bを取得
<div>
<span>0123</span>
<span>4567</span>
<p>
<span>89</span>
</p>
</div>
tags = driver.find_elements_by_css_selector("div > span") # len(tags) == 2
要素Aと同じ階層の要素の内、要素Aの後のものを取得
<div>
<p>012</p>
<p class="mark">345</p>
<p>678</p>
<p>9</p>
</div>
tags = driver.find_elements_by_css_selector("p.mark ~ p") # len(tags) == 2 tags[0].text == 678 tags[1].text == 9
要素Aと同じ階層の直後の要素Bを取得
<div>
<p>012</p>
<p class="mark">345</p>
<p>678</p>
<p>9</p>
</div>
tags = driver.find_elements_by_css_selector("p.mark + p") # tags[0].text == 678
要素の並び順で取得
<ul>
<li>0</li><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li>
</ul>
tags = driver.find_elements_by_css_selector("li:first-child") # "0" tags = driver.find_elements_by_css_selector("li:last-child") # "5" tags = driver.find_elements_by_css_selector("li:nth-child(3)") # 2番目 "2" tags = driver.find_elements_by_css_selector("li:nth-child(odd)") # 1から数えて奇数番目 "0","2","4" tags = driver.find_elements_by_css_selector("li:nth-child(even)") # 1から数えて偶数番目 "1","3","5" tags = driver.find_elements_by_css_selector("li:not(:last-child)") # "0","1","2","3","4"
組み合わせのサンプル
出来る限り厳密に要素を特定するようにしています。下のサンプルのように、機械的には見えないidを見つけ、そこから辿れるようにしています。
tags = driver.find_elements_by_css_selector('div#products ul.product_items > li > a[href^="https://towajusoubi.com/"]')
div#productsとaタグの間に何らかの変化があれば、len(tags)が0です。更に、要となる部分の要素数を数えてもいます。
tags = driver.find_elements_by_css_selector('div#products ul.product_items *') len(tags)
ここまですれば、WEBデザインの変化をいち早く察知できます。やりすぎの感がありますが、ユーザーの印象は良いか悪いかの二つしかない(出典:手塚治虫)のです。やりすぎが丁度良いに決まっています。
コメント