【Python】静的なWebページを『Beautiful Soup』でスクレイピングする

ログインが不要なWebサイトをスクレイピングしたい場合は、Pythonのサードパーティ製のモジュール『Beautiful Soup』を使うと簡単です。

ターミナル
pip install beautifulsoup4

Beautiful Soupは、Webページのソースコードの中から、お目当ての情報を『CSSセレクタ』や『その他の方法』を使って抽出するモジュールです。

そこで今回は、より実践的なBeautiful Soupの使い方を紹介します!

Beautiful Soupの使い方

Beautiful Soupは、HTMLやXMLから狙ったデータを抽出するためのモジュールです。

つまり、予めHTMLやXMLのソースコードを何らかの手段で用意しなければいけません。

そこで、Pythonのサードパーティ製のモジュールのrequestsを使用して、WebページのURLからソースコードを取得します。

ターミナル
pip install requests
MEMO
専門用語を使って説明すると、HTMLの取得をrequestsで行い、HTMLのパース処理をBeautiful Soupで行います。
Python
import requests
from bs4 import BeautifulSoup

url = '{{ スクレイピングするURL }}'
r = requests.get(url)
soup = BeautifulSoup(r.content, 'lxml') # または、'html.parser'

スクレイピングしたいWebページのソースコードが以下のような構成だったら

HTML
<!doctype html>
<html>
	<body>
		<div id="color">
			<p>色</p>
			<p class="r">あか</p>
			<p class="g">みどり</p>
			<div>
				<p class="b1 b"><a href="http://sample.com/">ぐんじょう</a></p>
				<p class="b2 b">つゆくさ</p>
				<p class="b3 b">さびあさぎ</p>
			</div>
		</div>
	</body>
</html>

Beautiful Souprequestsを組み合わせたプログラムを実行すると変数soupには、以下のようにページ全体のソースコードが格納されるようになります。

Python
print(soup)
# <!DOCTYPE doctype html>
# 
# <html>
# <body>
# <div id="color">
# <p>色</p>
# <p class="r">あか</p>
# <p class="g">みどり</p>
# <div>
# <p class="b1 b"><a href="http://sample.com/">ぐんじょう</a></p>
# <p class="b2 b">つゆくさ</p>
# <p class="b3 b">さびあさぎ</p>
# </div>
# </div>
# </body>
# </html>

要素を取得する

Beautiful Soupのチュートリアルや色んなサンプルを見てると、この記事では紹介していないfind_xxx系のメソッドがよく使われていますが、私の経験上ではCSSセレクタを使って抽出する場面がほとんどです。

CSSセレクタを使って要素を取得したい場合、複数の要素を取得する場合はselectメソッド、1つだけ取得する場合はselect_oneメソッドを使用します。

soup.select('CSSセレクタ', limit=None) # 戻り値:複数(リスト)
soup.select_one('CSSセレクタ')         # 戻り値:1つ(文字列)
soup.select('.b')
# [<p class="b1 b"><a href="http://sample.com/">ぐんじょう</a></p>, <p class="b2 b">つゆくさ</p>, <p class="b3 b">さびあさぎ</p>]
# type:list

soup.select_one('.b')
# <p class="b1 b"><a href="http://sample.com/">ぐんじょう</a></p>
# type:str

テキストを取得する

要素が取得できたら、そのままget_textメソッドを使用してテキストを取得することができます。

Python
soup.select_one('.b1').get_text()
# ぐんじょう

ただし、get_textメソッドは、listに対して使うことが出来ません。

Python
soup.select('.b').get_text()
# AttributeError: 'list' object has no attribute 'get_text'

リストからまとめてテキストを取得したい場合は、リスト内包表記を使用します。

Python
[i.get_text() for i in soup.select('.b')]
# ['ぐんじょう', 'つゆくさ', 'さびあさぎ']

属性の値を取得する

テキストの場合と同様に、要素が取得できたら、そのままgetメソッドを使用して属性の値を取得することができます。getメソッドには、属性名を指定します。

Python
soup.select_one('.b a').get('href')
# http://sample.com/

getメソッドもget_textメソッド同様に、listに対して使うことが出来ません。

Python
soup.select('.b').get('class')
# AttributeError: 'list' object has no attribute 'get'

リストからまとめて属性値を取得したい場合は、リスト内包表記を使用します。

Python
[i.get('href') for i in soup.select('.b a')]
# ['http://sample.com/']

[i.get('class') for i in soup.select('.b')]
# [['b1', 'b'], ['b2', 'b'], ['b3', 'b']]

[i.get('class') for i in soup.select('p')]
# [None, ['r'], ['g'], ['b1', 'b'], ['b2', 'b'], ['b3', 'b']]

実践編

この章では、実際のwebページからBeautiful Soupを使用して、スクレイピングしてみます。

http://pgl-db.net/season-pokemon/?battle=1

例として、ポケモンの対戦でよく使われている上位30位までスクレイピングしてみます。

