PythonでWebページ取得・スクレイピングするための定番ライブラリの活用
PythonはこれまでDjangoとセットで使ってきたのですが、Webアプリケーションでの活用が少しできる程度で、データ収集・解析のための技術、基礎力がまだまだ弱いと感じています。
最終的には機械学習の領域にもいつか足を踏み入れたいと思っているので、一旦基礎に立ち返って情報を整理します。
ライブラリのインストール
PythonにはPyPI(Python Package Index)というパッケージリポジトリがあり、ここからサードパーティライブラリを入手できます。JavaScriptでいうnpm、RubyでいうGemのようなものですね。
PyPIで公開されているライブラリのインストールには pip
を使います。
# ライブラリのインストール
pip install hogehoge
# ライブラリのバージョンを指定してインストール
pip install hogehoge==1.0
ライブラリのリストをまとめたtxtファイルからインストールすることもできます。
# ライブラリの複数インストール
pip install requirements.txt
# requirements.txtの中身(例)
Django==2.1.7
psycopg2==2.7.6
psycopg2-binary==2.7.7
djangorestframework==3.9.2
django-cors-headers==2.5.2
次のコマンドで環境内にインストールされた全てのライブラリを確認できます。
# インストールされた全てのライブラリの確認
pip freeze
pipでは同じライブラリの複数バージョンを共存できないので、複数のバージョンを使い分けたいときはDockerやvenvで環境ごと分ける必要があります。
Webページ取得用の定番ライブラリ
- requests
- Beautiful Soup
__requests__はWebページの取得に、__Beautiful Soup__はHTMLのスクレイピングに使われる定番のライブラリです。
requests…GETとかPOST
GETやPOSTの他にHTTPヘッダーの追加やBasic認証など、少し凝った処理も可能で、運用保守作業の自動化などに便利です。
# requestsのインストール
pip install requests
試しに次のコードをpythonのインタラクティブシェルで実行してみます。
>>> import requests
>>> r = requests.get("https://www.google.com/")
>>> r
<Response [200]>
>>> dir(r)
['__attrs__', '__bool__', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__enter__', '__eq__', '__exit__', '__format__', '__ge__', '__getattribute__', '__getstate__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__nonzero__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setstate__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_content', '_content_consumed', '_next', 'apparent_encoding', 'close', 'connection', 'content', 'cookies', 'elapsed', 'encoding', 'headers', 'history', 'is_permanent_redirect', 'is_redirect', 'iter_content', 'iter_lines', 'json', 'links', 'next', 'ok', 'raise_for_status', 'raw', 'reason', 'request', 'status_code', 'text', 'url']
requests.get()
で対象のWebページの情報(Response)をオブジェクトとして取得できます。オブジェクトの属性をリスト表示する標準ライブラリ dir()
でチェックしたところ、jsonやheaders、connection、status_codeなど、様々な属性があります。コンテンツの中身はtextやcontentなどで取得できます。
postする場合、キーワード引数dataにdict形式で情報を指定するとHTMLフォーム形式で送信できます。他にもHTTPヘッダーの追加やBasic認証、HTTP Keep-Aliveの指定なども可能です。
# post送信
r = requests.post('target_url', data={'key1': 'value1'})
# リクエストにHTTPヘッダーを指定
r = requests.get('target_url', headers={'key1': 'value1'})
# Basic認証
r = requests.get('https://api.github.com/user', auth=('ID', 'PASSWORD'))
# HTTP Keep-Alive(一度確立したTCPコネクションを複数のリクエストで使い回す)
s = requests.Session()
BeautifulSoup…HTMLのスクレイピング
__Beautiful Soup__はシンプルで使いやすいAPIでデータを抜き出せる人気のライブラリ。同じようなスクレイピング用のライブラリで、jQueryっぽく使える__pyquery__というものもありますが、こちらはあまり使われていないようです。
# beautifulsoup4のインストール
pip install beautifulsoup4
↓試しにインタラクティブシェルで実行してみます。
# 指定URLから、Webページのtitleタグの情報を取得
>>> import requests
>>> from bs4 import BeautifulSoup
>>> r = requests.get("https://www.google.com/")
>>> bs = BeautifulSoup(r.text, 'html.parser')
>>> bs.title
<title>Google</title>
一度requestsでページの情報を取得した後、 BeautifulSoup()
関数の第一引数にコンテンツを渡しています。 BeautifulSoup()
関数で取得してしまえば、あとは煮るなる焼くなりできます。
# ↓'lga'というidをもつタグの情報を取得
>>> bs.select('#lga')
[<div id="lga"><img alt="Google" height="92" id="hplogo" src="/images/branding/googlelogo/1x/googlelogo_white_background_color_272x92dp.avif" style="padding:28px 0 14px" width="272"/><br/><br/></div>]
# ↓'gb1'というクラスをもつタグの情報を取得
>>> bs.select('.gb1')
[<b class="gb1">....
# ↓全てのdiv要素を取得
>>> bs.find_all('div')
urllib…URLを相対パスから絶対パスに変換したり
複数のページにまたがって情報を取りたいときに、相対パスになっているURLを絶対パスに変換する必要があったりします。
標準ライブラリに urllib.parse
というモジュールがあり、url関連の操作を行えます。
例えば下記のように、ベースのURLと相対パスの画像を繋げて絶対パスに変換できます。
>>> from urllib.parse import urljoin
>>> base_url = 'https://www.google.com'
>>> relative_url = '/images/branding/googlelogo/2x/googlelogo_color_272x92dp.avif'
>>> urljoin(base_url, relative_url)
'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.avif'
まとめ
スクレイピングでよく使うライブラリ、requestsとBeautifulSoupについてまとめました。
最近はスクレイピングよりもWebAPIを使う方がスマートかと思いますが、スクレイピングは歴史が長いので情報も多く、手軽に使えて便利です。WebAPIが実装されていないサイトにも対応できて、運用保守・オペレーター業務の効率化で大活躍です。
MySQLなどのDBと連携すれば、取得したデータを整形して保存したり、データ分析に役立てることもできるかなと思います。