【Python】スクレイピングの基礎 BeautifulSoupの基礎と使い方(find, get, text, string, select各メソッドの紹介)

Beautiful Soup(ビューティフル・スープ)とは、HTMLやXMLファイルから情報を取得し、解析するPythonのWEBスクレイピングに用いられるライブラリです。

ここでPythonにおけるWEBスクレイピングの流れを主に使用するライブラリを含めて図表に示します。

PythonにおけるWEBスクレイピングの流れ(使用するライブラリを含めて)

PythonにおけるWEBスクレイピングの流れ
静的サイト 動的サイト
データの取得 Requests Selenium
データの解析 BeautifulSoup

上記のように、Requests(リクエスツ)やSelenium(セレニウム)で対象のWEBサイトから情報を取得し、取得した情報をBeautifulSoupを用いて解析するという流れがPythonにおけるWEBスクレイピングの一般的な流れです。文章で表現すると下記のようになります。

手順①:RequestsやSeleniumでHTMLやXMLファイルを取得

手順②:BeautifulSoupで 「①」で取得したHTMLファイルやXMLファイルを解析

以降で簡単にRequests(リクエスツ)とSelenium(セレニウム)というライブラリについても解説します。

データの取得に使用するライブラリ①:Requests

Requestsの使い方は、まずrequests.get(url)メソッドによって、引数のurlに渡したURLに対してHTTPリクエスト(GETメソッド)を実行します。その返り値として取得できるHTTPレスポンス(HTMLファイルやXMLファイル)の中身をBeautifulSoupなどの解析ツールを用いて解析を行います。

データの取得に使用するライブラリ②:Selenium

Seleniumはブラウザ操作(ブラウザの立ち上げ、HTTPリクエスト、HTMLやXMLファイルの取得)を実行できます。RequestsとSeleniumの違いは、Seleniumでは動的サイト(JavaScript等によってHTMLファイルが加工されるサイト)の情報も取得が可能な点が挙げられます。一方、Requestsでは静的サイト(JavaScript等によってHTMLファイルが加工されないサイト)の情報取得に使用されます。

RequestsとSeleniumについては別記事を作成致しますのでそちらをご参照下さい。

ここからようやくBeautifulSoupの解説に入ります。

BeautifulSoupのインポート方法

BeautifulSoupはbs4というライブラリのメソッドになりますので、下記のようにまずbs4をインストールした後に、bs4からBeautifulSoupをインポートします。

bs4ライブラリのインストール
pip install bs4
BeautifulSoupのインポート
from bs4 import BeautifulSoup

BeautifulSoupの使い方

例、今回は例として当ブログのトップページから<title>タグを抽出してみたいと思います。

WEBサイトからの情報取得

まずは、requests.get(url)メソッドを使ってHTMLファイルを文字列として取得します。

requests.get(url)メソッドでHTMLファイルを文字列として取得
import requests
from bs4 import BeautifulSoup

url = "https://zeroterasu.com/blog/"
res = requests.get(url)
html = res.content もしくは res.text

# 実行結果
'<!doctype html><html lang="ja"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-
...(以下省略)'

<手順>

①ライブラリのインポート。

②url変数定義。スクレイピング対象サイトのURLをurl変数に格納。

③requests.get(url)メソッドの返り値をres変数に格納。

④html変数定義。”③”で取得したHttpResponseオブジェクト(HTML)のbytes形式のデータをhtml変数に格納。(res.textの場合、HTMLをテキストデータとして取得しますが、文字化けする可能性があるので、contentを使用します。)

取得したデータの解析

上記では、HTMLファイルをテキストデータとして取得しました。しかし、テキストデータのままでは、HTML中の任意のデータを抽出するのが面倒です。(正規表現等を使えば、テキストデータのままで任意の情報を取得することもできます。)

そこで、HTMLのテキストデータをDOM(Document Object Model)に変換して任意の情報を取得する方法が、BeautifulSoupを使った解析方法になります。

まずは、BeautifulSoupメソッドについて解説致します。

BeautifulSoupメソッド: - BeautifulSoup(解析対象HTML/XML, 使用するパーサー(文字列))

第一引数に、解析対象となるHTMLまたはXMLを渡します。

第二引数に、HTML/XMLの解析に使用するパーサー(解析器)を指定します。

