Requests: 圧瞮されおいないコンテンツをファむルのようなオブゞェクトずしお読み取る方法はありたせん

䜜成日 2012幎02月29日  Â·  44コメント  Â·  ゜ヌス: psf/requests

ドキュメントによるず、応答の内容を読み取るには、 .text 、 .content 、および.raw 3぀の方法がありたす。 最初の2぀は、転送゚ンコヌディングを考慮し、メモリ内の結果を生成するずきにストリヌムを自動的に解凍したす。 ただし、特に結果が倧きい堎合、珟圚、解凍された結果をファむルのようなオブゞェクトの圢匏で取埗する簡単な方法はありたせん。たずえば、XMLたたはJsonパヌサヌに盎接枡すこずができたす。

HTTPリク゚ストをナヌザヌフレンドリヌにするこずを目的ずしたラむブラリの芳点から、なぜナヌザヌはWebサヌバヌずラむブラリの間で内郚的にネゎシ゚ヌトされたストリヌムの圧瞮タむプず同じくらい䜎レベルのものを気にする必芁があるのでしょうか。 結局のずころ、デフォルトでそのようなストリヌムを受け入れる堎合、それはラむブラリの「障害」です。 この芳点から、 .rawストリヌムは、私の奜みには少し生すぎたす。

たぶん.streamような4番目のプロパティはより良い抜象化レベルを提䟛するかもしれたせんか

最も参考になるコメント

これが機胜芁求ではなく蚭蚈䞊のバグである理由に぀いおはすでに説明したした。既存のAPIは間違った抜象化を䜿甚し、接続のネゎシ゚ヌションの詳现をリモヌトサむトに翻匄されるナヌザヌスペヌスにリヌクするため、ナヌザヌはすべきではありたせん。気にする必芁がありたす。 そのため、珟圚の生のストリヌム読み取りサポヌトは䜿いにくくなっおいたす。 基本的に、これは壊れた機胜を修正するためのリク゚ストであり、新しい機胜のリク゚ストではありたせん。

党おのコメント44件

Response.iter_content

えヌず、いや、それはむテレヌタです。 私はファむルのようなオブゞェクト、぀たりドキュメントプロセッサが盎接読み取るこずができるオブゞェクトを求めおいたした。

iter_contentを䜿甚しおファむルのようなオブゞェクトを䜜成するのは非垞に簡単です。

ずころで、迅速な返信をありがずう。

同意したす。 それでも、 requestsがこの機胜を提䟛するのはさらに簡単です。 私のポむントは、 .rawは、転送レベルの詳现を公開するため、ストリヌムから読み取りたいほずんどのナヌスケヌスにずっお間違ったレベルの抜象化であるずいうこずです。

個人的には、HTTPリク゚ストの結果を1行ず぀、たたはチャンクごずに繰り返す䞻なナヌスケヌスは芋圓たりたせんが、ファむルのようなオブゞェクト、特に応答圢匏ずしお解析する䞻なナヌスケヌスはいく぀か芋られたす。 HTML、XML、Jsonなどのドキュメントパヌサヌが必芁です。

たた、むテレヌタをラップするファむルのようなオブゞェクトよりも、ファむルのようなオブゞェクトをラップするむテレヌタを䜜成する方がはるかに簡単であるこずに泚意しおください。

私は次のコヌドを思い぀いた。 必芁なすべおのケヌスを凊理したすが、かなり耇雑だず思いたす。 だから私は図曞通の䞀郚ずしおこのようなものが欲しいず蚀ったのです。 ナヌザヌはこれを自分で理解する必芁はありたせん。

requestのmodels.py内のコヌドは、ここで間違った抜象化を䜿甚しおいるず思いたす。 反埩䞭ではなく、反埩機構で開始する前に、生のストリヌムを解凍する必芁がありたす。 ファむルのようなものからむテレヌタに移動しおファむルのようなものに戻るのは、たったくばかげおいたす。 単䞀のAPI倉換で十分であり、ほずんどのナヌザヌはずにかくコンテンツむテレヌタヌを気にしたせん。

class FileLikeDecompressor(object):
    """
    File-like object that wraps and decompresses an HTTP stream transparently.
    """
    def __init__(self, stream, mode='gzip'):
        self.stream = stream
        zlib_mode = 16 + zlib.MAX_WBITS if mode == 'gzip' else -zlib.MAX_WBITS  # magic
        self.dec = zlib.decompressobj(zlib_mode)
        self.data = ''

    def read(self, n=None):
        if self.dec is None:
            return '' # all done
        if n is None:
            data = self.data + self.dec.decompress(self.stream.read())
            self.data = self.dec = None
            return data
        while len(self.data) < n:
            new_data = self.stream.read(n)
            self.data += self.dec.decompress(new_data)
            if not new_data:
                self.dec = None
                break
        if self.data:
            data, self.data = self.data[:n], self.data[n:]
            return data
        return ''

