【Python】スクレイピング:BeautifulSoup4によるHTML解析

時計 2021.09.23 / 時計

【Python】スクレイピング:BeautifulSoup4によるHTML解析

記事ではPythonによるスクレイピング時に利用する、BeautifulSoup4によるHTML解析について解説していきます。

Pythonは様々な用途で利用されるプログラミング言語ですが、スクレイピング時に利用されることも非常に多いです。

IT用語の確認

スクレイピングとは、プログラムを用いてWebサイトから大量のデータを集める手法です

スクレイピングではクローリングにSeleniumを用い、HTML解析にBeautifulSoup4を利用します。

ここではBeautifulSoup4によるHTML解析手法を詳しく説明していきます。

スクレイピング

インターネット上には多種多様なWebサイトがあり、非常に多くの情報がインターネット上に存在します。

それらデータを利用するために、手作業で大量のデータを集めようと思うと多くの時間を有してしまいます。そこで利用するのがスクレイピングです。

スクレイピングとはインターネット上のWebサイトから、大量のデータを集める作業をプログラムに行わせる手法です。

Pythonでスクレイピングを行う際に利用するモジュールは次のようなものがあります。

  • webbrowser:Webサイトをブラウザで開く
  • Requests:インターネットからWebページをダウンロードする
  • Beautiful Soup:HTMLの解析を行う
  • Selenium:Webブラウザを操作する

WebサイトはHTMLというプログラミング言語で書かれています。HTMLを解析することで必要な情報を抽出して利用できるようにするのがスクレイピングです。

BeautifulSoup4

BeautifulSoup4とは

BeautifulSoup4はWebサイトのHTMLから情報を抽出するためのライブラリです。
抽出したHTMLを解析して、目的のデータを取得できます。

BeautifulSoup4のモジュール名はbs4(Beautiful Soup バージョン4)です。

標準ライブラリではないため、pipコマンドでインストールする必要があります。

pip install beautifulsoup4

PythonをAnacondaでインストールしている方は、すでにインストール済みなのでpipでのインストールは不要です。

Pythonスクリプトでインポートする際はbs4と書くので注意してください。

import bs4

本記事執筆時のBeautifulSoup4のバージョンは4.8.0です。

Seleniumと組み合わせる

スクレイピングではBeautifulSoup4とSeleniumを組み合わせることが多いです。

なぜならログインが必要なWebサイトやJavaScript、Ajaxを利用しているWebサイトからHTMLを取得するには、Seleniumでブラウザ操作を行う必要があるからです。

現代では多くのWebサイトでログイン必須であったり、Ajaxでデータを読み込むWebサイトが多いです。

SeleniumだけでもHTML分析が可能ですが、BeautifulSoup4の方が高速なため、クローリングにはSelenium、HTML分析にはBeautifulSoup4を利用することをお勧めします。

Seleniumの使い方については以下記事をご参照ください。

BeautifulSoup4の利用方法

次の流れでWebページのスクレイピングを行います。

  1. ソースコード(HTML)の取得
  2. BeautifulSoupオブジェクトの生成
  3. BeautifulSoupオブジェクトから要素の検索
  4. 要素からテキストや属性を取得

ソースコード(HTML)の取得

スクレイピングには対象WebページのHTMLを取得する必要があります。
WebページのHTMLを取得する方法は次の3つあります。

  • Requestsモジュールのget()メソッドを利用
  • Seleniumモジュールのpage_sourceを利用
  • urllib.requestのurlopen()メソッドを利用

Requestsモジュール

requests.get()の引数には対象のWebページのURLを指定します。

import requests
res = requests.get(URL)

request.get()はWebサーバーへリクエストを送信し、サーバーからのレスポンスをレスポンスオブジェクトとして返します。

返ってきたレスポンスオブジェクトに対してtextを使うことでHTMLテキストを取得できます。

res.text

Seleniumモジュール

WebDriverオブジェクトに対してpage_sourceを利用することで、HTMLを取得できます。

driver.page_source

urllib.request

urllib.requestのurlopen()を使ってWebページのHTMLコンテンツを取得できます。urlopenを利用する場合は以下のようにインポートします。

from urllib.request import urlopen

urlopen()の引数には対象のURLを指定します。urlopenで返されたファイルオブジェクトにはread()メソッドを呼び出してHTMLコンテンツを取得します。

BeautifulSoupオブジェクトの生成

取得したHTMLテキストをbs4.BeautifulSoup()関数に渡すことでBeautifulSoupオブジェクトが返ってきます。

構文

BeautifulSoup(HTMLテキスト, パーサー)

オブジェクトを作成するためのパーサーには以下のパーサーが指定できます。

