RAKUS Developers Blog | ラクス エンジニアブログ

株式会社ラクスのITエンジニアによる技術ブログです。

Webスクレイピングとは?Pythonで始めるWebスクレイピング実践・活用法

f:id:tech-rakus:20201029201027p:plain

はじめに

みなさんこんにちは。フジサワです。
みなさんも一度や二度は、インターネット上の情報を収集して分析するという作業を行ったことがあるのではないでしょうか。
調べる対象のWebサイトが数ページくらいであれば、ブラウザを開いてコピー&ペーストを繰り返すだけでもなんとかなりますが、 対象が数十、数百ともなると、途端に人力で情報収集を行うことが難しくなります。
こうした時に用いられるのが「Webスクレイピング」という手法です。
先日、とあるWebサイトから情報を集めて分析するという作業をやっていたのですが、チマチマ手作業でやろうとするも精神的に限界を迎えたので、Webスクレイピングしてうまく情報を集められないかと考えました。
ところが、簡単なWebスクレイピングであれば、さほど技術的に難易度の高いものではないのですが、調べてみると、意外とWebスクレイピングには法要件など、技術面以外で注意しなければならないポイントが多いようです。

そこで、本稿では「Webスクレイピングとは何なのか?」「Webスクレイピングで気を付けなければならないこと」などの基本事項と併せて、「Pythonを用いてWebスクレイピングを実践するにはどうすればよいのか」といったあたりを解説したいと思います。

特にエンジニア諸氏におかれては、「あまり意識せずにWebスクレイピングしていた」人も少なくないのではないでしょうか? Webスクレイピングについて初めて学ぶ方はもちろん、既に実践されている方も、本稿を読んで改めてWebスクレイピングについて整理して頂けると幸いです!

Webスクレイピングの基本事項

Webスクレイピング(Scraping)とは

Webスクレイピングとは、Webサイトに含まれる情報から必要なものを抽出する技術・行為を指すものです。
※なお、本稿では「Web」スクレイピングと正確に記載するようにしていますが、単に「スクレイピング」と呼ぶ場合も、おおよそWebスクレイピングを指していることが多いと思います。
Webスクレイピングと並んで、よく耳にする言葉に、Webクローリング(Crawling)というものがありますが、これは、あるURLを基に、そのWebページに含まれているリンクを辿りながら、情報を収集する仕組みを指します。
ちょっとややこしいですが、この両者の違いは「クローラーが集めた情報をスクレイピングする」という文章にすれば、より分かりやすいのではないでしょうか。

Webスクレイピングの活用シーン

Webスクレイピングの基本的な狙いは、膨大な情報の収集を手作業ではなく自動化することにより業務を効率化することです。
主な活用シーンとしては次のようなものが挙げられます。

Webスクレイピングの基本的な仕組み

Webスクレイピングの基本的な仕組みは次の通りです。

  1. あるWebサイトからHTMLデータを取得する
  2. HTMLデータをタグ構造に従って分解・分析(パース)する
  3. 目的となる情報を選び出し、ファイルやDBに保存する
  4. 1.~3.のプロセスを、複数のページに対して繰り返して実行する

Webスクレイピングの注意事項

Webスクレイピングはデータ収集を行う上でとても強力な手段ですが、注意しなければならない点がいくつかあります。

取得先への攻撃とみなされたり、規約違反や、著作権法違反に問われることもある

  • 取得先への攻撃

Webスクレイピングはプログラムが自動で実行するという性質上、人間には不可能な大量のリクエストをデータの取得先に送信することができてしまいます。
しかしながら、短時間に大量のリクエストを送信することは、取得先のサーバーの処理を遅延させ、場合によってはサーバーをダウンさせるなどの損失を与えてしまいます。過去に、この件で逮捕されてしまった事例などもあるので、慎重に考える必要があるでしょう。
では、どれくらいの頻度であれば許容されるのかというと、残念ながら明確な基準はありません。
Webサイトの運営者がrobots.txtを設置している場合は、クロールして良いURL/良くないURLや許容するアクセス頻度について明言されている場合がありますので、そちらも確認しましょう。

