Python Requests と csv.reader で複数行の CSV レコードをストリーム処理する
プロキシ経由の応答を csv.reader にストリームし、物理的な行ごとに区切り文字で分割するのではなくレコード境界を保ちます。
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 を呼ぶことやすべての行をリストに集めることは、内容全体をメモリに戻すことになります。ストリーミングでも転送バッファと現在のレコードのための場所は必要で、レコード自体が大きなフィールドを含むことがあります。
応答コンテキストはループを抜けるときに本文を閉じます。早期一致の場合も同様です。読み取りタイムアウトは受信データの待ち時間を制限するもので、大きなファイル転送の総寿命を制限するものではありません。