JSON nesnesinin bayt kabul etmesine veya urlopen çıktı dizelerinin kullanılmasına izin ver


177

Python 3 ile bir URL'den bir json belgesi talep ediyorum.

response = urllib.request.urlopen(request)

responseNesne sahip bir dosya benzeri nesnedir readve readlinemetotları. Normalde bir JSON nesnesi, metin modunda açılmış bir dosya ile oluşturulabilir.

obj = json.load(fp)

Ne yapmak istiyorum:

obj = json.load(response)

Ancak urlopen ikili modda bir dosya nesnesini döndürdüğü için bu çalışmaz.

Bir çözüm elbette:

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

ama bu kötü hissettiriyor ...

Bir bayt dosya nesnesini bir dize dosya nesnesine dönüştürmek için daha iyi bir yolu var mı? Ya da herhangi bir parametre eksik urlopenveya json.loadbir kodlama vermek için?


2
Orada bir yazım hatası olduğunu düşünüyorum, "readall" "okunmalı" olmalı?
Bob Yoplait

@BobYoplait katılıyorum.
CaptainNemo

Yanıtlar:


79

HTTP bayt gönderir. Söz konusu kaynak metinse, karakter kodlaması normalde İçerik Türü HTTP üstbilgisi veya başka bir mekanizma (bir RFC, HTML meta http-equiv, ...) tarafından belirtilir.

urllib gereken bir dizeye bayt kodlamak için biliyorum, ama çok naif-öyle bir korkunç yeterince güçlü ve un-Pythonic Kütüphane.

Python 3'e Dalış , durum hakkında genel bir bakış sunar.

"Geçici çözüm" iyi - yanlış hissettirse de, bunu yapmanın doğru yolu.


6
Bu yapmak için "doğru" yolu olabilir ama Python 3 hakkında geri alabilir bir şey olsaydı bu bayt / dizeleri bok olurdu. Yerleşik kütüphane işlevlerinin en azından diğer yerleşik kütüphane işlevleriyle nasıl başa çıkacağını bileceğini düşünürsünüz. Python kullanmamızın bir nedeni basit sezgisel sözdizimidir. Bu değişiklik her yeri kırar.
ThatAintWorking

4
"İstekler" kütüphanesine göz atın - bu tür şeyleri sizin için otomatik olarak işler.
offby1

2
Bu, diğer işlevlerle nasıl başa çıkacağını bilmesi gereken yerleşik kütüphane işlevlerinin bir örneği değildir. JSON, nesnelerin UTF-8 temsili olarak tanımlanır, bu nedenle kodlamasını bilmediği baytları sihirli bir şekilde çözemez. urlopenKodlamayı bildiği için baytların kendisini deşifre edebilmesi gerektiğine katılıyorum . Her neyse, Python standart kütüphane çözümünü bir cevap olarak gönderdim - codecsmodülü kullanarak baytların akış kod çözme işlemini yapabilirsiniz .
jbg

1
@ThatAintWorking: Kabul etmem. Boyunda ve dizeler arasındaki farkı açıkça yönetmek zorunda kalmak boyundaki bir acı olsa da, dilin sizin için örtük bir dönüşüm yapması çok daha büyük bir acıdır. Örtük bayt <-> dize dönüşümleri birçok hatanın kaynağıdır ve Python3 tuzaklara dikkat çekmekte çok yardımcı olur. Ancak kütüphanenin bu alanda iyileştirilmesi gereken bir alanı olduğunu kabul ediyorum.
EvertW

@Bu başarısızlık, bence, dizeleri unicode olmaya zorlar.
ThatAintWorking

99

Python'un kurtarmaya yönelik harika standart kütüphanesi…

import codecs

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

Hem py2 hem de py3 ile çalışır.

Dokümanlar: Python 2 , Python3


11
Bu cevabı denerken python 3.4.3neden bu yüzden emin değilim bu hatayı aldım ? HataTypeError: the JSON object must be str, not 'StreamReader'
Aaron Lelevier

9
@AronYsidoro json.loads()Bunun yerine kullandınız json.load()mı?
sleepycal

