JSONオブジェクトにバイトを受け入れさせるか、出力文字列をurlopenさせます


177

Python 3では、URLからjsonドキュメントを要求しています。

response = urllib.request.urlopen(request)

responseオブジェクトが持つファイルライクオブジェクトreadreadlineメソッド。通常、JSONオブジェクトは、ファイルをテキストモードで開いて作成できます。

obj = json.load(fp)

私がしたいのは:

obj = json.load(response)

ただし、urlopenはバイナリモードでファイルオブジェクトを返すため、これは機能しません。

回避策はもちろんです。

str_response = response.read().decode('utf-8')
obj = json.loads(str_response)

しかし、これは気分が悪い...

バイトファイルオブジェクトを文字列ファイルオブジェクトに変換できるより良い方法はありますか?または、いずれかのパラメータurlopenまたはjson.loadエンコーディングを指定するためのパラメータが不足していますか?


2
あなたはそこにタイプミスがあると思います、「readall」は「read」でなければなりませんか?
Bob Yoplait

@BobYoplait同意する。
CaptainNemo 2017

回答:


79

HTTPはバイトを送信します。問題のリソースがテキストである場合、文字エンコードは通常、Content-Type HTTPヘッダーまたは別のメカニズム(RFC、HTML meta http-equivなど)によって指定されます。

urllib バイトを文字列にエンコードする方法を知っている必要がありますが、それはあまりにもナイーブです—それはひどくパワー​​不足でPythonでないライブラリです。

Dive Into Python 3では、状況の概要を説明しています。

「回避策」は問題ありません。間違っていると感じますが、それは正しい方法です。


6
これは「正しい」方法である可能性がありますが、Python 3について元に戻すことができることが1つあれば、このバイト/文字列のがらくたになります。組み込みライブラリ関数は、少なくとも他の組み込みライブラリ関数を処理する方法を知っていると思います。Pythonを使用する理由の1つは、シンプルで直感的な構文です。この変更は、あちこちでそれを壊します。
ThatAintWorking 2014年

4
「リクエスト」ライブラリをチェックしください。このライブラリは、このようなことを自動的に処理します。
offby1 2014

2
これは、組み込みライブラリ関数が他の関数を処理する方法を「知る」必要がある場合ではありません。JSONはオブジェクトのUTF-8表現として定義されているため、エンコーディングがわからないバイトを魔法のようにデコードすることはできません。urlopenエンコーディングを知っているので、バイト自体をデコードできるはずだと私は同意します。とにかく、私は答えとしてPython標準ライブラリソリューションを投稿しました— codecsモジュールを使用してバイトのストリーミングデコードを行うことができます。
jbg

1
@ThatAintWorking:同意しない。バイトと文字列の違いを明示的に管理しなければならないのは苦痛ですが、言語に暗黙の変換を行わせるのははるかに困難です。暗黙的なバイト<->文字列変換は多くのバグの原因であり、Python3は落とし穴を指摘するのに非常に役立ちます。しかし、図書館にはこの分野で改善の余地があることに同意します。
EvertW 2017

@EvertW失敗、私の意見では、そもそも文字列をユニコードにすることを強いています。
ThatAintWorking 2017

99

Pythonのすばらしい標準ライブラリが救いに…

import codecs

reader = codecs.getreader("utf-8")
obj = json.load(reader(response))

py2とpy3の両方で動作します。

ドキュメント:Python 2Python3


11
python 3.4.3理由がわからないときにこの回答を試したときにこのエラーが発生しましたか?エラーはTypeError: the JSON object must be str, not 'StreamReader'
アーロン・レヴィエ

9
@AronYsidoroのjson.loads()代わりに使用しましたjson.load()か?
Sleepycal

6
ボーナスポイントについては、utf-8:と仮定する代わりに、応答で指定されたエンコーディングを使用してくださいresponse.headers.get_content_charset()。戻りNoneそこにはエンコードされていない、とpython2上に存在しない場合。
Phil Frost

5
@PhilFrostそれは巧妙です。実際には、そのことに注意することは価値があるかもしれません。JSONは常に定義により常にUTF-8、UTF-16、またはUTF-32であり(圧倒的にUTF-8である可能性が高い)、そのためWebサーバーから別のエンコードが返された場合は、Webサーバーソフトウェアの構成が誤っている可能性があります。本当に非標準のJSON。
jbg 2016年