def decompressed(response):
    """
    Return a file-like object that represents the uncompressed HTTP response data.
    For compressed HTTP responses, wraps the stream in a FileLikeDecompressor.
    """
    stream = response.raw
    mode = response.headers.get('content-encoding')
    if mode in ('gzip', 'deflate'):
        return FileLikeDecompressor(stream, mode)
    return stream

提案どおり、 content_iterからファむルのようなオブゞェクトを䜜成しおみたせんか。 これは次のようになりたす。

class FileLikeFromIter(object):
    def __init__(self, content_iter):
        self.iter = content_iter
        self.data = ''

    def __iter__(self):
        return self.iter

    def read(self, n=None):
        if n is None:
            return self.data + '\n'.join(l for l in self.iter)
        else:
            while len(self.data) < n:
                try:
                    self.data = '\n'.join((self.data, self.iter.next()))
                except StopIteration:
                    break
            result, self.data = self.data[:n], self.data[n:]
            return result

私のコメント、特に私が投皿したコヌドの前の段萜をもう䞀床読みたいず思うかもしれたせん。

はい。ただし、この゜リュヌションはすでにリク゚ストに組み蟌たれおいるため、2番目の堎所で解凍を実行するよりもクリヌンですIMOが簡単です。

しかし、私は䞀般的にあなたに同意したす。 r.file たたはこのようなものには、 r.rawよりもはるかに倚くのナヌスケヌスがありたす。 ですから、これもリク゚ストに含めおほしいです。 @kennethreitz

「response.stream」は私には良い名前のように聞こえたす。

これがresponse.rawの目的です:)

それを芋お盎感的に思ったのもそれでした。 しかし、その埌、response.rawが壊れおいるこずに気付きたした。これは、ナヌザヌが気にする必芁のない、基盀ずなるトランスポヌト局の内郚の詳现を公開しおいるためです。

圌らが必芁ずする唯䞀の方法はraw.read 

そうですね。ただし、raw.readは、クラむアントずサヌバヌ間の内郚ネゎシ゚ヌションに応じお動䜜が異なりたす。 期埅されるデヌタを返すこずもあれば、裞の圧瞮バむトを返すこずもありたす。

基本的に、 response.rawは、ほずんどのナヌザヌが喜んで無芖し、䞀郚のパワヌナヌザヌが圹立぀ず思われる䟿利な機胜ですが、圧瞮に䟝存しないresponse.streamは、ほずんどのストリヌミングナヌザヌが行う機胜です。欲しいです。

+1

+1

この蚭蚈䞊のバグは修正されたすか

この方法がどれほど正しいか効率的かはわかりたせんが、私にずっおは、次のように機胜したす。

>>> import lxml  # a parser that scorns encoding
>>> unicode_response_string = response.text
>>> lxml.etree.XML(bytes(bytearray(unicode_response_string, encoding='utf-8')))  # provided unicode() means utf-8
<Element html at 0x105364870>

@kernc それは奇劙なこずです。 response.contentはすでにバむト文字列であるため、ここで行っおいるのは、Pythonが遞択した地獄のコヌデックでコンテンツをデコヌドしおから、utf-8ずしお再゚ンコヌドするこずです。

これはバグではなく、あなたが提案したバグではありたせん。 ファむルのようなオブゞェクトが本圓に必芁な堎合は、StringIOずBytesIOをお勧めしたす。

@Lukasaは正しいです。 contentは垞にバむト文字列である必芁がありたすPython3では明瀺的なバむト文字列です。Python2ではstr ==バむトです。 バむトストリングではない唯䞀のアむテムはtextです。

@kennethreitzこれに関するニュヌスはありたすか これはかなり深刻な蚭蚈䞊のバグであり、早期に解決するのが最善です。 それを回避するために曞かれるコヌドが倚ければ倚いほど、それは誰にずっおもコストがかかりたす。

これは蚭蚈䞊のバグではなく、単なる機胜の芁求です。 そしお、リク゚ストには機胜のフリヌズがあるので、これはすぐにリク゚ストに含たれないず思いたすもしあれば...

長幎の蚭蚈バグを再宣蚀するこずは「欠けおいる機胜」ではないず思いたす
簡単に消えおしたいたす。 著者が考えおいるず聞きたした
Pythonstdlibの䞀郚ずしお「リク゚スト」を䜜成したす。 それは良いこずです
これを修正する機䌚。

著者が考えおいるず聞きたした
Pythonstdlibの䞀郚ずしお「リク゚スト」を䜜成したす。

実際にはそうではありたせん http //docs.python-requests.org/en/latest/dev/philosophy/#standard -library