6
Bonus puanları için, utf-8: varsayımı yerine yanıtta belirtilen kodlamayı kullanın response.headers.get_content_charset(). İade Nonehiçbir kodlama ve python2 üzerinde yoksa.
Phil Frost

5
@PhilFrost Bu kaygan. Uygulamada buna dikkat etmek faydalı olabilir; JSON her zaman tanım gereği UTF-8, UTF-16 veya UTF-32'dir (ve ezici bir şekilde UTF-8 olması muhtemeldir), bu nedenle web sunucusu tarafından başka bir kodlama döndürülürse, büyük olasılıkla web sunucusu yazılımının yanlış yapılandırılması yerine gerçekten standart olmayan JSON.
jbg

6
Python 3.5 içinde kullandığımda, hata "AttributeError: 'bytes' nesnesi 'read' özniteliği yok
Harper Koo

66

Sorunun en iyi cevap olduğunu düşünmeye başladım :)

import json
from urllib.request import urlopen

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

18

Bunu requestskütüphaneyi kullanarak çözmeye çalışan herkes için :

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
Bu işlevsellik yerleşiktir requests: şunları yapabilirsinizr.json()
jbg

1
Açıkla, @ jbg yöntemini kullanırsanız, yapmanız gerekmez json.loads. Yapmanız gereken tek şey r.json()JSON nesnenizi zaten bir diksiyona yüklediniz.
Blairg23

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

13

Bu benim için çalışıyor, insanlar için isteklerdejson() dokümanı kontrol etmek için 'istek' kütüphanesini kullandım

import requests

url = 'here goes your url'

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

Bu en iyi yol. Gerçekten okunabilir ve böyle bir şey yapan herkesin istekleri olmalı.
Baldrickk

6

Python 3.4.3 ve 3.5.2 ve Django 1.11.3 kullanarak benzer sorunlarla karşılaştım. Ancak, Python 3.6.1'e yükseltme yaptığımda sorunlar ortadan kalktı.

Bununla ilgili daha fazla bilgiyi buradan edinebilirsiniz: https://docs.python.org/3/whatsnew/3.6.html#json

Belirli bir Python sürümüne bağlı değilseniz, 3.6 veya sonraki bir sürüme yükseltmeyi düşünün.


3

Şişe mikroçerçevesini kullanırken bu sorunu yaşıyorsanız, şunları yapabilirsiniz:

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

Dokümanlar'dan : "as_text True olarak ayarlanırsa, dönüş değeri kodu çözülmüş bir unicode dizesi olur"


Bu sayfaya geldim çünkü Flask birim testleri ile ilgili bir sorunum vardı - tek hat çağrısını gönderdiğiniz için teşekkürler.
sfblackl

1

Geçici çözümün beni kurtardı. Falcon çerçevesini kullanarak isteği işleme konusunda çok fazla sorun yaşıyordum. Bu benim için çalıştı. req istek formu olmak curl pr httpie

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

1

Bu, bayt verilerini json'a aktaracaktır.

import io

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

Kodek modül okuyucuya io.TextIOWrapper tercih edilir. https://www.python.org/dev/peps/pep-0400/


`` AttributeError: 'Response' nesnesinin 'okunabilir' özelliği yok
andilabs

*** AttributeError: 'bayt' nesnesinin 'okunabilir' özelliği yok
andilabs

Urllib veya istek kullanıyor musunuz? Bu urllib içindir. Bayt nesneniz varsa kullanın json.loads(bytes_obj.decode()).
Collin Anderson

0

HttpResponse içeriğini json yapmak için bu basit yöntemi buldum

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"}

Umarım sana yardımcı olur


0

Python 3.6'dan itibaren, json.loads()bir bytesnesnenin serisini tamamen kaldırmak için kullanabilirsiniz (kodlama UTF-8, UTF-16 veya UTF-32 olmalıdır). Bu nedenle, yalnızca standart kitaplıktaki modülleri kullanarak şunları yapabilirsiniz:

import json
from urllib import request

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

-2

Aşağıdaki programı kullanmak için kullandım 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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.