連載
» 2019年10月18日 05時00分 公開

Python入門:[Python入門]Beautiful Soup 4によるスクレイピングの基礎 (1/2)

Beautiful Soup 4を使って、urllib.request.urlopen関数などで取得したHTMLファイルから情報を抜き出す基本的な方法を見てみよう。

[かわさきしんじ,Deep Insider編集部]
「Python入門」のインデックス

連載目次

 前回は、urllib.requestモジュールを利用して、Webからファイルを取得する方法の基本を見た。今回は、このモジュールを使って得たHTMLファイルからBeautiful Soup 4というライブラリを使って必要なデータを抜き出す方法を見てみよう。

スクレイピングとは

 スクレイピング(scraping)とは、Webサイトに表示されたHTMLページ(あるいはXMLなど、その他のリソース)から自分が必要とする情報を抽出する(抜き出す)ことだ。特に近年では、機械学習などで大量のデータを取得する必要が出てきていて、それを手作業で行うことは現実的ではないことから、プログラムを使ってそれを自動化(半自動化)することが多くなっている。

 Pythonにもスクレイピングを行うためのフレームワークやライブラリといったものが幾つもある。今回はそれらの中でBeautiful Soup 4というライブラリを使って、スクレイピングの基本事項を眺めてみよう。

Beautiful Soup 4

 Beautiful Soupは今いったような「HTMLファイルやXMLファイルからデータを抽出するためのPythonライブラリ」だ。本稿執筆時点(2019年10月16日)の最新バージョンは4.8.1であり、これはPython 2とPython 3の両者で動作する。この他にBeautiful Soup 3もあるが、これはPython 3には対応しておらず、また、Python 2のサポート終了とともに終了することから、本稿ではBeautiful Soup 4を使用することにする。

 簡単な使い方を紹介しておくと、「urllib.request.urlopen関数でWebからHTMLファイルを取得して、それをBeautiful Soup 4に渡すと、そのHTMLファイルをツリー構造で表現したオブジェクトが得られるので、そのオブジェクトに対して検索するなどして、必要な情報を抽出していく」ことになる。

 ただし、本連載で使用しているJupyter Notebook環境ではbs4モジュールは標準では利用できないので、使用する前にインストールする必要がある。これには、セルで「!pip install モジュール名」を実行する。Beautiful Soup 4のモジュール名は「bs4」なので、ここでは「!pip install bs4」となる。なお、Beautiful Soup 4で実際のスクレイピングに使うクラスはBeautifulSoupとしてbs4モジュールで定義されている。

!pip install bs4

Jupyter Notebook環境にbs4モジュールをインストールする

 これを実行すると次のようになる。

bs4モジュールをインストールしたところ bs4モジュールをインストールしたところ

 これでBeautiful Soup 4を利用する準備ができたので、本フォーラムの新着記事を抜き出しながら、その基本的な利用法を見ていこう。

Deep Insiderの新着記事を抽出する

 先ほども述べたように、Beautiful Soup 4を使うには、その前に目的のWebページが必要になる。これには前回で紹介したurllib.requestモジュールを使える。基本構造は次のようになる。

from urllib import request  # urllib.requestモジュールをインポート
from bs4 import BeautifulSoup  # BeautifulSoupクラスをインポート

url = '……'
response = request.urlopen(url)
soup = BeautifulSoup(response)
response.close()

# 得られたsoupオブジェクトを操作していく

urllib.request.urlopen関数でファイルを取得し、それを使ってBeautifulSoupオブジェクトを作成

 このように書くと、responseオブジェクトに対してreadメソッドを呼び出して、Webページの内容を読み込んでデコードするといった処理までを、BeautifulSoupインスタンス生成時に自動的に行ってくれる。ただし、これではうまくいかない場合もある(それについては後で見てみよう)。

 出来上がったBeautifulSoupクラスのインスタンスは、ソースとなったWebページの構造をツリー状に表現したもので、Webページを構成するタグに「.」でアクセスしたり、特定のタグを検索したりできる。

 ここでは、上の変数urlにDeep InsiderのトップページのURL「https://www.atmarkit.co.jp/ait/subtop/di/」を代入して、BeautifulSoupオブジェクトを作成してみよう。

from bs4 import BeautifulSoup
from urllib import request

url = 'https://www.atmarkit.co.jp/ait/subtop/di/'
response = request.urlopen(url)
soup = BeautifulSoup(response)
response.close()

print(soup)

Deep Insiderのトップページを基にBeautifulSoupオブジェクトを作成

 このコードを実行すると次のようになる。

実行結果 実行結果

 本フォーラムのトップページのHTMLが表示されているのが分かる。BeautifulSoupオブジェクトは既に述べたようにツリー構造のオブジェクトで「.」を使って、そのタグにアクセスできる。例えば、titleタグには次のようにアクセスできる(soup.titleとsoup.head.titleは実際には同じオブジェクト。興味のある方はis演算子で確認してみよう)。

print(soup.title)
print(soup.head.title)

titleタグにアクセス

 これを実行すると、次のようになる。

実行結果 実行結果

 タグのテキストはtext属性で得られる。例えば、titleタグのテキストであれば「title.text」とすればよい。

print(soup.title.text)

titleタグのテキストを取得

 実行結果を以下に示す。

実行結果 実行結果

 ツリー状のオブジェクトを移動するための属性(next_siblingやnext_elementなど)もあるが、これらについては解説を省略する。

 作成したsoupオブジェクトにはfindメソッドやfind_allメソッドで、その内容を検索できる。これを使って、新着記事を一覧してみよう。

 元のHTMLファイルの内容を詳しく説明することはやめておくが、本フォーラムでは新着記事が「TOP STORIES」という枠の中に表示されるようになっている。

