Pythonでスクレイピングを実践する

プログラミング
Pythonの活用方法のひとつにスクレイピングがあります。 スクレイピングとは、WEB上のページにアクセスし、必要な情報を抽出する行為です。 人間が1つひとつ手作業で情報を集めるのに比べ、プログラムを利用した情報の抽出は実装さえ済めば圧倒的に早い速度で多くの情報を集めることが出来ます。 今回は、Pythonでスクレイピングを行う方法について実際のコードと共に解説していきます。
Pythonでスクレイピングを実践する

スクレイピングという言葉をご存知でしょうか。
スクレイピングとは、WEB上の情報にプログラムからアクセスし、必要な情報を自動取得する行為です。プログラムでこれを行うことが出来るようになると、極めて効率よく大量の情報を収集することが可能になります。

今回はPythonでスクレイピングを行う方法について解説していきます。

Pythonでスクレイピングをするには

Pythonを学習中の方の中には、初めからスクレイピングを目的として学んでいる方もいるかと思います。
そうではない方も含め、スクレイピングを出来るようになるとPythonでHTTP通信をする方法などPythonを扱う総合的な能力を獲得できるので、本記事でスクレイピングについての理解を深めていきましょう。

スクレイピングとは

スクレイピングとは、WEB上の情報を取得・整形・抽出する行為を示します。
スクレイピング(Scraping)という単語そのものは【こすり落とす】や【削り落とす】といった意味を持っています。
これはWEB上の情報を取得し、その中から必要な情報を抽出するために余分な情報をこすり落とすことを例えているのでしょう。

Pythonでスクレイピングをする方法

スクレイピングをするために必要なもっとも基本的な機能は、【HTTP通信を行う機能】です。WEB上の情報と通信を行う機能がなければ、スクレイピングを行うことは出来ません。PythonにはHTTP通信を簡単に行うことが出来るモジュールが用意されています。

また、HTTP通信で取得した情報を整形・抽出するための方法もPythonには豊富に用意されており、比較的簡単に狙った情報を取得することが出来ます。

言語によってはHTTP通信で取得した情報を綺麗に整形・抽出する機能が豊富ではないために、正規表現による抽出等、様々な工夫が必要となる場合もあるのですが、Pythonに用意された機能はそういった場合と比べると極めて扱いやすい部類に入ります。

Pythonでスクレイピングをする方法を解説

スクレイピングをするには大きく分けて以下の機能が必要です。

  1. HTTP通信をする機能
  2. 通信結果を整形・抽出する機能

Pythonにはこれらの機能を提供する様々なモジュールが用意されていますが、今回はこれらの機能について前者をRequestsモジュールで、後者をbs4モジュールのBeautifulSoupオブジェクトで実現する方法について解説します。

RequestsとBeautifulSoupはPython標準のライブラリではありませんので、解説の前にこれらの導入を行いましょう。パッケージ管理ツールpipで、RequestsとBeautifulSoup4を導入します。

コマンドラインを立ち上げ、以下のpipコマンドを実行してください。

pip install requests
pip install beautifulsoup4

これでRequestsとBeautifulSoupの導入は完了です。
さっそく、スクレイピングの学習を始めていきましょう。

PythonでWEBページを取得する

Requestsモジュールを使用することで、PythonでHTTP通信を行い、WEBページを取得することが出来ます。
Requestsモジュールは人が使いやすいよう設計されていることを特徴としており、Requestsモジュールの日本語版公式ドキュメントにおいてもこのモジュールを【人間のためのHTTP】と紹介しているほどです。

RequestsモジュールでHTTP通信をする実際のサンプルコードは以下の通りです。

import requests

url1 = 'https://www.google.co.jp/'
res1 = requests.get(url1)
print(res1) # 出力:<Response [200]>
print(res1.text) # 出力:<!doctype html><html itemscope="" ...(略)

上記コードは次のような処理を行っています。

  1. URLの文字列を作成
  2. requests.get()メソッドでHTTP通信を行う(第一引数にURLを指定)
  3. HTTP通信の結果のステータスコードを出力(requests.get()メソッドの返却値をそのまま出力)
  4. HTTP通信の結果の本文を出力(requests.get()メソッドの返却値のtextプロパティに本文が格納されている)