BeautifulSoupに使用できるパーサーの種類

パーサー 使用方法 メリット デメリット
html.parser BeautifulSoup(html/xml, “html.parser”) ・デフォルトで使用できる。 ・lxmlほど高速ではない。
lxml BeautifulSoup(html/xml, “lxml”) ・非常に高速

・外部C依存

外部ライブラリのインストールが必要。

lxml-xml

BeautifulSoup(html/xml, “lxml-xml”)

BeautifulSoup(html/xml, “xml”)

・非常に高速

・外部C依存

・外部ライブラリのインストールが必要。

html5lib BeautifulSoup(html/xml, “html5lib”) ・ウェブブラウザ同様の解析が可能。 ・非常に遅い

第二引数を省略した場合、デフォルトの「html.parser」が使用されます。

lxmlはpipインストールする必要がありますが、非常に高速(C言語で作成されている様子です。)のため、lxmlで解析できるページであれば、lxmlを基本的に使用するのが良いと思います。

これらのパーサーで解析できないHTMLについては、html5libの使用を検討します。

BeautifulSoupを使ってHTMLを解析

今回は、先に述べた通り<title>タグを抽出致します。

BeautifulSoupを使ってHTMLを解析
import requests
from bs4 import BeautifulSoup

url = "https://zeroterasu.com/blog/"
res = requests.get(url)
html = res.text

soup = BeautifulSoup(html, 'html.parser')
soup.find('title')

# 実行結果
<title>ZeroTerasu Blog | あなたのアイデア、ITで実現しませんか?</title>

<手順>

①soup変数定義。BeautifulSoupオブジェクトを格納。

②BeautiflSoupオブジェクトからfind()メソッドを使って対象のデータを取得する。

BeautiflSoupオブジェクトの検索メソッド

上記の例では、find()メソッドを使って対象データを抽出しましたが、BeautiflSoupオブジェクトには全部で6つ(×2)のメソッドがありますので、解説致します。

find_all() と find()

find_all()メソッドは、BeautiflSoupオブジェクトの検索メソッドの中で最も使用頻度の高いメソッドになります。下記が引数の種類とそのデフォルトの値です。

find_all(name=None, attrs={}, recursive=True, text=None, limit=None, **kwargs)

この中で重要なのは、name(タグ名)kwargs(キーワード引数)です。

find_all()メソッドは、BeautifulSoupオブジェクト*またはタグオブジェクト**の子孫要素から一致した要素を全て取得できます。

*BeautifulSoupオブジェクト=htmlをBeautifulSoup()メソッドを使ってDOM化したオブジェクトです。下記コードの変数soupがBeautifulSoupオブジェクトです。

**タグオブジェクト=BeautiflSoupオブジェクトにfind系またはselect系メソッドを使って抽出されたタグがタグオブジェクトになります。

find_all()のポイント

nameには、”a”, “div”, “p”などのタグ名を文字列として引き渡します。

・kwargsには、タグの属性を引き渡します。
(例、id=”id名”, class_=”クラス名”, href=”パス名” *classはPythonの予約語のため”_”が必要です。)

・返り値はリストのようになっていますので、リスト内のインデックス番号を指定することでリスト内の要素を取得することが出来ます。

find_all()のその他の引数

name:タグ名を対象にして検索
attrs:タグの値を対象にして検索(class_= とすることでCSSクラス名を検索)
recursive:デフォルトでTrue、Falseで直下の子要素のみ検索(find_all()とfind()のみ使用可)
text:タグに含まれるテキストを対象に検索
limit:必要な検索結果の数を指定

import lxml

# ↓ サンプルHTMLを手入力している。
html = '''
<p id="id1" class="sample_class"></p>
<p id="id2" class="sample_class"></p>
<p id="id3" class="spcial_class"></p>
<a id="id4" class="sample_class"></p>
'''
# ↓ BeautifulSoupメソッドを使ってhtmlをBeautiflSoupオブジェクト(DOM)に変換。
soup = BeautifulSoup(html, 'lxml')

# ↓ 1. タグ名="p", キーワード引数(class_)に"sample_class"を指定してfind_all()メソッドを使って要素をリスト形式で取得。
soup.find_all("p", class_='sample_class')