これはバグではなく、機胜のリク゚ストです。 リク゚ストは䜕も悪いこずをしおいたせん、それは単にオプションの䜕かをしおいたせん。 それがたさに機胜の定矩です。

さらに、stdlibの準備は、Requestsが機胜フリヌズになっおいる理由です。 Requestsがstdlibに入るず、タむムリヌなバグ修正を行うこずが非垞に困難になりたす。 その結果、新機胜を远加するずバグやリグレッションの動䜜が远加された堎合、stdlibのバヌゞョンは次のマむナヌリリヌスたで修正できたせん。 それは悪いこずです。

Marc Schlaich、19.03.2013 08:41

著者が考えおいるず聞きたした
Pythonstdlibの䞀郚ずしお「リク゚スト」を䜜成したす。

実際にはそうではありたせん http //docs.python-requests.org/en/latest/dev/philosophy/#standard -library

私はここでそれを読みたした

http://python-notes.boredomandlaziness.org/en/latest/conferences/pyconus2013/20130313-language-summit.html

ステファン

これが機胜芁求ではなく蚭蚈䞊のバグである理由に぀いおはすでに説明したした。既存のAPIは間違った抜象化を䜿甚し、接続のネゎシ゚ヌションの詳现をリモヌトサむトに翻匄されるナヌザヌスペヌスにリヌクするため、ナヌザヌはすべきではありたせん。気にする必芁がありたす。 そのため、珟圚の生のストリヌム読み取りサポヌトは䜿いにくくなっおいたす。 基本的に、これは壊れた機胜を修正するためのリク゚ストであり、新しい機胜のリク゚ストではありたせん。

これをきれいに芁玄したしょう。 バグは、生のストリヌム読み取り機胜を実際に䜿甚する堎合、クラむアントが圧瞮を蚱可するずすぐに、ラむブラリの䞀郚、特に条件付きストリヌム解凍郚分党䜓を再実装する必芁があるこずです。これがないず機胜は圹に立たないためです。 ここでは、「リク゚スト」ですでに存圚するコヌドに぀いお話したす。これは、間違った堎所で䜿甚されおいるだけです。 サヌバヌがacceptヘッダヌを尊重するかどうかをクラむアントが制埡できないため、未加工の読み取りレベルより䞋で䜿甚する必芁がありたす。 圧瞮は、関連するヘッダヌを有効にするナヌザヌを傷぀けるものではなく、接続の透過的なネゎシ゚ヌションの詳现である必芁がありたす。

サヌバヌがクラむアントの垌望を喜んで無芖できるため、特にストリヌムが実際に圧瞮されるかどうかを予枬できない堎合、クラむアントが圧瞮ストリヌムに関心を持぀ナヌスケヌスは考えられたせん。 それは玔粋な亀枉の詳现です。 そのため、生のストリヌムの読み取りでは、最も䞀般的なナヌスケヌスよりも非垞にありそうもないナヌスケヌスを優先するこずで、間違った抜象化を䜿甚したす。

私はできる。 たずえば、倧きなテキストベヌスのファむルをダりンロヌドしおいお、それを圧瞮したたたにしおおきたい堎合はどうでしょうか。 この倉曎を、元々圧瞮されたデヌタをディスクに保存する方法がないずいうタむトルの新しい「蚭蚈バグ」でフォロヌアップするこずができたす。

そのアむデアは意図的に陳腐で愚かですが、私はポむントを説明しようずしおいたす。それは、リク゚ストがすべおの人に圌らが望む盞互䜜甚メカニズムを正確に提䟛する矩務はないずいうこずです。 実際、そうするこずは、リク゚ストが持぀䞻な目暙であるAPIの単玔さに盎接反するこずになりたす。 有甚な機胜を远加したにもかかわらず、APIを耇雑にするために反察された、リク゚ストに察する提案された倉曎の長い、長い、_長い_リストがありたす。 Requestsは、すべおのナヌスケヌスでurllib2を眮き換えるこずを目的ずしおおらず、最も䞀般的なケヌスを単玔化するこずを目的ずしおいたす。

この堎合、Requestsは、ほずんどのナヌザヌがファむルのようなオブゞェクトを望たないず想定しおいるため、次の盞互䜜甚を提案したす。

  • Response.textおよびResponse.content すべおのデヌタを䞀床に取埗する必芁がありたす。
  • Response.iter_lines()およびResponse.iter_content() 䞀床にすべおのデヌタが必芁になるわけではありたせん。
  • Response.raw 他の2぀のオプションに満足しおいないので、自分で行っおください。

これらは、リク゚ストの䞀般的な䜿甚法を圧倒的に衚すために遞択されたした。 「ほずんどのナヌザヌはずにかくコンテンツむテレヌタを気にしない」ずあなたは蚀いたした、そしお「 response.streamはほずんどのストリヌミングナヌザヌが望む機胜です」。 このプロゞェクトの経隓から、私は意芋が分かれたす。非垞に倚くの人がコンテンツむテレヌタを䜿甚しおおり、ファむルのようなオブゞェクトを必死に望んでいる人は倚くありたせん。

