【Python】SeleniumのCSSセレクターの使い方

seleniumによるHTML解析を10倍速くした方法 Pythonの使い方

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デザインの変化をいち早く察知できます。やりすぎの感がありますが、ユーザーの印象は良いか悪いかの二つしかない(出典:手塚治虫)のです。やりすぎが丁度良いに決まっています。

コメント

タイトルとURLをコピーしました