STEP.1
Google ChromeでURLを開く

STEP.2
ディベロッパーツールを起動する

+shift+iでGoogle Chromeのディベロッパーツールを起動します。

ディベロッパーツールの使い方は割愛します。

STEP.3
HTMLの構造を確認し、CSSセレクタを求める
HTML
<div>
	<div>
		<table>
			<tbody>
				<tr>
					<th>1</th>
					<td><img src=""></td>
					<td><a href="">ミミッキュ</a></td>
				</tr>
				︙省略
				<tr>
					<th>10</th>
					<td><img src=""></td>
					<td><a href="">メタグロス</a></td>
				</tr>
			</tbody>
		</table>
	</div>
	<div>
		<table>
			<tbody>
				<tr>
					<th>11</th>
					<td><img src=""></td>
					<td><a href="">カプ・テテフ</a></td>
				</tr>
				︙省略
				<tr>
					<th>20</th>
					<td><img src=""></td>
					<td><a href="">カビゴン</a></td>
				</tr>
			</tbody>
		</table>
	</div>
	︙省略
</div>

ざっーとHTMLのソースコードを確認します。

今回スクレイピングするデータは、table要素のtr要素の3番目のtd要素であることが分かりますね。

つまり、求めたいCSSセレクタは、table tr td:nth-child(3)になります。

STEP.4
プログラムを書く
Python
import requests
from bs4 import BeautifulSoup

url = 'http://pgl-db.net/season-pokemon/?battle=1'
r = requests.get(url)
soup = BeautifulSoup(r.content, 'lxml') # または、'html.parser'

print([i.get_text() for i in soup.select('table tr td:nth-child(3)', limit=20)])
# ['ミミッキュ', 'ランドロス れいじゅうフォルム', 'ゲッコウガ', 'ボーマンダ', 'リザードン', 'カプ・レヒレ', 'ギルガルド', 'カバルドン', 'カプ・コケコ', 'メタグロス', 'カプ・テテフ', 'ギャラドス', 'ゲンガー', 'ポリゴン2', 'ボルトロス れいじゅうフォルム', 'バシャーモ', 'テッカグヤ', 'ナットレイ', 'ヒードラン', 'カビゴン']

目的のデータが取れましたね(^o^)

まとめ

静的なwebページのスクレイピングは、requestsでHTMLを取得し、Beautiful SoupでHTMLをパースします。

import requests
from bs4 import BeautifulSoup

url = '{{ スクレイピングするURL }}'
r = requests.get(url)
soup = BeautifulSoup(r.content, 'lxml') # または、'html.parser'

要素は、複数の要素を取得する場合はselectメソッド、1つだけ取得する場合はselect_oneメソッドを使用し

soup.select('CSSセレクタ', limit=None) # 戻り値:複数(リスト)
soup.select_one('CSSセレクタ')         # 戻り値:1つ(文字列)

テキスト・属性の値は、それぞれget_textメソッド、getメソッドで取得します。

# テキスト
soup.select_one('CSSセレクタ').get_text()

# テキストのリスト
[i.get_text() for i in soup.select('CSSセレクタ')]
# 属性値
soup.select_one('CSSセレクタ').get('属性名')

# 属性値のリスト
[i.get('属性名') for i in soup.select('CSSセレクタ')]

連載:Pythonでクローリング/スクレイピング

《超実践的》Pythonでクローリング/スクレイピングを行うロードマップ

  1. クローリングとスクレイピングをする前の事前準備・知識など
  2. 静的なWebページを『Beautiful Soup』でスクレイピングする
  3. ログインが必要なWebサイトを『Selenium』でクローリングし、『Beautiful Soup』でスクレイピングする
  4. JavaScriptで書かれたページを『Selenium』でスクレイピングする
  5. Web APIやデータセットを使用してスクレイピングする
  6. スクレイピングで得たデータを様々な形式(pandas、BigQuery、スプレッドシート、DBなど)に変換する
  7. クローリング/スクレイピングをローカルマシンで定期実行する方法
  8. クローリング/スクレイピングをサーバーで定期実行する方法
  9. クローリング/スクレイピングを安定させるための3つの設定(待機処理・エラーの通知・処理のリトライ)
  10. クローリング/スクレイピングの次にやるべきこと

Selenium逆引きリファレンス

  1. Seleniumチートシート
  2. 【headlessモード】ブラウザを起ち上げずにSeleniumを実行する方法
  3. アラートダイアログを操作する
  4. セレクトボックスを選択する方法
  5. Basic認証を突破する方法
  6. 2段階認証(6桁のパスコード)を突破する方法
  7. reCAPTCHAを突破する方法
  8. 【target="_blank"対策】driverを別ウィンドウに切り替える方法
  9. 【display:none対策】JavaScriptを実行して隠された要素を表示させる方法
  10. 【表示読み込み対策】時間を遅延させる
  11. Beautiful Soupと組み合わせる
  12. herokuで定期実行させる手順