私はPythonでrequestとbeautifulsoup4モジュールを組み合わせてWebスクレイピングをすることが多いのですが、コーディング量を減らしたいということでrequests.getからBeautifulSoupのインスタンス化までの流れを自作モジュール化しました。
本記事では作成した自作モジュールを共有したいと思います。
目次
背景
これまでBeautifulSoupのインスタンス化を行う際におまじないような感じでこのようなコードを何も考えずに書いていました。
# モジュールのインポート
from time import sleep
from bs4 import BeautifulSoup
import requests
def main():
r = requests.get('https://webscraper.blog/', timeout=30) # レスポンスの取得
r.raise_for_status() # 例外送出
sleep(1) # 待機処理
soup = BeautifulSoup(r.content, 'lxml') # BeautifulSoupのインスタンス化
if __name__ == '__main__':
main()このコードでは1リクエストのみで待機処理は不要ですが、実際にはほとんどが複数リクエストを行うコードなのでこの後に別のリクエストを行うことを想定して待機処理を記載しています。複数リクエストとなるとrequest.getからBeautifulSoupのインスタンス化のコードを再度入力が必要となります。
複数リクエストとなるとrequest.getからBeautifulSoupのインスタンス化のコードを再度入力が必要となります。
はっきり言って定型コードなのに毎回このコードを入力するのはめんどくさいですよね?そう思い自作モジュール化しようと考えました。
作成した自作モジュール
コードの詳細は割愛しますが、作成した自作モジュールのコードは以下のとおりです。
import random
from time import sleep
import traceback
from bs4 import BeautifulSoup
import requests
from tenacity import retry, stop_after_attempt, wait_fixed
class MyBeautifulSoup(BeautifulSoup):
"""BeautifulSoupクラスを継承し、リクエスト失敗時のリトライ機能とリクエスト後の待機処理を追加
最大リトライ2回
"""
def __init__(
self,
url: str,
*,
headers: dict[str, str] = None,
sleep_time: int | float | tuple[int | float, int | float] = 1,
) -> None:
"""
Args:
url (str): リクエストURL
headers (dict[str, str], optional): リクエストヘッダ、初期値None
sleep_time (int | float | tuple[int | float, int | float], optional): リクエスト後の待機時間、タプルで範囲指定した場合は範囲内でランダム、初期値1
"""
self._url = url
self._headers = headers
if isinstance(sleep_time, tuple) and len(sleep_time) != 2:
raise ValueError('When sleep_time is a tuple, 2 arguments must be specified')
self._response = self._fetch_url()
if isinstance(sleep_time, tuple):
wait_time = random.uniform(sleep_time[0], sleep_time[1])
else:
wait_time = sleep_time
sleep(wait_time)
super().__init__(self.response.content, 'lxml')
@retry(stop=stop_after_attempt(3), wait=wait_fixed(60))
def _fetch_url(self):
try:
response = requests.get(self._url, headers=self.headers, timeout=30)
response.raise_for_status()
except requests.RequestException:
print(traceback.format_exc())
raise
return response
@property
def url(self):
return self._url
@property
def headers(self):
return self._headers
@property
def response(self):
return self._responseポイント
- tenacityモジュールを使用してrequests.get失敗時に最大2回のリトライ機能を追加
(stop_after_attemptの引数を変更することで最大リトライ回数、wait_fixedの引数を変更することで失敗後の待機時間を変更可) - requests.get後の待機時間を指定可能
(int、floatで指定時はその時間待機、tupleで指定時はその範囲内でランダムの時間待機)
この自作モジュールをインポートして書くことでこのようにスッキリとしたコードでBeautifulSoupのインスタンス化を行うことが可能となります。ぜひ使用してみてください。
from mybeautifulsoup import MyBeautifulSoup
def main():
soup = MyBeautifulSoup('https://webscraper.blog/')
if __name__ == '__main__':
main()

コメント