stream=True を使うと、プロキシ経由の応答本文が完全にダウンロードされる前に処理を始められます。CSV ではテキスト行のイテレータを csv.reader に渡すことで、引用符内の区切り文字や複数行にまたがるレコードが CSV データのまま保たれます。

セミコロンを使う UTF-8 ファイルの例を示します。

import csv
import requests

routes = {'https': 'http://proxy.example:8080'}
with requests.get(
    'https://data.example/observations.csv',
    proxies=routes,
    stream=True,
    timeout=(5, 30),
) as response:
    response.raise_for_status()
    response.encoding = 'utf-8'
    lines = (
        line + '\n'
        for line in response.iter_lines(
            chunk_size=65536, decode_unicode=True)
    )
    for record in csv.reader(lines, delimiter=';'):
        if record and record[0] == 'wanted':
            print(record)
            break

実際のファイルのエンコーディングと方言を選んでください。許可された公開プロキシ経路での公開 CSV ダウンロードには、HTTP プロキシ一覧が routes URL の候補を提供します。この版は物理的な行末を LF に正規化します。iter_lines がそれらを取り除き、ジェネレータが CSV パーサのために改行を付け直します。引用符内のフィールドが別の物理行に続く場合に重要です。

埋め込まれた CRLF 文字をバイト単位で厳密に保持する必要があるワークフローでは、それらの行末を保つストリーミングのテキストラッパー構成を使ってください。例の意図的な正規化は、LF が意図された表現であるレコード指向の処理に適しています。

1 つの応答、多数の読み取り

イテレータは 1 つの HTTP 応答からチャンクを消費します。行ごとに新しいリクエストを発行するわけではありません。HTTP Range リクエストは別の仕組みであり、CSV レコードの境界を自然に識別するものではありません。

response.text を呼ぶことやすべての行をリストに集めることは、内容全体をメモリに戻すことになります。ストリーミングでも転送バッファと現在のレコードのための場所は必要で、レコード自体が大きなフィールドを含むことがあります。

応答コンテキストはループを抜けるときに本文を閉じます。早期一致の場合も同様です。読み取りタイムアウトは受信データの待ち時間を制限するもので、大きなファイル転送の総寿命を制限するものではありません。

関連記事