7. Web API、スクレイピング¶
節サブタイトル
自動でデータを収集する方法
7.1. Web APIとスクレイピングとは¶
Web API はインターネット上に用意されているAPIをプログラムから呼び出す技術のことです。 スクレイピング はウェブサイトから情報を抽出する、コンピュータソフトウェア技術のことをいいます。
ここではPythonを使った実用的なプログラムの例として、Web APIとスクレイピングの演習を行います。
7.2. 環境構築¶
前章の「 venvとは 」を参考に、venvモジュールを利用して、スクレイピング用のvenv環境を構築します。
venv環境を activate
コマンドで有効にし、Web APIとスクレイピングに使用する Requests と Beautiful Soup 4 を pip
コマンドでインストールします。
$ mkdir scraping
$ cd scraping
$ python3 -m venv env
$ source env/bin/activate
(env) $ pip install requests
(env) $ pip install beautifulsoup4
> mkdir scraping
> cd scraping
> python -m venv env
> env\Scripts\Activate.ps1
(env) > pip install requests
(env) > pip install beautifulsoup4
7.2.1. Requests¶
Requests について簡単に紹介します。 Requests はウェブサイトにアクセスしてHTMLなどのデータを取得するためのライブラリです。 Pythonの標準ライブラリ urllib.request でも同様のことは行なえますが、より便利な Requests をここでは使用します。
7.2.2. Beautiful Soup 4¶
Beautiful Soup 4はHTMLやXMLの中身を解析して、任意の情報を取得するためのライブラリです。 Pythonの標準ライブラリ html.parser でも同様のことは行なえますが、より便利な Beautiful Soup 4 をここでは使用します。 なお、beautifulsoupとbeautifulsoup4が存在しますが、新しい beautifulsoup4 を使うようにしてください。
7.3. シンプルなWeb APIのコード¶
Web APIの例として、SWAPI.INFOの「Star Wars API」で映画『スター・ウォーズ』シリーズに関する情報を入手してみましょう。
下記のコードを films.py
という名前で、先ほど作成したscrapingフォルダ内に保存します(リスト 7.3)。
import requests
def main():
url = "https://swapi.info/api/films"
response = requests.get(url)
films = response.json()
for film in films:
print(f"Episode {film['episode_id']}: {film['title']} ({film['release_date']})")
if __name__ == "__main__":
main()
このコードを実行すると、『スター・ウォーズ』シリーズの旧三部作、新三部作のタイトルと公開日時の一覧を取得できます。(リスト 7.4)。 なお、『スター・ウォーズ』シリーズは制作順序とエピソード番号の順序が異なるので注意しましょう。
(env) $ python films.py
Episode 4: A New Hope (1977-05-25)
Episode 5: The Empire Strikes Back (1980-05-17)
Episode 6: Return of the Jedi (1983-05-25)
Episode 1: The Phantom Menace (1999-05-19)
Episode 2: Attack of the Clones (2002-05-16)
Episode 3: Revenge of the Sith (2005-05-19)
7.3.1. コードの解説¶
上記のコードがどういった内容なのかを解説します。
Web APIを実行するために
requests
をインポートします
import requests
メインとなる処理を
main
関数として定義しています。 なお、関数の名前に特に決まりはなく、必ずしもmain
である必要はありません。
def main():
APIのエンドポイントとなるURLを設定します。映画以外にも登場人物や乗り物、惑星などの情報を取得できるので、興味がある方はドキュメントを読んで試してみてください。
url = "https://swapi.info/api/films"
requests.get()
にURLとパラメーターを指定して結果を取得します。結果はJSON形式で返ってくるので、
.json()
メソッドでPythonのデータ型(辞書、リスト等)に変換します。
response = requests.get(url)
films = response.json()
Pythonデータ型の映画に関する情報から、エピソード番号、タイトル、公開日付を取得して出力します。
for film in films:
print(f"Episode {film['episode_id']}: {film['title']} ({film['release_date']})")
最後に、このスクリプトが実行された時に、
main()
関数を実行するように指定します。
if __name__ == "__main__":
main()
コラム: JSON形式
Web APIにデータを送ったり、受け取ったりするときによく使われるデータの形式です。JavaScript Object Notationという、JavaScriptのオブジェクト表記法を使っています。この表記法は項目名と値のペアをテキストで記述したもので、階層構造にも対応しています。人間が簡単に読み取れて、コンピュータからも扱いやすいため、Pythonを含めさまざまな言語で活用されています。
7.3.2. 新たなるWeb API¶
今回紹介したStar Wars API以外にも興味深いWeb APIがたくさんあります。 いくつか紹介します。
名称 |
Webサイト |
内容 |
---|---|---|
NASA API |
NASA(アメリカ航空宇宙局)が所有するデータが扱えるAPI |
|
PokeAPI |
ゲームソフト『ポケットモンスター』シリーズに関するデータを取得できるAPI |
|
国立国会図書館サーチ API |
国立国会図書館の所蔵検索APIや書影検索API |
また、Public-APIsには種類別にWeb APIがリストアップされています。 気になるWeb APIがあれば、是非試してみましょう。
7.4. シンプルなスクレイピングのコード¶
スクレイピングの例として、docs.python.orgの組み込み関数一覧のページ(https://docs.python.org/ja/3.10/library/functions.html)から関数名の情報を抜き出します。
下記コードを funcs.py
という名前で、scrapingフォルダ内に保存します(リスト 7.11)。
import requests
from bs4 import BeautifulSoup
def main():
url = 'https://docs.python.org/ja/3.10/library/functions.html'
res = requests.get(url)
content = res.content
soup = BeautifulSoup(content, 'html.parser')
functions = soup.find_all('dl', class_='py function')
print('件数:', len(functions))
for func in functions:
func_name = func.dt.code.text
# 上記記述だと@staticmethodの関数名が正しく取れないので、取りたい場合はこちら
# func_name = func.dt.find_all('code', class_='sig-name')[0].text
print(func_name)
if __name__ == '__main__':
main()
このコードを実行すると、以下のように関数名の一覧が取得できます(リスト 7.12)。
(env) $ python funcs.py
件数: 52
abs
aiter
all
anext
any
ascii
bin
breakpoint
:
コラム: Pythonのコーディング規約「PEP8」
Pythonには PEP8(ペップエイト) というコーディング規約があります。 チームで開発をする際、人によってプログラムコードの書き方がバラバラだと読みにくいコードになってしまいます。 そのため、PEP8のルールに従う習慣を身につけておくとよいでしょう。
コードがPEP8のルールに従っているかは、 pycodestyle というツールで検証できます(以前はツールの名前もpep8でした)。
pycodestyleは pip install pycodestyle
でインストールして使用します。
funcs.py
を検証するには、 pycodestyle funcs.py
を実行します。
7.4.1. コードの解説¶
上記のコードがどういった内容なのかを解説します。
以下のコードはRequestsとBeautiful Soup 4をimportして利用できるようにしています。
import requests
from bs4 import BeautifulSoup
メインとなる処理を
main
関数として定義しています。 なお、関数の名前に特に決まりはなく、必ずしもmain
である必要はありません。
def main():
Requestsを使用して、Webページの内容(HTML)を取得します。res.contentにHTMLの中身が文字列データとして入っています。
url = 'https://docs.python.org/ja/3.10/library/functions.html'
res = requests.get(url)
content = res.content
次にHTMLをBeautiful Soup 4に渡して解析します。HTMLの解析についてはもう少し詳しく説明します。
soup = BeautifulSoup(content, 'html.parser')
functions = soup.find_all('dl', class_='py function')
print('件数:', len(functions))
for func in functions:
func_name = func.dt.code.text
# 上記記述だと@staticmethodの関数名が正しく取れないので、取りたい場合はこちら
# func_name = func.dt.find_all('code', class_='sig-name')[0].text
print(func_name)
最後に、このスクリプトが実行された時に、main()関数を実行するように指定します。
if __name__ == '__main__':
main()
7.4.2. HTMLの解析の解説¶
Beautiful Soup 4でHTMLを解析して、値が取り出せましたが、どのように指定しているのでしょうか? 組み込み関数一覧のHTMLを見てみると、以下のような形式になっています。(リスト 7.18)
<dl class="py function">
<dt id="abs">
<code class="sig-name descname">abs</code>
<span class="sig-paren">(</span><em class="sig-param">
<span class="n">x</span></em>
<span class="sig-paren">)</span>
<a class="headerlink" href="#abs" title="この定義へのパーマリンク">¶</a>
</dt>
<dd>
<p>数の絶対値を返します。引数は整数、浮動小数点数または
<code class="xref py py-meth docutils literal notranslate">
<span class="pre">__abs__()</span>
</code>
が実装されたオブジェクトです。引数が複素数なら、その絶対値 (magnitude) が返されます。
</p>
</dd>
</dl>
<dl class="py function">
<dt id="aiter">
<code class="sig-name descname">aiter</code>
<span class="sig-paren">(</span>
<em class="sig-param">
<span class="n">async_iterable</span>
</em>
<span class="sig-paren">)</span>
<a class="headerlink" href="#aiter" title="この定義へのパーマリンク">¶</a>
</dt>
<dd>
<p>:term:
<a href="#id1">
<span class="problematic" id="id2">`</span>
</a>
asynchronous iterable`から :term:
<a href="#id3">
<span class="problematic" id="id4">`</span>
</a>
asynchronous iterator`を返します。
<a href="#id5">
<span class="problematic" id="id6">``</span>
</a>x.__aiter__()``を呼び出すのと等価です。
</p>
<p>なお、:func:
<a href="#id1">
<span class="problematic" id="id2">`</span>
</a>
iter`とは異なり、:func:
<a href="#id3">
<span class="problematic" id="id4">`</span>
</a>
aiter`は第二引数を持ちません。
</p>
<div class="versionadded">
<p>
<span class="versionmodified added">バージョン 3.10 で追加.</span>
</p>
</div>
</dd>
</dl>
(以下続く)
このHTMLを見ると、関数の名前とURLは以下のようにして取得できそうです。
1つの関数の情報は
<dl class="py function">
の中に入っている関数名は
<code class="sig-name descname">
タグで囲まれた中に入っている
HTMLの構造がわかったところで、もう一度HTMLを解析しているコードを見てみます。
soup = BeautifulSoup(content, 'html.parser')
functions = soup.find_all('dl', class_='py function')
print('件数:', len(functions))
for func in functions:
func_name = func.dt.code.text
# 上記記述だと@staticmethodの関数名が正しく取れないので、取りたい場合はこちら
# func_name = func.dt.find_all('code', class_='sig-name')[0].text
print(func_name)
まず、 soup.find_all()
メソッドで、全関数の情報が含まれている dl 要素を取得しています。
次に、各関数情報(func変数に入っている)から値を取り出しています。
関数名を取得して、出力しています。
7.5. 作り変えてみよう¶
RequestsやBeautiful Soup 4の動作を変えて、さまざまなWebページから色んな要素を取得できます。
以下にそれぞれのライブラリの簡単な使い方を紹介します。それ以外にもいろいろな使用方法があるので、ドキュメントを参考にしていろいろ作り変えてみてください。
7.5.1. Requests の主な使い方¶
ここでは Requests の主な使い方の例をいくつか載せます。 詳細については以下の公式ドキュメントを参照してください。
公式ドキュメント: Requests: HTTP for Humans
以下は認証つきのURLにアクセスして、結果を取得する例です。
>>> import requests
>>> r = requests.get('https://api.github.com/user', auth=('user', 'pass'))
>>> r.status_code
200
POST を行う場合は以下のように、POSTのパラメーターを辞書で定義します。
>>> payload = {'key1': 'value1', 'key2': 'value2'} # POST するパラメーター
>>> r = requests.post('http://httpbin.org/post', data=payload)
>>> print(r.text)
GET に ?key1=value1&key2=value2
のようなパラメーター付きでアクセスする場合も同様に、辞書で定義します。
>>> payload = {'key1': 'value1', 'key2': 'value2'}
>>> r = requests.get('http://httpbin.org/get', params=payload)
>>> print(r.url)
http://httpbin.org/get?key2=value2&key1=value1
>>> payload = {'key1': 'value1', 'key2': ['value2', 'value3']}
>>> r = requests.get('http://httpbin.org/get', params=payload)
>>> print(r.url)
http://httpbin.org/get?key1=value1&key2=value2&key2=value3
7.5.2. Beautiful Soup 4の主な使い方¶
ここではBeautiful Soup 4の主な使い方の例をいくつか載せます。 詳細については以下の公式ドキュメントを参照してください。
公式ドキュメント: Beautiful Soup Documentation
>>> import requests
>>> from bs4 import BeautifulSoup
>>> r = requests.get('https://www.python.org/blogs/')
>>> soup = BeautifulSoup(r.content, 'html.parser') # 取得したHTMLを解析
>>> soup.title # titleタグの情報を取得
<title>Our Blogs | Python.org</title>
>>> soup.title.name
'title'
>>> soup.title.string # titleタグの文字列を取得
'Our Blogs | Python.org'
>>> soup.a
<a href="#content" title="Skip to content">Skip to content</a>
>>> len(soup.find_all('a')) # 全ての a タグを取得しt len() で件数を取得
164
また、 find()
find_all()
などでタグを探す場合には、タグの属性などを条件として指定できます。
>>> len(soup.find_all('h1')) # 指定したタグを検索
3
>>> len(soup.find_all(['h1', 'h2', 'h3'])) # 複数のタグのいずれかにマッチ
24
>>> len(soup.find_all('h3', {'class': 'event-title'})) # <h3 class="event-title"> にマッチ
5
7.6. まとめ¶
本節では、Pythonでスクレイピングをする方法を解説しました。
RequestsとBeautiful Soup 4を使いこなすことにより、さまざまなウェブサイトから情報を取得できるようになります。
なお、短時間にWebサイトに大量にアクセスをすると迷惑となるので、そういうことがないようにプログラムを実行するときには注意してください。
7.7. 参考書籍¶
Pythonでのスクレイピングについてもいくつか書籍が出ています。