このように、requests.get()メソッドを使用することで簡単にHTTP通信を行うことが出来ます。
requests.get()メソッドが返却する値はrequests.models.Response型のオブジェクトであり、このオブジェクトには本文が格納されているtext属性や、通信したURLが格納されているurl属性などが存在します。

またrequests.get()メソッドが行うHTTP通信はそのメソッド名のとおり、GETで行われています。これに対し、POSTでHTTP通信を行うrequests.post()メソッドも用意されています。

前述のコードでは単に引数で指定したURLと通信を行うのみでしたが、request.get()メソッドはURLパラメータを付与して通信を行うことも可能です。

URLパラメータを付与したHTTP通信は、以下のコードで行うことが出来ます。

url2 = 'https://www.google.co.jp/search'
p = {'q': 'cat'}
res2 = requests.get(url2, params=p)
print(res2.url) # 出力:https://www.google.co.jp/search?q=cat
print(res2.text) # 出力:<!doctype html><html itemscope="" ...(略)

上記コードのように、requests.get()メソッドはキーワード引数paramsに付与したいパラメータを辞書オブジェクトで指定することが出来ます。

辞書オブジェクトのキーがそのままURLパラメータのパラメータ名になり、辞書オブジェクトの値がパラメータの値になります。ここではGoogle検索のURLにqパラム(検索キーワード)として”cat”を指定して通信を行っています。
通信結果のURL属性を確認すると、指定したパラメータが正しくURLに付与されていることが確認できます。

上記コードでは付与しているパラメータはqパラムのみですが、キーワード引数paramsに指定する辞書オブジェクトのキーと値のペアを複数用意することで、複数のパラメータを一度に付与することも可能です。

Beautiful Soupで特定のタグを抽出する

Requestsモジュールを利用することで、HTTP通信を行うことが出来ました。スクレイピングをする上でのWEBページとのやりとりは、これで可能となります。
次はbs4モジュールのBeautiful Soupオブジェクトを利用して、取得したWEBページの情報を整形し、狙った情報を抽出する方法を解説します。

Beautiful SoupはPythonにおける有力なHTMLの整形・抽出ライブラリのひとつであり、2019年3月現在はバージョン4の使用が推奨されています。Python3ではバージョン3のBeautiful Soupは動作しない為、バージョン4のBeautiful Soupを使用しましょう。

Beautiful SoupにはHTML等の構文解析について様々な機能が用意されています。一度にこの記事内で一度に全ての機能を紹介することは出来ませんが、そのうちの基本的な機能の使い方について、以下のコードで紹介していきます。

まず、HTTP通信で取得したHTMLのソースからBeautiful Soupオブジェクトを作成します。

import requests
from bs4 import BeautifulSoup

url1 = 'https://www.google.co.jp/'
res1 = requests.get(url1)
html_text = res1.text

# ________BeautifulSoupオブジェクト________
soup = BeautifulSoup(html_text)

これで、HTTP通信で取得したHTMLの本文を保持するBeautiful Soupオブジェクトが作成されました。このBeautiful Soupオブジェクトのprettify()メソッドを実行することで、HTMLを1行1タグに整形します。

# ________HTMLを1行1タグに整形されたUnicode文字列________
print(soup.prettify())
# 実行結果:
#<!DOCTYPE doctype html>
#<html itemscope="" itemtype="http://schema.org/WebPage" lang="ja">
# <head>
#  <meta content="世界中のあらゆる情報を検索するためのツールを提供しています。さまざまな検索...(略)

これだけで、改行等がないまま羅列されていたHTTP通信の取得結果が、人間が読みやすい形で整形された状態で出力できるようになりました。

次に、Beautiful Soupオブジェクトから通信で取得したHTMLから様々な要素を取得する方法を紹介します。

まず、HTMLのタイトルを取得する方法の紹介です。タイトルはBeautiful Soupオブジェクトのtitle属性に格納されているので、次のコードのみで確認することができます。

# ________タイトル取得________
print(soup.title)
# 実行結果:<title>Google</title>

print(soup.title.name)
# 実行結果:title

print(soup.title.string)
# 実行結果:Google

このように、title属性で【titleタグを含めたHTMLのタイトル】を取得できます。また、title.name属性でタグの名前を、title.string属性でタグで指定された実際のタイトルを取得可能です。

