Để đối tượng JSON chấp nhận byte hoặc để chuỗi đầu ra urlopen


177

Với Python 3 tôi đang yêu cầu một tài liệu json từ một URL.

response = urllib.request.urlopen(request)

Đối responsetượng là một đối tượng giống như tệp với readreadlinecác phương thức. Thông thường một đối tượng JSON có thể được tạo bằng một tệp được mở ở chế độ văn bản.

obj = json.load(fp)

Những gì tôi muốn làm là:

obj = json.load(response)

Tuy nhiên, điều này không hoạt động vì urlopen trả về một đối tượng tệp ở chế độ nhị phân.

Một công việc xung quanh là tất nhiên:

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

nhưng điều này cảm thấy tồi tệ ...

Có cách nào tốt hơn để tôi có thể chuyển đổi một đối tượng tệp byte thành một đối tượng tệp chuỗi không? Hoặc tôi đang thiếu bất kỳ tham số nào cho một urlopenhoặc json.loadđể mã hóa?


2
Tôi nghĩ rằng bạn có một lỗi đánh máy ở đó, "readall" nên được "đọc"?
Bob Yoplait

@BobYoplait Tôi đồng ý.
Thuyền trưởngNemo

Câu trả lời:


79

HTTP gửi byte. Nếu tài nguyên được đề cập là văn bản, mã hóa ký tự thường được chỉ định, theo tiêu đề HTTP Loại nội dung hoặc theo cơ chế khác (RFC, HTML meta http-equiv, ...).

urllib nên biết cách mã hóa các byte thành một chuỗi, nhưng nó quá ngây thơ, đó là một thư viện không đủ sức mạnh và không có Pythonic.

Đi sâu vào Python 3 cung cấp một cái nhìn tổng quan về tình huống này.

"Cách giải quyết" của bạn là tốt, mặc dù cảm thấy sai, đó là cách chính xác để làm điều đó.


6
Đây có thể là cách "chính xác" để làm điều đó nhưng nếu có một điều tôi có thể hoàn tác về Python 3 thì đó sẽ là byte / chuỗi crap. Bạn sẽ nghĩ rằng các chức năng thư viện tích hợp ít nhất sẽ biết cách xử lý các chức năng thư viện tích hợp khác. Một phần lý do chúng tôi sử dụng python là cú pháp trực quan đơn giản. Sự thay đổi này phá vỡ tất cả mọi nơi.
ThatAintWorking

4
Kiểm tra thư viện "yêu cầu" - nó xử lý loại điều này cho bạn một cách tự động.
off1

2
Đây không phải là trường hợp của các chức năng thư viện tích hợp cần để biết cách xử lý các chức năng khác. JSON được định nghĩa là biểu diễn UTF-8 của các đối tượng, do đó, nó không thể giải mã một cách kỳ diệu các byte mà nó không biết mã hóa. Tôi đồng ý rằng urlopenphải có khả năng giải mã các byte vì nó biết mã hóa. Dù sao, tôi đã đăng giải pháp thư viện chuẩn Python dưới dạng câu trả lời - bạn có thể thực hiện giải mã luồng byte bằng codecsmô-đun.
jbg

1
@ThatAintWorking: Tôi sẽ không đồng ý. Mặc dù việc phải quản lý sự khác biệt giữa byte và chuỗi là một nỗi đau lớn hơn nhiều, nhưng việc ngôn ngữ thực hiện một số chuyển đổi ngầm cho bạn là một nỗi đau lớn hơn nhiều. Các byte ẩn <-> chuyển đổi chuỗi là nguồn gốc của nhiều lỗi và Python3 rất hữu ích trong việc chỉ ra những cạm bẫy. Nhưng tôi đồng ý thư viện có chỗ để cải thiện trong lĩnh vực này.
EvertW

@EvertW sự thất bại, theo ý kiến ​​của tôi, nó buộc các chuỗi phải unicode ở vị trí đầu tiên.
ThatAintWorking

99

Thư viện tiêu chuẩn tuyệt vời của Python để giải cứu

import codecs

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

Hoạt động với cả py2 và py3.

Tài liệu: Python 2 , Python3


11
Tôi đã nhận được lỗi này khi thử câu trả lời này trong python 3.4.3không chắc chắn tại sao? Lỗi làTypeError: the JSON object must be str, not 'StreamReader'
Aaron Lelevier

9
@AronYsidoro Bạn có thể sử dụng json.loads()thay vì json.load()?
ngủ