# ↓ 2. キーワード引数(class_)に"sample_class"を指定してfind_all()メソッドを使って要素をリスト形式で取得。
soup.find_all(class_='sample_class')

# ↓ 3. find_all()メソッドで取得したリスト形式オブジェクトからインデックス番号を指定して特定の要素を取得。
soup.find_all(class_='sample_class')[0]

# ↓ 4. オブジェクトの種類の確認 
[type(soup), type(soup.find_all(class_="sample_class")), type(soup.find(class_="sample_class"))]

# 実行結果
# 1. [<p class="sample_class" id="id1"></p>, <p class="sample_class" id="id2"></p>]
# 2. [<p class="sample_class" id="id1"></p>,
     <p class="sample_class" id="id2"></p>,
     <a class="sample_class" id="id4">
     </a>]
# 3. <p class="sample_class" id="id1"></p>
# 4. [bs4.BeautifulSoup, bs4.element.ResultSet, bs4.element.Tag]

find_parents() と find_parent()

find_parent()系メソッドは、タグオブジェクトの親要素を取得します。

find_parents()メソッドでは、一階層、二階層、、、と上流階層をリスト形式で取得することが出来ます。

find_parent()メソッドでは、一階層上流階層の要素を取得できます。

find_parents()メソッドとfind_parent()メソッドの使用例
html = '''
<div class="class1">
    <p class="p_class1">Pタグです。</p>
    <a class="a_class1">aタグです。</a>
    <ul>
        <li id="list1" class="list_class">リスト1です。</li>
        <li id="list2" class="list_class">リスト2です。</li>
    </ul>
</div>

'''
soup = BeautifulSoup(html, "lxml")
soup.find('li', id="list2").find_parent()
# 実行結果
<ul>
<li class="list_class" id="list1">リスト1です。</li>
<li class="list_class" id="list2">リスト2です。</li>
</ul>
# 実行結果
# リストの先頭はfind_parent()メソッドと同様に一階層上の要素が取得されます。
# リストの2番目には、2階層上の要素が取得されています。
# リストの3番目には、3階層上の要素である<body>要素が取得されています。*
# リストの3番目には、4階層上の要素である<html>要素が取得されています。*
# *<body>タグ、<html>タグは自ら追加した要素ではないが、BeautiflSoup()メソッドでは自動的に付与される様子。
Output exceeds the size limit. Open the full output data in a text editor
[<ul>
 <li class="list_class" id="list1">リスト1です。</li>
 <li class="list_class" id="list2">リスト2です。</li>
 </ul>,
 <div class="class1">
 <p class="p_class1">Pタグです。</p>
 <a class="a_class1">aタグです。</a>
 <ul>
 <li class="list_class" id="list1">リスト1です。</li>
 <li class="list_class" id="list2">リスト2です。</li>
 </ul>
 </div>,
 <body><div class="class1">
 <p class="p_class1">Pタグです。</p>
 <a class="a_class1">aタグです。</a>
 <ul>
 <li class="list_class" id="list1">リスト1です。</li>
 <li class="list_class" id="list2">リスト2です。</li>
 </ul>
 </div>
 </body>,
 <html><body><div class="class1">
 <p class="p_class1">Pタグです。</p>
 <a class="a_class1">aタグです。</a>
 <ul>
...
 <li class="list_class" id="list1">リスト1です。</li>
 <li class="list_class" id="list2">リスト2です。</li>
 </ul>
 </div>
 </body></html>]

find_previous_siblings() と find_previous_sibling()

find_previous_sibling()系メソッドは前にある兄弟要素を取得するために使用します。

例、<ul>
    <li>リスト1</li>
    <li>リスト2</li>
  </ul>
  ↑この例では、<li>リスト2</li>から見ると、<li>リスト1</li>が前にある兄弟要素になります。

find_previous_siblings() と find_previous_sibling()の使用例
html = '''
<div class="class1">
    <p class="p_class1">Pタグです。</p>
    <a class="a_class1">aタグです。</a>
    <ul>
        <li id="list1" class="list_class">リスト1です。</li>
        <li id="list2" class="list_class">リスト2です。</li>
    </ul>
</div>
'''
soup = BeautifulSoup(html, "lxml")
soup.find('li', id="list2").find_previous_sibling()
# 実行結果
<li class="list_class" id="list1">リスト1です。</li>