次に、取得したHTML内の指定のタグを全取得するコードを紹介します。 指定のタグを全取得するには、BeautifulSoup.find_all()メソッドを使用します。

# ________aタグ全取得________
print(soup.find_all('a'))
# 実行結果:[<a class="gb1" href="https://www.google.co.jp/imghp?hl=ja&amp;tab=wi">画像</a>, <a class="gb1" ...(略)

このように、BeautifulSoup.find_all()メソッドの引数にタグ名を指定することで、そのタグ1つひとつを配列の要素として取得することが出来ます。

BeautifulSoup.find_all()メソッドはこの他にもHTML要素の絞り込みが可能です。例として、aタグかつclass=”gb1”の要素を全取得するコードを紹介します。

# ________aタグかつ class="gb1"の要素全取得________
print(soup.find_all('a', class_='gb1'))
# 実行結果:[<a class="gb1" href="https://www.google.co.jp/imghp?hl=ja&amp;tab=wi">画像</a>, <a class="gb1"...(略)

このように、キーワード引数class_を指定することで、タグの抽出の他に、クラス名による絞り込みを行うことが可能です。この【クラス名による絞り込み】ですが、キーワード引数class_を利用する方法以外にも、次のような方法で同様の処理を行うことも可能です。

# ________辞書オブジェクトで要素の属性を指定することもできる________
print(soup.find_all('a', attrs={'class': 'gb4'}))
# 実行結果:[<a class="gb4" href="http://www.google.co.jp/history/optout?hl=ja">ウェブ履歴</a>, <a class="gb4"...(略)>

このように、キーワード引数attrsに辞書オブジェクトを指定することで、辞書オブジェクトで指定した属性と値のペアによる検索結果の絞り込みを行うことが可能です。
ここではclassによる絞り込みのみを行っていますが、同じ辞書オブジェクト内にid等の絞り込みを追加することも可能です。

さらに、上記コードではBeautifulSoup.find_all()メソッドによる絞り込みを行いましたが、この他にもHTML要素の抽出を行うメソッドが用意されています。
BeautifulSoup.select()メソッドを使用することで、他言語ではありますがJavaScriptの代表的なライブラリ【jQuery】等で頻繁に利用される【CSSセレクタ】によるHTML要素の絞り込みを行うことが可能です。

CSSセレクタでHTMLの要素を指定する基本的な記法は次の通りです。

  • タグ:”a” (aタグ)
  • class:”.foo”(クラス名 foo)
  • id:”#hoo”(ID名 hoo)

この他にもCSSセレクタでは様々な記法が存在するため、興味のある方はぜひ調べてみてください。

BeautifulSoup.select()メソッドでCSSセレクタによる要素取得を行う実際のコードは次の通りです。

# ________CSSセレクタによる要素取得________
print(soup.select('.gb4'))
# 実行結果:[<a class="gb4" href="http://www.google.co.jp/history/optout?hl=ja">ウェブ履歴</a>, <a class="gb4" href="/preferences?hl=ja">設定</a>, <a class="gb4" href="https://accounts.google.com/ServiceLogin?hl=ja&amp;passive=true&amp;continue=https://www.google.co.jp/" id="gb_70" target="_top">ログイン</a>]

このように、BeautifulSoup.select()メソッドを使用することで、CSSセレクタで要素を取得することが出来ます。

また、BeautifulSoup.select()メソッドが引数に指定したCSSセレクタの条件に該当する要素を全取得するのに対し、該当する要素のうちもっとも最初に見つかったものを返すBeautifulSoup.select_one()メソッドも存在します。

print(soup.select_one('.gb4'))
# 実行結果:<a class="gb4" href="http://www.google.co.jp/history/optout?hl=ja">ウェブ履歴</a>

なお、全件取得を行うBeautifulSoup.select()に対して単要素を取得するBeautifulSoup.select_one()メソッドがあるように、先ほど紹介したBeautifulSoup.find_all()メソッドに対しても、同じ使い方で単要素を取得するBeautifulSoup.find()メソッドが存在します。状況に応じた使い分けをするとよいでしょう。

スクレイピング実践!Pythonでスクレイピングをするコードを紹介