新着記事は[TOP STORIES]枠に表示される 新着記事は[TOP STORIES]枠に表示される

 そして、その部分に該当するソースコードは次のようになっている。

該当箇所のHTMLソース 該当箇所のHTMLソース

 要するに、class属性に「colBoxTopstories」を持つdivタグを見つければよい(その内部にclass属性を「colBoxIndex」とするdivタグが複数あり、それぞれに新着記事が1つずつ記述されている)。実際に試してみよう。BeautifulSoupオブジェクトでタグを検索するには、findメソッドやfind_allメソッドを使える。基本的には次のようにして呼び出せばよい(詳細についてはBeautiful Soupのドキュメント「find()」を参照されたい)。

soup.find(タグ名, 属性=)
soup.find_all(タグ名, 属性=

findメソッドとfind_allメソッドの基本構文

 「タグ名」には「div」「p」などのタグを文字列として指定する。その次の「属性=値」に「id=……」「class_=……」のように属性とその値を指定すると、指定したタグのうち、指定した属性が指定した値を持つものだけが得られる。このときclass属性に特定の値を持つものだけを検索したいときには「class=……」ではなく「class_=……」とアンダースコアを付加することに注意しよう(Pythonでは「class」がクラス定義に使われているため)。

 findメソッドとfind_allメソッドの違いは、前者は最初に一致したものだけが戻り値となるのに対して、後者は指定した範囲(ここの例であればsoup.find_all(……)とすることで、Webページ全体がその範囲となる)で条件に合致するもの全てを要素とするリストが戻り値となる。

 では、findメソッドでclass属性が「colBoxTopstories」であるdivタグを検索してみよう。

topstories = soup.find('div', class_='colBoxTopstories')
print(topstories)

class属性が「colBoxTopstories」のdivタグを検索する

 これを実行すると、次のようになる。

実行結果 実行結果

 ちゃんと該当箇所が得られているようだ。ここからは変数topstoriesに取得した部分を対象に検索をしていこう。既に述べたように、この部分のHTMLは大まかには次のような構造になっている。

[TOP STORIES]の構造 [TOP STORIES]の構造

 一つ一つの記事はclass属性が「colBoxIndex」であるdivタグの内部に記述されている。そして、その中にはclass属性が「colBoxSubTitle」「colBoxTitle」「colBoxDescription」となっているdivタグがあり、そこに連載タイトル、個々の記事のタイトル、説明が書かれている(それをaタグで囲んでいる)。

 そこで、まずはclass属性が「colBoxIndex」であるdivタグをfind_allメソッドで検索して、それらをリストに取得しよう。

colboxindexes = topstories.find_all('div', class_='colBoxIndex')
print(colboxindexes[0])

class属性が「colBoxIndex」であるdivタグを検索する

 これを実行すると次のようになる。上のコードでは、先頭要素を画面に出力するようにしている。

実行結果 実行結果

 このことから、一つ一つの記事に対応するdivタグの内容が得られたことが分かるはずだ。後は、ここから必要な要素を抜き出していくだけだ。これについてもfindメソッドで個々のdivタグを検索してもよいが、Beautiful SoupにはCSSセレクタの書式を利用して要素を取り出すselectメソッドがある。これを使うと、個々の要素を抜き出すコードは次のように書ける。ここでは記事タイトル(class属性が「colBoxTitle」)と説明(class属性が「colBoxDescription」)だけを抽出している。なお、selectメソッドの戻り値はリストになるので、ここではその先頭要素のtext属性を取り出して、必要なデータを得ている。

title = colboxindexes[0].select('div.colBoxTitle')[0].text
description = colboxindexes[0].select('div.colBoxDescription')[0].text
print(title, ':', description)

新着記事の先頭にあるもののタイトルと説明を抽出して、表示

 実行結果を以下に示す。

実行結果 実行結果

 このように1つの記事のタイトルと説明が得られた。後はこれと同じ処理を変数colboxindexesの各要素に対して行えばよい。

top_articles = []
for item in colboxindexes:
    title = item.select('div.colBoxTitle')[0].text
    description = item.select('div.colBoxDescription')[0].text
    top_articles.append(f'{title}: {description}')

for articles in top_articles:
    print(articles)

全ての新着記事についてそのタイトルと説明を得て、文字列のリストとするコード

 実行結果を以下に示す。

実行結果 実行結果

 これで記事の抽出ができた。

 なお、上のコードではclass属性が「colBoxIndex」であるdivタグを検索して、それに対してselectメソッドを使うことで記事を抽出したが、変数topstoriesに対して、find_allメソッドでclass属性「colBoxTitle」と「colBoxDescription」を検索し、それらをzip関数でまとめてしまうといったやり方も考えられる。説明はしないがコード例を掲載しておこう。

titles = topstories.find_all('div', class_='colBoxTitle')
descriptions = topstories.find_all('div', class_='colBoxDescription')

title_and_descs = zip(titles, descriptions)
top_articles = [f'{item[0].text}: {item[1].text}' for item in title_and_descs]

for articles in top_articles:
    print(articles)

取得した新着記事群からclass属性が「colBoxTitle」「colBoxDescription」であるdivタグのリストを検索して、そこから新着記事一覧を作成

 ここではリスト内包表記を使って短いコードとしている(先の例もリスト内包表記で記述できる構造になっているので、興味のある方は書き換えてみよう)。

       1|2 次のページへ

Copyright© Digital Advantage Corp. All Rights Reserved.

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。