インストール
1 2 3 4 5 |
conda install beautifulsoup4 または pip install beautifulsoup4 |
インポート
1 2 |
from bs4 import BeautifulSoup import requests |
BeautifulSoupの他にRequestsをimportします。(もしインストールしていない場合は事前にpipなどでインストールしておく必要があります。)
構文
1 |
BeautifulSoup(解析対象のHTML/XML,利用するパーサー) |
BeautifulSoupで利用することができるパーサー
パーサー | 指定方法 | 特徴 |
Python's html.parser | "html.parser" | 追加ライブラリが不要 |
lxml's HTML parser | "lxml" | 高速に処理が可能 |
lxml's XML parser | "xml" | XMLに対応し、高速に処理が可能 |
html5lib | "html5lib" | 正しくHTML5を処理可能 |
実装
Requestと組み合わせます。
1 2 3 4 5 6 7 |
response = requests.get('サイトのURL') if response.status_code == 200: soup = BeautifulSoup(r.text) # HTMLの解析 soup.h2 # 最初のh2を取得する。 else: raise # printなどでログ出力でも良いです。 |
タグ取得メソッド
単数のタグを取得
1 2 3 4 5 6 |
# h2を取得 soup.h2 または soup.find('h2') |
最初のh2を取得する。どちらを使っても良いのですが、findの方がおすすめです。なぜなら全てのタグを取得したい場合の構文が「find_all()」というメソッドがあるので、一つを取得する場合はfindを使った方がわかりやすい(可読性が高い)ためです。
テキストを取得
1 2 3 4 5 |
soup.h2.text または soup.h2.get_text() |
h2のテキスト。これはget_text()ではなくtextを使った方が良いでしょう。理由は以下です。
- .textの方が簡潔に書けるため。
- Pythonの辞書型のget()が値が返ってこない場合はNoneが帰るのに、get_text()だとエラーになり紛らわしいため。
複数のタグを取得
1 |
soup.find_all('h2') |
一つ目のタグを取得
1 |
soup.find_all('h2')[0] |
これは「soup.find('h2')」と同じになります。
複数のタグを同時の検索
1 |
soup.find_all(['h2', 'h3']) |
複数のタグの中から、テキストを取得
1 2 |
for h2_tag in soup.find_all('h2'): print(h2_tag.text) |
find_allの結果はlist形式で値が返ってくるので、上記のようにfor文を使えます。また、enumerateなどの構文も使えます。
1 |
h2_text_list = [tag.text for tag in soup.find_all('h2')] |
リスト内包表記を使って上記のようにh2のテキストのリストを作ったりすることもできます。
特定のclassを持つタグを取得
タグ指定だと件数が多すぎるケースが多いので通常はclass指定の方が良いです。
1 |
soup.find_all('span', class_='クラス名') |
Pythonの文法のclassと区別するためにBeautifulSoupではclassの末尾にアンダースコアをつけるようです。
1 |
soup.find_all('span', {'class': 'クラス名'}) |
辞書で指定することも可能です。
複数のclassを検索
1 |
soup.find_all('span', class_=['クラス名1', 'クラス名2']) |
検索範囲を狭くしてからタグを取得する手法
検索範囲を狭める。
1 |
soup.find('article').find_all(['h2', 'h3']) |
記事の全HTMLから毎回全てのタグを取得してくるという形では非常に大量のタグが毎回ヒットしてしまいます。
そこで、findでまずarticleというタグ構造を取得してその中から必要なタグを取得します。
記事のボリュームの多いサイトであれば使えるので非常によく使うテクニックなので是非覚えておきましょう。
該当する要素を削除する。
1 2 |
soup.find('h2', class_='クラス名').extract() soup.find_all('h2'') |
指定したclass名の要素が消えます。
どちらを選べば良いのか?
「検索範囲を狭くする方法」を選びましょう。「該当する要素を削除する」のは破壊的な方法になってしまうためです。仮に消してしまった情報の中から別に欲しい情報があった場合にもう一度Requestsを使ってリクエストし直す必要があるのでリクエスト数が増えてしまいお相手のサーバーに余計に負荷をかけることになってしまいます。
XPathを使う。
lxmlというパッケージを使う必要があります。
1 |
pip install lxml |
実装
例えば、h2のhref属性の値を取得するサンプルとしては以下のようになります。
1 2 3 4 |
soup = BeautifulSoup(response.text, features="lxml") lxml_coverted_data = html.fromstring(str(soup)) data = lxml_coverted_data.xpath("//h2/a/@href") print(data) |
テーブルから情報を抽出する例
各情報を辞書に登録してそれをlistに格納していきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
list = [] table = soup.find('table', class_='tableのclass名') tr_tags = table.find_all('tr', class_='trのclass名') for tr_tag in tr_tags: # 欲しい情報だけ抜き出す。 madori, yatin, kyori = tr_tag.find_all('td')[2:5] # 取得したすべての情報を辞書に格納する d = { 'madori': madori.text, # 中身の情報だけ取得する。 'yatin': yatin.text, # 中身の情報だけ取得する。 'kyori':kyori.text, # 中身の情報だけ取得する。 } # 取得した辞書をd_listに格納する list.append(d) |
なぜ[{辞書1},{辞書2}]のようなデータ構造にしているかといえば、今後データを何らかの表形式で保存していくことになるかと思いますが、その際に処理が非常に簡単になるためです。
この記事へのコメントはありません。