前項までの知識があれば、もう実際にスクレイピングを始めることは可能です。
RequestsモジュールでHTTP通信を行い、bs4モジュールのBeautiful Soupオブジェクトで通信結果の解析を行うことで、必要な情報をPythonで自動取得することが可能です。
ここで、実際に簡単なスクレイピングを行うコードを実行してみましょう。

Google画像検索から画像URLを抽出するサンプルコードを紹介

Googleの画像検索からサムネイル画像のURLを100件取得し、テキストファイルとして書き出すという処理を行う処理を実装します。画像URLを100件分取得するにあたって、以下の前提知識が必要となります。

  1. Google画像検索が必要とするURLパラメータの詳細
  2. HTTP通信のみで一度にGoogle画像検索から取得できる画像件数が20件であり、それ以上を取得するにあたっては何らかの工夫が必要であること

これらの情報についてまず事前に調査を行う必要があります。こういった【狙ったページのスクレイピングを行うための前提知識】をいかに収集するかは、スクレイピングにおける肝となります。
WEBページによって、狙った情報を表示させるためのパラメータ等の条件は異なりますので、こういった調査をしっかり行わないと、どのようなコードを実装すれば目的の情報を収集できるかは分かりません。

今回の場合、事前調査によって以下のことが判明しました。

  1. Google画像検索で必要となるURLパラメータ
    q: 検索キーワード
    tbm: 検索パターンの指定
    start: 検索をスタートする開始位置

  2. HTTP通信のみで一度にGoogle画像検索から取得できる画像件数(20件)を突破する方法

    • HTTP通信のみでは突破は不可能
    • 20件以上を同時取得することは不可能だが、startパラメータに指定した位置から20件分の検索結果を取得することは可能
    • startパラメータに指定する値を変更しながらGoogle画像検索と通信を行うことで、100件分の画像URLの自動取得が可能

以上の調査さえ済めば、あとは狙ったURLへ通信を行い、目的の情報を抽出するのみでスクレイピングが可能です。結果、次のようなコードで画像URLを100件取得し、それをテキストファイルに書き込むコードを実装しました。

import requests
from bs4 import BeautifulSoup

url = 'https://www.google.co.jp/search'
path = './imgList.txt'

# 書き込みモードでファイルを開く
with open(path, mode='w') as file:

    # URLパラメータの【start】に渡す値で繰り返す(0, 20, 40, 60, 80)
    for val in range(0, 100, 20):

        # URLパラメータ作成:q: 検索キーワード, tbm: 検索パターンの指定, start: 検索をスタートする開始位置
        p = {'q': 'Python', 'tbm': 'isch', 'start': val}
        
        # HTTP通信と結果の取得・パース
        res = requests.get(url, params=p)
        soup = BeautifulSoup(res.text)
        
        # 【images_tableクラスのtableタグ内】の【imgタグ】に画像検索結果のサムネイルが入っている
        images = soup.select('table.images_table img')
        
        # 取得した画像検索結果のサムネイルのリストを、1つひとつ取り出してファイルに書き込む
        for index, img in enumerate(images):
                line = f'{val + index}. {img["src"]}'
                print(line)
                file.write(f'{line}\n')

# 実行結果:
# 0. https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRxLnZUXfHEFxQ7S4PsL_n1GFkhNcAFTPCVMeEhiFBtvnSd-ElmTb1JNNE
# 1. https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTj2FNBRcHNXhwk-DQ3XdRbtAeGlnKUzpIxmh5sJ_LMZ1j-FqQ3ex4Wh2uuYw
# 2. https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQU-qvpHl-zLk_DsV1dMqoAS9_RT2ID9Dod384ntXezHCubJzHeh3uBWIS_
# 3. https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcR9dElsmdBf85z1RravRilniERvETtdWql0OMKIur3aowwB-IwLvL3EJJM
# 4. https://...
# 5. ....
# .....
# ..
# .(略)

このように、スクレイピングにあたってはPython上のプログラムはもちろんのこと、事前の調査が大切です。
事前の調査で必要な情報の洗い出しさえ完了すれば、その通りにコードを実装することで情報の取得が可能となりますので、しっかりと事前調査を行いましょう。


この記事のキーワードに関する勉強会・イベントを探す

TECH PLAYでは、ITエンジニア向けの勉強会・イベント情報を提供しています。
興味のある方はぜひご参加ください。


おすすめのコラム