6
私がpython 3.5で使用したとき、エラーは「AttributeError: 'bytes' object has no attribute 'read'」でした
Harper Koo

66

私は質問が最良の答えであると考えました:)

import json
from urllib.request import urlopen

response = urlopen("site.com/api/foo/bar").read().decode('utf8')
obj = json.loads(response)

18

requestsライブラリを使用してこれを解決しようとしている他の人のために:

import json
import requests

r = requests.get('http://localhost/index.json')
r.raise_for_status()
# works for Python2 and Python3
json.loads(r.content.decode('utf-8'))

12
この機能は次の機能に組み込まれていrequestsます。簡単に実行できますr.json()
jbg

1
明確にします。@ jbgのメソッドを使用する場合は、行う必要はありませんjson.loads。あなたがしなければならないすべてはあなたがr.json()あなたのJSONオブジェクトをすでにdictにロードしていることです。
Blairg23 2017年

*** UnicodeEncodeError: 'ascii' codec can't encode characters in position 264-265: ordinal not in range(128)
andilabs 2018年

13

これは私のために働きます、私は人間のためのリクエストでjson()ドキュメントをチェックアウトする「リクエスト」ライブラリを使用しました

import requests

url = 'here goes your url'

obj = requests.get(url).json() 

これが最良の方法です。本当に読みやすい、そしてこのようなことをしている人は誰でも要求を持つべきです。
Baldrickk


3

フラスコのマイクロフレームワークを使用しているときにこの問題が発生した場合は、次のようにしてください。

data = json.loads(response.get_data(as_text=True))

ドキュメントから:「as_textがTrueに設定されている場合、戻り値はデコードされたUnicode文字列になります」


Flask単体テストで問題が発生したため、このページにアクセスしました-単一行の呼び出しを投稿していただきありがとうございます。
sfblackl 2017

1

あなたの回避策は実際に私を救いました。Falconフレームワークを使用したリクエストの処理で多くの問題が発生していました。これでうまくいきました。リクエストはcurl pr httpieのリクエストフォームです

json.loads(req.stream.read().decode('utf-8'))

1

これにより、バイトデータがjsonにストリーミングされます。

import io

obj = json.load(io.TextIOWrapper(response))

io.TextIOWrapperは、コーデックのモジュールリーダーよりも優先されます。https://www.python.org/dev/peps/pep-0400/


`*** AttributeError: 'Response' object has no attribute 'readable'``
andilabs

*** AttributeError: 'bytes' object has no attribute 'readable'
andilabs

urllibまたはリクエストを使用していますか?これはurllib用です。バイトオブジェクトがある場合は、単にを使用しますjson.loads(bytes_obj.decode())
コリンアンダーソン

0

jsonとしてHttpResponseコンテンツを作成するこの単純なメソッドを見つけました

import json

request = RequestFactory() # ignore this, this just like your request object

response = MyView.as_view()(request) # got response as HttpResponse object

response.render() # call this so we could call response.content after

json_response = json.loads(response.content.decode('utf-8'))

print(json_response) # {"your_json_key": "your json value"}

お役に立てば幸い


0

Python 3.6以降では、を使用json.loads()してbytesオブジェクトを直接逆シリアル化できます(エンコードはUTF-8、UTF-16、またはUTF-32である必要があります)。したがって、標準ライブラリのモジュールのみを使用して、次のことができます。

import json
from urllib import request

response = request.urlopen(url).read()
data = json.loads(response)

-2

私は以下のプログラムを使用しました json.loads()

import urllib.request
import json
endpoint = 'https://maps.googleapis.com/maps/api/directions/json?'
api_key = 'AIzaSyABbKiwfzv9vLBR_kCuhO7w13Kseu68lr0'
origin = input('where are you ?').replace(' ','+')
destination = input('where do u want to go').replace(' ','+')
nav_request = 'origin={}&destination={}&key={}'.format(origin,destination,api_key)
request = endpoint + nav_request
response = urllib.request.urlopen(request).read().decode('utf-8')
directions = json.loads(response)
print(directions)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.