find_all_previous() とfind_previous()

find_previous()系メソッドは、前にある要素を取得するために使用されます。

・兄弟要素の中の先頭以外の要素に対して実行する場合、HTMLの上から順で一つ前の要素から取得する。

find_all_previous() とfind_previous()の使用例
html = '''
<div class="class1">
    <p class="p_class1">Pタグです。</p>
    <a class="a_class1">aタグです。</a>
    <ul>
        <li id="list1" class="list_class">リスト1です。</li>
        <li id="list2" class="list_class">リスト2です。</li>
    </ul>
</div>
'''
soup = BeautifulSoup(html, "lxml")
soup.find('li', id="list1").find_previous()
# 実行結果
<ul>
<li class="list_class" id="list1">リスト1です。</li>
<li class="list_class" id="list2">リスト2です。</li>
</ul>

find_all_next() とfind_next()

find_next()系メソッドは、次にある要素を取得するために使用されます。

・兄弟要素の中の末尾以外の要素に対して実行する場合、HTMLの上から順で一つ前の要素から取得する。

find_all_next() とfind_next()の使用例
html = '''
<div class="class1">
    <p class="p_class1">Pタグです。</p>
    <a class="a_class1">aタグです。</a>
    <ul>
        <li id="list1" class="list_class">リスト1です。</li>
        <li id="list2" class="list_class">リスト2です。</li>
        <li id="list3" class="list_class">リスト3です。</li>
    </ul>
</div>
'''
soup = bs4(_html, "lxml")
soup.find('li', id="list1").find_all_next()[1]

# 実行結果
<li class="list_class" id="list3">リスト3です。</li>

find()メソッド と select()メソッドの使い分け

どちらのメソッドもHTML要素を取得するためのメソッドです。

違いは、引数検索条件の指定方法だけです。

find(要素名, 属性指定)

select(CSSセレクタ)

上記の通り、select()メソッドでは、CSSセレクタを引数に渡して検索条件を特定します。

CSSセレクタの扱いに慣れていらっしゃる方はselect()メソッドの方が容易いかもしれません。

タグオブジェクトの取り出し方(find()やselect()よりも簡便な取得方法)

タグオブジェクトは、要素名を単純にドットで繋ぐだけで取り出すことも可能です。

タグオブジェクトの取り出し方(find()やselect()よりも簡便な取得方法)の使用例
html = '''
<div class="class1">
    <p class="p_class1">Pタグです。</p>
    <a class="a_class1">aタグです。</a>
    <ul>
        <li id="list1" class="list_class">リスト1です。</li>
        <li id="list2" class="list_class">リスト2です。</li>
    </ul>
</div>
'''
soup = BeautifulSoup(html, "lxml")
# 実行-1
soup.html.body.div.ul.li
# 実行結果
<li class="list_class" id="list1">リスト1です。</li>

# 実行-2
soup.find('li', id="list1")) == soup.html.body.div.ul.li
# 実行結果
True
↑
find()メソッドとドット繋ぎどちらでも同じ結果が返されることがわかりました。

# 実行-3
[soup.find('li', id="list1")) == soup.html.body.div.ul.li
# 実行結果
True
↑
find()メソッドとドット繋ぎどちらでも同じタグ要素が取得できています。

文字列の取得方法(「.text」と「.string」)

「.text」:単純に要素のテキストだけを取得したい場合は「.text」を使用します。

「.string」:テキストに対して、置換等メソッドを実行するために「.string」を使用します。

属性値の取得方法(「href / src」):get(‘属性名’)メソッド

<a href=””>タグの「href属性値」(リンク先)や<img src=””>の「src属性値」(画像ソース)を取得する際は、タグで絞り込んで、get()メソッドを使って取得します。

属性値の取得方法(「href / src」)の使用例
html = '''
<body>
    <div class="sample">
        <a href="https://zeroterasu.com/page2">リンク先はこちらです。</a>
        <img src="imgages/img">
    </div>
</body>
'''
soup = BeautifulSoup(html, 'lxml')
soup.a.get('href')
# 実行結果
'https://zeroterasu.com/page2'

soup.img.get('src')
# 実行結果
'imgages/img'

コメント

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