robots.txt…Webサイトの運営者が、Googleなどの検索エンジンクローラー向けに配置するファイル。通常は、対象ドメインのルートに配置されます。(例:https://xxxx.example.com/robots.txt

Webサイトによっては、明確にスクレイピングすることを禁止しているものも存在します。
スクレイピングする際には、十分に取得先のWebサイトの利用規約を確認するようにしましょう。

Webスクレイピングの対象となる情報に著作権が生じている場合、活用方法を誤ると著作権法違反となりますので注意が必要です。
なお、著作権法では、第三十条の四にて「情報解析の用に供する場合」は著作権者の承諾なく利用することができるとされています。
活用事例として挙げた「マーケテイングや研究分野におけるデータ分析」や「機械学習におけるモデル作成のための学習データ」としてスクレイピングした情報を活用することは問題ないと言われています。
※筆者は法律の専門家ではないので、この点については改めて十分に確認されることをお薦めします

取得先の変更に影響を受ける

スクレイピングの基本的な仕組みは、「HTML要素を特定のパターンに沿って抽出する」ことですので、データの取得先の仕様変更やデザイン変更によって、HTML構造が変わり、うまく抽出できなくなってしまうことがあります。
スクレイピングが上手く処理されない場合は、取得先のHTML構造に変化がないか確認し、調整をする必要があります。
そのほか、スクレイピングが過度に相手先に負荷をかけていなかったとしても、相手先の通信拒否リストに載ってしまい、スクレイピングができなくなるような場合もあります。

取得先がAPIを公開しているならそちらを活用する方が良い

ここまで述べたとおり、Webスクレイピングにはセンシティブな問題や面倒ごとがつきまといますので、データ取得先がAPIで必要な情報を公開している場合は、そちらを活用する方が安全だと思います。
本当にWebスクレイピングする必要があるのかどうかをまず考えましょう。
もちろん、APIを利用する際にも、利用規約があればしっかり確認してくださいね。

Webスクレイピングの実践方法

Webスクレイピングを実践するには

Webスクレイピングを実践するには、次のいずれかを選択することになります。

1. ベンダーのサービスやツールを利用する

ベンダーが提供しているサービスはとても種類が多く、紹介しきれないので、ここでは簡単に導入できるChrome拡張をいくつか紹介します。

  • Instant Data Scraper
    • 複数ページにまたがる一覧画面からのスクレイピングに特化した拡張で、起動すると自動で画面上の一覧要素を判定して抽出してくれます。あとは、次のページに進むボタンがどれかを指示するだけで、自動でスクレイピングしてくれます。
  • Scraper
    • とてもシンプルなスクレイピングツール。ブラウザ上の右クリックメニューから実行できるなど、現在表示しているサイトの情報を簡易的に抽出したい場合には手軽に活用できます。
  • Web Scraper
    • 複数ページに対するクローリングの設定や、動的ページへの対応など、使い方を覚えるのに少し時間が必要な半面、自由度が高く高機能なスクレイピングツールです。

2. 自分でプログラムを作成する

PythonJava,PHP,Rubyといったプログラム言語を用いて、スクレイパーを作成する方法です。
費用もかからず、自由度も高いため、簡単なスクレイピング処理であれば、慣れているエンジニアにとってはベンダーのサービスやツールを使用するよりも手軽かもしれません。
仮に、ベンダーのサービスやツールを利用する場合であっても、内部で動作しているプログラムの基本的な考え方は同じですので、ツールの機能を理解するために、スクレイパーの実装を把握しておくのも良いのではないでしょうか。

それでは、実際にPythonでWebスクレイピングをするプログラムを作成してみましょう。
なお、本稿では、PythonのバージョンはPython3系を用いて解説していますが、Python2系をお使いの方はご了承ください。

なぜPythonなのか?

Python以外の言語でも同様のことは実現できるのですが、Pythonの場合、Webスクレイピングを実現するために必要なライブラリや書籍などが豊富だからです。

Pythonでのスクレイピング実践方法

PythonでWebスクレイピングを実現する方法は様々です。
本稿では、比較的ベーシックな作り方である、Pythonライブラリの「BeautifulSoup4」を使用した実践例を解説します。

他の方法としては、「Scrapy」というWebスクレイピングに特化したフレームワークを用いる場合や、「Selenium」などのWebブラウザを動作させて実行するライブラリを用いる場合、あるいは少しWebスクレイピングの本流からは離れますが、「Pandas」を用いたデータ解析などが挙げられます。

事前準備

BeautifulSoup4のインストール

HTMLの解析を行うライブラリに、もっとも定番のライブラリと言われている「BeautifulSoup4」を使って解説します。
「BeautifulSoup4」は次のようにインストールすることができます。
なお、今回のサンプルでは、対象のWebサイトにアクセスするためのライブラリとして、こちらもPythonでは定番となっているRequestsを使用するので、併せてインストールしています。
Python実行環境の構築や、pip(Pythonのライブラリ管理ツール)の導入については本稿では触れませんのでご了承ください。

$ pip install beautifulsoup4 requests

模擬Webサイトの構築

それでは、スクレイパーを実装するにあたり、模擬的なデータ取得元のWebサイトを構築しましょう。 次のファイルを、Pythonを実行するディレクトリと同じ階層に配置してください。

  • index.html
<html>
    <head><meta charset="utf-8"/><title>フルーツショップ</title></head>
    <body>
        <h1>商品一覧</h1>
        <ul class="fruits_list">
            <li><a href="http://localhost:8000/apple.html">りんご</a></li>
            <li><a href="http://localhost:8000/grape.html" class="new_item">ぶどう</a><span style="color:red">新商品!</span></li>
            <li><a href="http://localhost:8000/banana.html">バナナ</a></li>
        </ul>
    </body>
</html>
<html>
    <head><meta charset="utf-8"/><title>商品</title></head>
    <body>
        <p id="item_name">りんご</p>
        <p id="item_price">100円</p>
    </body>
</html>
  • grape.html
<html>
    <head><meta charset="utf-8"/><title>商品</title></head>
    <body>
        <p id="item_name">ぶどう</p>
        <p id="item_price">200円</p>
    </body>
</html>
  • banana.html
<html>
    <head><meta charset="utf-8"/><title>商品</title></head>
    <body>
        <p id="item_name">バナナ</p>
        <p id="item_price">79800円</p>
    </body>
</html>

Webサーバーを立ち上げる

上記のファイルを配置したディレクトリで、下記のコマンドを実行して、Pythonのビルトインサーバー機能を使って、Webサーバーを立ち上げましょう。

$ python -m http.server 8000

ブラウザを起動し、http://localhost:8000にアクセスをして、次のような画面が表示されればOKです。 なお、次節以降でWebスクレイピングを行うPythonプログラムを実行しますが、上記コマンドを実行したターミナルとは別のターミナルを立ち上げて実行する形を想定しています。

f:id:miracle-fjsw:20201028164742p:plain

初級編:特定の要素から単一の要素を抜き出す

それでは、先ほど作成したフルーツショップサイトから、「新商品」マークがついている商品の名前を取得してみましょう。

以下のPythonコードをscraping_beginner.pyというファイル名で保存します。

import requests
from bs4 import BeautifulSoup

target_url = "http://localhost:8000/"
r = requests.get(target_url)           # target_urlにアクセスして、HTMLを取得
r.encoding = 'utf-8'                   # 文字化け対策のため明示的に文字コードを指定

bs = BeautifulSoup(r.text, 'html.parser') 
new_fruit = bs.find(class_='new_item') # HTML要素の中から目的の要素を探す

print(new_fruit.text)
  • 実行結果
$ python scraping_beginner.py 
ぶどう
bs = BeautifulSoup(r.text, 'html.parser')

ここではHTML文字列を元にパースを実行し、Pythonプログラムで扱いやすくするオブジェクトへの変換を行っています。
第一引数r.textには、target_urlにアクセスした際のレスポンスのHTML文字列が格納されています。
今回は、実際にWebサイトにアクセスしていますが、次のような形で文字列を直接渡すことや、ファイルオブジェクトを渡すことも可能です。
開発中やデバッグの際に活用すると便利ですね。

bs = BeautifulSoup('<b class="boldest">Extremely bold</b>', 'html.parser')

第二引数html.parserは、HTMLの解析を行うライブラリに何を指定するかを記述します。
上記のものも含め、Pythonの定番パーサーを列挙します。

html.parser … Pythonの標準機能として備わっているパーサーで、手軽に利用できる半面、Pythonで記述されているため処理速度が遅い。
lxmlC言語で記述された高速なパーサーがあり、大量のHTMLを処理するのであればこちらを利用することがお薦めだが、別途OSにlxmlをインストールする必要がある。
html5lib … HTMLが正しく記述されていないなどの多少の問題があっても柔軟に解釈してくれる一方、処理速度がとても遅い。Python標準のライブラリではないので、別途pipでインストールが必要。

new_fruit = bs.find(class_='new_item')

ここでは、パースされたHTML要素の中から、目的の要素を探す処理を行っています。
今回の例であれば、classnew_itemが指定されている要素のうち、最初に見つかったものを返す、という処理になります。
※ちなみに、キーワードがclass_となっているのは、classという文字列がPython予約語だからです。

BeautifulSoupは、HTML要素にアクセスするために必要な機能を揃えています。 イメージを掴みやすくするため、いくつかの例を紹介しておきます。

・IDがnameである<a>要素のhref属性の値を取得する

bs.find('a', id=name)

classparentが指定されている<div>の子要素である<p>を取得する

bs.select(div.parent > p)

中級編:あるページから繰り返しを伴う複数の要素を抜き出す

次に、フルーツショップサイトから、商品の名前一覧と、商品の詳細ページへのリンクURLを全て取得してみましょう。

以下のPythonコードをscraping_intermediate.pyというファイル名で保存します。

import requests
from bs4 import BeautifulSoup

target_url = "http://localhost:8000/"
r = requests.get(target_url)
r.encoding = 'utf-8'

bs = BeautifulSoup(r.text, 'html.parser')

fruits = bs.find('ul', class_='fruits_list').find_all('li')

for fruit in fruits:
    fruit_name = fruit.a.text
    fruit_detail_url = fruit.a['href']
    print(fruit_name)
    print(fruit_detail_url)
  • 実行結果
$ python scraping_intermediate.py
りんご
http://localhost:8000/apple.html
ぶどう
http://localhost:8000/grape.html
バナナ
http://localhost:8000/banana.html
fruits = bs.find('ul', class_='fruits_list').find_all('li')

ここでは、二段階で要素の検索を行っています。
一段階目のfindでは、classfruits_listを持つ<ul>要素を探し、二段階目でその子要素の<li>を全て取得しています。 初級編で解説した通り、findは最初に見つかった要素を1つ返すだけでしたが、find_allは、見つかった要素を全て、配列で返却します。

for fruit in fruits:
    fruit_name = fruit.a.text
    fruit_detail_url = fruit.a['href']

find_allで取得した配列を走査し、それぞれのテキスト要素と、href属性要素を抜き出しています。

上級編:複数のページから複数の要素を抜き出し、CSVに出力する

ここまでは、ある単一のWebページから情報を抽出するだけでした。
最後に、極めて初歩的ではありますが、Webクローラーとしての振る舞いをもったプログラムを作成します。
フルーツショップサイトのトップページに記載されている全商品の詳細ページに含まれている、商品の金額情報を集め、CSVに出力してみましょう。

以下のPythonコードをscraping_advanced.pyというファイル名で保存します。

import csv
import requests
import time
from bs4 import BeautifulSoup

target_url = "http://localhost:8000/"
r = requests.get(target_url)
r.encoding = 'utf-8'

bs = BeautifulSoup(r.text, 'html.parser')

fruits = bs.find('ul', class_='fruits_list').find_all('li')

fruit_prices = []

for fruit in fruits:
    time.sleep(5)   # 一定時間待つ

    fruit_name = fruit.a.text
    fruit_detail_url = fruit.a['href']
    
    r_detail = requests.get(fruit_detail_url)
    r_detail.encoding = 'utf-8'

    bs_detail = BeautifulSoup(r_detail.text, 'html.parser')
    fruit_price = bs_detail.find('p', id='item_price').text

    fruit_prices.append([fruit_name, fruit_price])

with open('fruit_prices.csv', 'w') as csvfile:
    csvwriter = csv.writer(csvfile)
    for fruit_price in fruit_prices:
        csvwriter.writerow([fruit_price[0], fruit_price[1]])
  • 実行結果
$ python scraping_advanced.py

では、出力されたCSVを見てみましょう。

$ cat fruit_prices.csv
りんご,100円
ぶどう,200円
バナナ,79800円

期待通り、それぞれのページを参照して、必要な情報を集めることができていますね。

r = requests.get(target_url) … [A]
  :

fruits = bs.find('ul', class_='fruits_list').find_all('li')  … [B]
  :

for fruit in fruits:
     :
    fruit_detail_url = fruit.a['href']
     :    
    r_detail = requests.get(fruit_detail_url) … [C]

上記の処理が、とても簡単な処理ですが、この一覧の動きがWebクローラーとしての振る舞いになります。
[A]でトップ画面にアクセスし、[B]で表示されている果物リストを取得し、[C]で、それぞれの詳細ページにアクセスをする、という流れになっています。

time.sleep(5)   # 一定時間待つ

5秒間待ってから、次のページへのアクセスを行っています。
この処理はとても重要で、本稿で既に述べた通り、一定時間待つという処理を挟まない場合、データ取得先のサーバーに大きな負荷をかけてしまうことになるので、かならずスリープを挟むようにしましょう。

with open('fruit_prices.csv', 'w') as csvfile:
    csvwriter = csv.writer(csvfile)
    for fruit_price in fruit_prices:
        csvwriter.writerow([fruit_price[0], fruit_price[1]])

fruit_prices.csvというファイル名で、スクレイピングした情報をCSVに出力しています。 今回の例では、CSVに出力していますが、ここの処理をDBへの登録処理や、Excelファイルへの出力処理など、用途に応じて変更すると良いです。

とても簡単なサンプルでしたが、Webスクレイピング、およびWebクローラーの基本的な仕組みは以上です。

おわりに

さて、いかがでしたでしょうか。
今回は、Webスクレイピングを活用する上での前提知識、注意事項をふまえつつ、Pythonプログラムの実装例を紹介しました。

みなさんもぜひ、Webスクレイピングを活用して、業務の効率化をされてはいかがでしょうか。
くれぐれも、
・データ取得先のサーバーへの過度な負荷をかけないこと
利用規約を守ること
著作権法を守ること
について、ご注意くださいませ。

ではでは、みなさま良きWebスクレイピングライフを。

Copyright © RAKUS Co., Ltd. All rights reserved.