6
Đối với điểm thưởng, hãy sử dụng mã hóa được chỉ định trong phản hồi, thay vì giả sử utf-8 : response.headers.get_content_charset(). Trả về Nonenếu không có mã hóa và không tồn tại trên python2.
Phil Frost

5
@PhilFrost Đó là khéo léo. Trong thực tế, nó có thể trả tiền để cẩn thận với điều đó; Định nghĩa JSON luôn là UTF-8, UTF-16 hoặc UTF-32 (và rất có khả năng là UTF-8), vì vậy nếu một mã hóa khác được máy chủ web trả về, thì đó có thể là cấu hình sai của phần mềm máy chủ web thay vì JSON thực sự không chuẩn.
jbg 22/03/2016

6
Khi tôi sử dụng trong python 3.5, lỗi là "AttributionError: 'byte' object không có thuộc tính 'read'"
Harper Koo

66

Tôi đã có ý kiến ​​rằng câu hỏi là câu trả lời tốt nhất :)

import json
from urllib.request import urlopen

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

18

Đối với bất kỳ ai khác đang cố gắng giải quyết điều này bằng requeststhư viện:

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
Chức năng này được tích hợp sẵn requests: bạn có thể thực hiện một cách đơn giảnr.json()
jbg

1
Làm rõ, nếu bạn sử dụng phương pháp của @ jbg, bạn không cần phải làm json.loads. Tất cả những gì bạn phải làm là r.json()và bạn đã tải đối tượng JSON của mình vào một lệnh.
Blairg23

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

13

Cái này hoạt động với tôi, tôi đã sử dụng thư viện 'request' với json()kiểm tra tài liệu trong các yêu cầu cho con người

import requests

url = 'here goes your url'

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

Đây la cach tôt nhât. Thực sự có thể đọc được, và bất cứ ai đang làm một cái gì đó như thế này nên có yêu cầu.
Baldrickk

6

Tôi gặp vấn đề tương tự khi sử dụng Python 3.4.3 & 3.5.2 và Django 1.11.3. Tuy nhiên, khi tôi nâng cấp lên Python 3.6.1, các vấn đề đã biến mất.

Bạn có thể đọc thêm về nó ở đây: https://docs.python.org/3/whatsnew/3.6.html#json

Nếu bạn không bị ràng buộc với một phiên bản Python cụ thể, chỉ cần xem xét nâng cấp lên 3.6 trở lên.


3

Nếu bạn đang gặp phải vấn đề này trong khi sử dụng microframework, thì bạn có thể thực hiện:

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

Từ các tài liệu : "Nếu asSphere được đặt thành True, giá trị trả về sẽ là một chuỗi unicode được giải mã"


Tôi đã truy cập trang này vì tôi gặp vấn đề với các bài kiểm tra đơn vị Flask - cảm ơn vì đã đăng cuộc gọi đường dây đơn.
sfblackl

1

Cách giải quyết của bạn thực sự chỉ cứu tôi. Tôi đã gặp rất nhiều vấn đề khi xử lý yêu cầu bằng khung Falcon. Điều này làm việc cho tôi. req là mẫu yêu cầu curl pr httpie

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

1

Điều này sẽ truyền dữ liệu byte vào json.

import io

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

io.TextIOWrapper được ưu tiên cho trình đọc mô-đun của codec. https://www.python.org/dev/peps/pep-0400/


`*** AttributionError: Đối tượng 'Phản hồi' không có thuộc tính 'readable'``
andilabs

*** AttributionError: đối tượng 'byte' không có thuộc tính 'có thể đọc được'
andilabs

Bạn đang sử dụng urllib hoặc yêu cầu? Đây là cho urllib. Nếu bạn có một đối tượng byte, chỉ cần sử dụng json.loads(bytes_obj.decode()).
Collin Anderson

0

Chỉ cần tìm phương pháp đơn giản này để biến nội dung của httpResponse thành json

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

Mong rằng sẽ giúp bạn


0

Kể từ Python 3.6, bạn có thể sử dụng json.loads()để bytesgiải tuần tự hóa một đối tượng trực tiếp (mã hóa phải là UTF-8, UTF-16 hoặc UTF-32). Vì vậy, chỉ sử dụng các mô-đun từ thư viện chuẩn, bạn có thể làm:

import json
from urllib import request

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

-2

Tôi đã sử dụng chương trình dưới đây để sử dụng 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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.