ให้วัตถุ JSON ยอมรับไบต์หรือให้ urlopen สตริงออก


177

ด้วย Python 3 ฉันกำลังขอเอกสาร json จาก URL

response = urllib.request.urlopen(request)

responseวัตถุเป็นวัตถุไฟล์เช่นเดียวกับreadและreadlineวิธีการ โดยปกติแล้ววัตถุ 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

คำตอบ:


79

HTTP ส่งไบต์ หากทรัพยากรที่สงสัยคือข้อความการเข้ารหัสอักขระจะถูกระบุตามปกติโดยส่วนหัว HTTP เนื้อหาประเภทหรือโดยกลไกอื่น (RFC, HTML meta http-equiv, ... )

urllib ควรรู้วิธีการเข้ารหัสไบต์สตริง แต่ก็ไร้เดียงสาเกินไปมันเป็นยกเลิก Pythonic ห้องสมุด underpowered อย่างน่ากลัวและ

Dive Into Python 3ให้ภาพรวมเกี่ยวกับสถานการณ์

"การแก้ไขปัญหา" ของคุณนั้นใช้ได้ - แม้ว่ามันจะรู้สึกผิด แต่เป็นวิธีที่ถูกต้อง


6
นี่อาจเป็นวิธีที่ "ถูกต้อง" แต่ถ้ามีสิ่งหนึ่งที่ฉันสามารถยกเลิกได้เกี่ยวกับ Python 3 มันจะเป็นอึไบต์ / สตริงนี้ คุณจะคิดว่าอย่างน้อยฟังก์ชั่นห้องสมุดในตัวจะรู้วิธีจัดการกับฟังก์ชั่นห้องสมุดในตัวอื่น ๆ ส่วนหนึ่งของเหตุผลที่เราใช้ไพ ธ อนเป็นไวยากรณ์ที่เข้าใจง่าย การเปลี่ยนแปลงนี้ทำให้เกิดความเสียหาย
ThatAintWorking

4
ลองดูห้องสมุด "คำขอ" - จัดการกับเรื่องแบบนี้โดยอัตโนมัติ
offby1

2
นี่ไม่ใช่กรณีของฟังก์ชั่นห้องสมุดในตัวที่จำเป็นต้อง "รู้วิธี" เพื่อจัดการกับฟังก์ชั่นอื่น ๆ JSON หมายถึงการเป็นตัวแทนของวัตถุ UTF-8 ดังนั้นจึงไม่สามารถถอดรหัสไบต์ได้อย่างน่าอัศจรรย์โดยที่ไม่รู้การเข้ารหัส ฉันเห็นด้วยที่urlopenควรจะสามารถถอดรหัสไบต์เองเพราะรู้การเข้ารหัส อย่างไรก็ตามฉันได้โพสต์โซลูชันไลบรารี Python มาตรฐานเป็นคำตอบ - คุณสามารถทำการสตรีมถอดรหัสของ bytes โดยใช้codecsโมดูล
jbg

1
@ThatAintWorking: ฉันจะไม่เห็นด้วย ในขณะที่ความเจ็บปวดในคอต้องมีการจัดการความแตกต่างระหว่างไบต์และสตริงอย่างชัดเจนมันเป็นความเจ็บปวดที่ยิ่งใหญ่กว่าที่จะมีภาษาที่ทำให้การแปลงโดยนัยสำหรับคุณ ไบต์การแปลงสตริงโดยนัย <-> เป็นแหล่งที่มาของข้อบกพร่องจำนวนมากและ Python3 มีประโยชน์อย่างมากในการชี้จุดผิดพลาด แต่ฉันเห็นด้วยที่ห้องสมุดมีห้องพักสำหรับการปรับปรุงในพื้นที่นี้
EvertW

@ หลีกเลี่ยงความล้มเหลวในความคิดของฉันมันบังคับให้สตริงเป็น Unicode ในสถานที่แรก
ThatAintWorking

99

ห้องสมุดมาตรฐานที่ยอดเยี่ยมของ Python เพื่อช่วยเหลือ ...

import codecs

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

ทำงานได้กับทั้ง py2 และ py3

เอกสาร: Python 2 , Python3


