end0tknr's kipple - web写経開発

太宰府天満宮の狛犬って、妙にカワイイ

requests for python による大容量レスポンスの分割ダウンロード

requests for python で、巨大なfileをhttp getする場合、stream=True で chunk化 - end0tknr's kipple - web写経開発

以前の上記entryの修正版です。

国土交通省で公開している不動産取引価格情報取得API ( https://www.land.mlit.go.jp/webland/api.html )に対し、 python3標準の urllib.request を用い

import urllib.request
http_conf = {"retry_limit":3, "retry_sleep":5 }

def get_http_requests(self, req_or_url,req_timeout=5):
    i = 0
    while i < http_conf["retry_limit"]:
        i += 1
        try:
            http_res = urllib.request.urlopen(req_or_url, timeout=req_timeout)
            html_content = http_res.read()
            return html_content
        
        except Exception as e:
            if "404: Not Found" in str(e):
                logger.error("{} {}".format(req_or_url, e))
                return None
            
            logger.warning(e)
            logger.warning("retry {} {}".format(i,req_or_url))
            time.sleep(http_conf["retry_sleep"])

    return None

のように http getを行うと、本来、数MBの要領がある影響でしょうか、 レスポンスが途中で途切れてしまいます。

https://docs.python.org/ja/3/library/urllib.request.html を見ると、 「 より高水準のHTTPクライアントインターフェースとして Requestsパッケージ がお奨めです。」 という記載がありましたので、

「pip install requests」した上で、以下のように requests を用い、かつ、「stream=True」で分割ダウンロードすることで解消

import requests

def get_http_requests(self,req_url):
    i = 0
    while i < 3: # 最大3回 retry
        i += 1
        try: # 先方サーバによっては http response codeを返さない為、try-except
            res = requests.get(req_url, timeout=(5,60), stream=True)
        except Exception as e:
            logger.warning(e)
            logger.warning("retry {} {}".format(i,req_url))
            time.sleep(10)

        if res.status_code == 404:
            logger.error( "404 error {}".format(req_url) )
            return

        try:
            res.raise_for_status()
        except Exception as e:
            logger.warning(e)
            logger.warning("retry {} {}".format(i,req_url))
            time.sleep(10)

    # 大容量の為か urllib.request.urlopen()では
    # response contentを取得できなかった為、stream=True で chunk化
    chunks = []
    for chunk in res.iter_content(chunk_size=1024*1024):
        chunks.append(chunk)
      
    content = b"".join(chunks).decode()
    return json.loads( content )