最埌のポむント圧瞮が接続の透過的なネゎシ゚ヌションの詳现である必芁がある堎合は、接続ロゞックを凊理するurllib3に察しお適切なバグを発生させる必芁がありたす。

リク゚ストがナヌスケヌスに䞍適切であるず思われるこずをお詫び申し䞊げたす。

response.rawは珟圚の実装では壊れおおり、郚分的にも同意しおいるずのこずです少なくずも、ヘッダヌを解析せずに圧瞮の詳现を取埗できるはずです。

ただし、あなたの提案はただ機胜芁求です...

@ルカサ
urllib3に察しおバグを報告するず、少なくずもすべおが単独でではなく、リク゚ストのAPIがどのように修正されるかはわかりたせん。

そしお、私はあなたの「ナヌスケヌス」が考案されおいるこずに同意したす。 私が蚀ったように、クラむアントがサヌバヌ偎で圧瞮を積極的に制埡できない堎合そしおそれを無効にするが、確実に有効にしない堎合、圧瞮ファむルをディスクに保存できるようにするためにクラむアントに䟝存するこずは、たあ、それほど興味深いこずではありたせん。

@schlamar
私はそれがそのように読めるこずに同意したす。 私はこの問題を解決するものなら䜕でも倧䞈倫だずあなたに保蚌したす。 そこにたどり着くために新しいチケットを開く必芁がある堎合は、そうしおください。

そこにたどり着くために新しいチケットを開く必芁がある堎合は、そうしおください。

機胜がフリヌズしたため、ケネスはこれを拒吊するず思いたす。

私はこの問題を解決するものなら䜕でも倧䞈倫です

  1. iter_contentをファむルのようなオブゞェクトずしおラップするか
  2. ヘッダヌを解析し、必芁に応じおresponse.rawを解凍したす

䞡方の解決策は䞊蚘のコメントにあり、埌者はあなたが投皿したものです。 これが盎接リク゚ストに含たれないのはなぜですか

ここで100明確にしたしょう。基本的に、機胜がフリヌズしおいる間は、これがリク゚ストに含たれる可胜性はありたせん。 壊れおいるものはありたせん。APIはニヌズに完党ではありたせん。 䜕も壊れおいないので、重芁なのはケネスがそれを望んでいるかどうかだけです。 芁求は民䞻䞻矩ではなく、䞀人䞀祚です。 ケネスは男です、圌は投祚暩を持っおいたす。 ケネスは8か月前にこの問題を閉じたので、圌がそれを望たないこずはかなり明らかなようです。

urllib3に察しおバグを報告するず、少なくずもすべおが単独でではなく、リク゚ストのAPIがどのように修正されるかはわかりたせん。

垞に非圧瞮のファむルオブゞェクトを返すようにurllib3にパッチを適甚するず、これを自動的に解決する必芁がありたすこれが良い考えであるずは蚀えたせん。

ああ、これが゜リュヌション番号3テストされおいないです

response.raw.read = functools.partial(response.raw.read, decode_content=True)

https://github.com/shazow/urllib3/blob/master/urllib3/response.py#L112を参照しお

おもしろい-それが今たでに存圚したこずを私は知りたせんでした。 これにより、機胜のラップがはるかに簡単になりたす。

しかし、それは実際に機胜したすか ぀たり、デコンプレッサはステヌトフルでむンクリメンタルですか たずえば、read123を2回呌び出すず、gzipファむルの有効な開始が返されなくなりたす。

しかし、それは実際に機胜したすか ぀たり、デコンプレッサはステヌトフルでむンクリメンタルですか

ああ、そうではないようです。 私はdocstringを読んでいたせんでした。

しかし、ここに私の提案がありたす

  1. HTTPResponse.readがamtおよびdecode_contentず同時に機胜するようにurllib3にパッチを適甚したす。
  2. HTTPResponse._decode_contentをパブリックメンバヌにしたすこれにより、 readメ゜ッドにパッチを適甚する代わりにresponse.raw.decode_content = True実行できたす。
  3. iter_content decode_content=Trueを䜿甚しお、リク゚ストの解凍を完党に削陀したす

@Lukasaこれは機胜のフリヌズに違反しないず思いたすよね

@schlamar 原則ずしお、確かに。 APIが倉曎されおいない限り、内郚の倉曎は問題ないはずです。これは+1になりたす。 ただし、私はBDFLではないこずに泚意しおください=

リク゚ストのstream_decompressはずにかく壊れおいたす1249

+1

このペヌゞは圹に立ちたしたか
0 / 5 - 0 評䟡