今回は、Pythonの標準ビルトインモジュールのurllib.requestを使ってWebサイトのデータを取得する方法についてまとめます。
確認した環境
- Ubuntu16.04LTS
- Python3.7.0
Webサイトのデータ取得
urllib.requestモジュールは、Webサイトを開く機能を提供するPythonの標準ビルトインモジュールです。
指定したURLのWebサイトを開くには、urlopen()メソッドを使います。書式は以下です。
urllib.request.urlopen(url, data=None, [timeout, ]*, cafile=None, capath=None, cadefault=False, context=None)
第一引数urlに開きたいサイトのURLを設定します。引数urlは文字列の他にRequestオブジェクトを取ることも出来ますが、本記事では文字列で指定することを前提に記載します。
また、他にもたくさんパラメータがありますが、ここでは割愛します。(詳細は公式ドキュメントを参考)
urlopen()関数はURLがHTTPやHTTPSの場合は、HTTPResponseオブジェクトを少し修正したオブジェクトを返します。
このオブジェクトが提供するメソッドを使ってWebサイトのデータを取得します。
例えば、当サイトのトップ画面のHTMLデータを読み込む場合はread()メソッドを使って以下のように書けます。
>>> import urllib.request
>>> with urllib.request.urlopen('https://hibiki-press.tech/learn_prog/') as f:
... f.read(100).decode('utf-8')
...
# 取得結果
'<!DOCTYPE html>\n<html lang="ja" class="no-js no-svg">\n<head>\n<meta charset="UTF-8">\n<meta name="view'
read()は、urlopen()で返ってきたレスポンスを読み出します。引数に読み出したいバイト数を設定します。また、urlopen()関数はbytesオブジェクトを返すので、読み込んだデータに対して、’utf-8’(※)でデコードする必要があります。
※該当するWebサイトが使用しているエンコーディングでデコードする必要があります。
urlopen()関数によって返されたオブジェクト(レスポンス)は、read以外にもメソッドがあります。主なものを以下に挙げます。
-
- geturl()メソッド
開いたWebサイトの実際のURLを取得します。指定したURLがリダイレクトされているかどうかを確認できます。
以下はurlopen関数で指定したURLと実際のURLが異なっており、リダイレクトが発生した例です。
>>> res = urllib.request.urlopen('https://www.hibiki-press.tech/learn_prog/')
>>> res.geturl()
'https://hibiki-press.tech/learn_prog/'
-
- info()メソッド
サーバから送信されたヘッダー情報を辞書の形式で返します。
>>> with urllib.request.urlopen('https://www.hibiki-press.tech/learn_prog/') as res:
... dict(res.info())
...
{'Date': 'Thu, 26 Jul 2018 23:40:33 GMT',
'Server': 'Apache/2.4.10 (Debian)',
'Referrer-Policy': 'unsafe-url',
'x-frame-options': 'SAMEORIGIN',
'X-XSS-Protection': '1; mode=block',
'X-Content-Type-Options': 'nosniff',
'Link': '<https://hibiki-press.tech/learn_prog/wp-json/>; rel="https://api.w.org/"',
'Vary': 'Accept-Encoding',
'Connection': 'close',
'Transfer-Encoding': 'chunked',
'Content-Type': 'text/html; charset=UTF-8'}
-
- getcode()メソッド
サーバから返されるステータスコードを確認できます。以下の場合は200なので、正常に読み込めていることがわかります。
>>> res.getcode()
200
ちなみに、ステータスコード一覧はこちらをご参照ください。
エラー処理
スクレイピング等で指定したサイトにアクセスした際に、そのサイトが見つからなかったり、レスポンスを処理出来なかった場合、サーバーはエラー(例外)を返します。これによってプログラムに不測の事態が発生しないよう適切に処理をする必要があります。
この章では、urlopen関数が送出するURLErrorおよびHTTPErrorについて、またこれらのエラーの処理方法の一例についてまとめます。
URLErrorとHTTPError
これらのエラーはurllib.errorモジュールから提供されています。
-
- URLError
URLErrorはネットワーク接続が無い場合や、指定したサーバが無い場合に送出されます。また、reason属性でエラーの理由を文字列あるいは他の例外インスタンスで示します。
# 指定したサーバがない場合
>>> import urllib.request
>>> try:
... urllib.request.urlopen('https://hibiki-press.xxx')
... except urllib.error.URLError as e:
... print(e.reason)
...
[Errno -2] Name or service not known
-
- HTTPError
urlopen関数がレスポンスを処理できない場合は、HTTPErrorを送出します。典型的なエラーには ‘404:NOT_FOUND’、 ‘403:FORBIDDEN’、 ‘401:UNAUTHORIZED’などがあります。また、この例外はurlopen()の返り値と同じ属性(readやstatus、reason等)が使えます。
# 指定したページが存在しない場合
>>> try:
... urllib.request.urlopen('https://hibiki-press.tech/no_exist')
... except urllib.error.HTTPError as e:
... print(e.code)
... print(e.reason)
...
404
Not Found
エラー処理のやりかた
HTTPErrorとURLErrorの両方を補足できるようにするコードの例を以下に示します。
ここで、”except URLError”がHTTPErrorも捕まえてしまうのを避けるため、”except HTTPError”を最初に書く必要がります。
def url_check(url):
try:
res = urllib.request.urlopen(url)
except urllib.error.HTTPError as e:
print('raise HTTPError')
print(e.code)
print(e.reason)
except urllib.error.URLError as e:
print('rase URLError')
print(e.reason)
else:
print(res.status)
print(res.read(100))
res.close()
実行結果
# 正常にアクセスできるサイトの場合
>>> url_check('https://hibiki-press.tech/learn_prog/')
200
b'<!DOCTYPE html>\n<html lang="ja" class="no-js no-svg">\n<head>\n<meta charset="UTF-8">\n<meta name="view'
# 存在しないサイトにアクセスした場合
>>>url_check('https://hibiki-press.tech/no_exist/')
raise HTTPError
404
Not Found
# 存在しないドメイン?にアクセスした場合
url_check('https://hibiki-press.xxx')
rase URLError
[Errno -2] Name or service not known
これで指定したサイトにアクセスが出来ない場合の処理ができるようになりますので、後はプログラムが不正に停止しないよう適切な処理を追加できるようになると思います。
まとめ
今回は、urllib.requestモジュールを使ってWebサイトのデータを取得する方法と、アクセスエラー等の例外が発生した場合の対処の方法についてまとめました。
が、公式には「HTTPクライアントインターフェイスとしてはRequestsパッケージがおすすめ」と記載があります。
よって次回は、Requestsモジュールを用いたWebサイトのデータ取得についてまとめる予定です。