11
ฉันได้รับข้อผิดพลาดนี้เมื่อลองคำตอบนี้โดยpython 3.4.3ไม่แน่ใจว่าทำไม ข้อผิดพลาดคือTypeError: the JSON object must be str, not 'StreamReader'
Aaron Lelevier

9
@AronYsidoro คุณอาจใช้json.loads()แทนjson.load()หรือไม่?
sleepycal

6
สำหรับคะแนนโบนัสใช้การเข้ารหัสที่ระบุไว้ในการตอบสนองแทนการสมมติ response.headers.get_content_charset()UTF-8: ส่งคืนNoneถ้าไม่มีการเข้ารหัสและไม่มีใน python2
Phil

5
@PhilFrost มันลื่น ในทางปฏิบัติมันอาจจ่ายให้ระวังด้วย; JSON มักเป็น UTF-8, UTF-16 หรือ UTF-32 ตามคำจำกัดความ (และมีแนวโน้มว่าจะเป็น UTF-8) ดังนั้นหากเว็บเซิร์ฟเวอร์ส่งคืนการเข้ารหัสอีกครั้งอาจเป็นความผิดพลาดของซอฟต์แวร์เซิร์ฟเวอร์เว็บมากกว่า JSON ที่ไม่ได้มาตรฐานอย่างแท้จริง
jbg

6
เมื่อฉันใช้ใน python 3.5 ข้อผิดพลาดคือ "AttributeError: 'bytes' object ไม่มี 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 ของคุณโหลดเข้ามาแล้ว
Blairg23

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

13

อันนี้ใช้ได้กับฉันฉันใช้ไลบรา 'คำขอ' พร้อมjson()ตรวจสอบเอกสารตามคำขอของมนุษย์

import requests

url = 'here goes your url'

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

นี่เป็นวิธีที่ดีที่สุด สามารถอ่านได้จริงๆและทุกคนที่ทำสิ่งนี้ควรมีคำขอ
Baldrickk

6

ฉันพบปัญหาที่คล้ายกันโดยใช้ Python 3.4.3 & 3.5.2 และ Django 1.11.3 อย่างไรก็ตามเมื่อฉันอัพเกรดเป็น Python 3.6.1 ปัญหาก็หมดไป

คุณสามารถอ่านเพิ่มเติมได้ที่นี่: https://docs.python.org/3/whatsnew/3.6.html#json

หากคุณไม่ได้เชื่อมโยงกับ Python รุ่นใดรุ่นหนึ่งให้ลองอัปเกรดเป็น 3.6 หรือใหม่กว่า


3

หากคุณประสบปัญหานี้ในขณะที่ใช้งานกล้องขนาดเล็กคุณก็สามารถทำสิ่งต่อไปนี้

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

จากเอกสาร : "ถ้า as_text ถูกตั้งค่าเป็น True ค่าส่งคืนจะเป็นสตริง Unicode ที่ถอดรหัส"


ฉันมาที่หน้านี้เพราะฉันมีปัญหากับการทดสอบหน่วย Flask - ขอบคุณสำหรับการโพสต์สายเดียว
sfblackl

1

วิธีแก้ปัญหาของคุณเพียงแค่ช่วยฉัน ฉันมีปัญหามากมายในการประมวลผลคำขอโดยใช้เฟรมเวิลเหยี่ยว สิ่งนี้ใช้ได้สำหรับฉัน ต้องเป็นแบบฟอร์มคำขอ 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: วัตถุ 'การตอบสนอง' ไม่มีแอตทริบิวต์ 'ที่อ่านได้' '
andilabs

*** AttributeError: วัตถุ 'ไบต์' ไม่มีแอตทริบิวต์ 'อ่านได้'
andilabs

คุณใช้ urllib หรือคำขออยู่หรือไม่? นี่สำหรับ urllib json.loads(bytes_obj.decode())ถ้าคุณมีวัตถุไบต์ใช้เพียง
Collin Anderson

0

เพิ่งค้นพบวิธีการง่ายๆนี้เพื่อทำให้เนื้อหา HttpResponse เป็น 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"}

หวังว่าจะช่วยคุณ


0

ในฐานะของ Python 3.6 คุณสามารถใช้json.loads()เพื่อ deserialize 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.