パーサー名 説明
html.parser 標準ライブラリに含まれる
lxml 高速なパーサー。pipでインストールが必要
html5lib pipでインストールが必要

基本的にはどのパーサーを使っても違いはないです。

私の場合

Python3に最初から組み込まれているhtml.parserを主に利用しています

パーサーのlxmlは整っていないHTMLでもパースできる利点があります。
パーサーのhtml5libはlxmlよりさらに問題のあるHTMLをパースすることができます。

タグが欠けている、規則に従っていないHTMLを解析する場合はlxmlかhtml5libを利用してみましょう。

BeautifulSoup()関数で返ってきたBeautifulSoupオブジェクトに用意されているメソッドを利用することで、HTML文書内から必要な情報だけを抽出できます。

import requests
import bs4

res = requests.get("https://office54.net/")
html_text = bs4.BeautifulSoup(res.text, 'html.parser')
print(type(html_text))
# <class 'bs4.BeautifulSoup'>

BeautifulSoupオブジェクトから要素の検索

BeautifulSoupオブジェクトに用意されているメソッドを利用することで、HTMLから要素を抽出できます。

メソッドには以下に記す「select系」と「find系」の2種類あります。
それぞれ引数に条件(CSSセレクタや要素名、属性値)を指定して、条件に合った要素を取得します。

メソッド名 説明
select() 条件に合うすべての要素を取得
select_one() 条件に合う最初の要素を取得
find_all() 条件に合うすべての要素を取得
find() 条件に合う最初の要素を取得

select()とfind_all()は、Tagオブジェクトのリストを返します。
select_one()とfind()は、Tagオブジェクトを返します。

selectとfindの違い

selectとfindの各メソッドの機能は同じです。条件に合う要素を取得できます。

これらの違いは、引数に指定する条件の指定方法です。

selectでは引数に「CSSセレクタ」を指定します。
findでは引数に「要素名, 属性キーワード」で指定します。

例えばa要素でhref属性がhttps://office54.netの要素を取得するにはそれぞれ次のように指定します。

html_text.select("a[href='https://office54.net']")
html_text.find_all("a", href="https://office54.net")

私の場合

CSSセレクタの方が慣れているので主にselect系のメソッドを利用しています。

import requests
import bs4

res = requests.get("https://office54.net/")
html_text = bs4.BeautifulSoup(res.text, 'html.parser')
href = html_text.select("a[href='/']")
print(type(href))
# 
str(href[0])
# '<a href="/"><div id="logo">OFFICE54</div></a>'

要素からテキストや属性を取得

selectまたはfindメソッドで取得したTagオブジェクトから内部テキストを取得するには、get_text()メソッドを使用します。

href[0].get_text()
# 'OFFICE54'

Tagオブジェクトにattrsを使用すると、要素の属性を辞書として返します。

href[0].attrs
# {'href': '/'}

Tagオブジェクトの属性値のみを取得する場合はget()メソッドを利用し、引数に属性名を指定します。

href[0].get('href')
# '/'

例外処理の利用

Webサイトはときにダウンしていたり、何かしらの問題が発生してアクセスできない可能性があります。そういった状態のときにHTMLを取得しようとすると HTTPエラー(404 Page Not Foundなど)が発生します。

HTTPエラーが発生した際に、プログラムを止めないために例外処理を施しておくようにしましょう。

from urllib.request import urlopen
from urllib.error import HTTPError

try:
    html = urlopen('https://office54.net')
except HTTPError as e:
    print(e)
else:
    print('成功')

urlopenはHTTPエラーが発生すると例外HTTPErrorを投げますので、例外でキャッチするようにしています。

次にHTMLが無事に取得できたとしてもBeautifulSoupで要素を抽出する際に、要素が存在していないことによるエラー(AttributeErrorが投げられる)が発生する可能性もあります。

この場合も以下のように例外処理を施すようにしましょう。

from urllib.request import urlopen
from urllib.error import HTTPError
from bs4 import BeautifulSoup

def titleGet(url):
    try:
        html = urlopen(url)
    except HTTPError as e:
        print(e)
        return None
    try:
        bs = BeautifulSoup(html.read(), 'html.parser')
        title = bs.h2
    except AttributeError as e:
        print(e)
        return None
    return title

title = titleGet('https://office54.net')
if title == None:
    print("タイトルを取得できませんでした")
else:
    print(title)

上記のように例外処理を記入することでプログラムを止めることもありませんし、全体的に読みやすいコードにすることができます。

Pythonでの例外処理について詳しくは以下記事をご参照ください。

まとめ

本記事「【Python】スクレイピング:BeautifulSoup4によるHTML解析」はいかがでしたか。

BeautifulSoup4とSeleniumを駆使して、スクレイピングプログラムを作成してみてください。