54

I want to manipulate the information at THIS url. I can successfully open it and read its contents. But what I really want to do is throw out all the stuff I don't want, and to manipulate the stuff I want to keep.

Is there a way to convert the string into a dict so I can iterate over it? Or do I just have to parse it as is (str type)?

from urllib.request import urlopen

url = 'http://www.quandl.com/api/v1/datasets/FRED/GDP.json'
response = urlopen(url)

print(response.read()) # returns string with info
Colton Allen
  • 2,940
  • 2
  • 23
  • 33

5 Answers5

96

When I printed response.read() I noticed that b was preprended to the string (e.g. b'{"a":1,..). The "b" stands for bytes and serves as a declaration for the type of the object you're handling. Since, I knew that a string could be converted to a dict by using json.loads('string'), I just had to convert the byte type to a string type. I did this by decoding the response to utf-8 decode('utf-8'). Once it was in a string type my problem was solved and I was easily able to iterate over the dict.

I don't know if this is the fastest or most 'pythonic' way of writing this but it works and theres always time later of optimization and improvement! Full code for my solution:

from urllib.request import urlopen
import json

# Get the dataset
url = 'http://www.quandl.com/api/v1/datasets/FRED/GDP.json'
response = urlopen(url)

# Convert bytes to string type and string type to dict
string = response.read().decode('utf-8')
json_obj = json.loads(string)

print(json_obj['source_name']) # prints the string with 'source_name' key
buræquete
  • 14,226
  • 4
  • 44
  • 89
Colton Allen
  • 2,940
  • 2
  • 23
  • 33
  • I ama getting `json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)` error – alper May 31 '23 at 13:18
28

You can also use python's requests library instead.

import requests

url = 'http://www.quandl.com/api/v1/datasets/FRED/GDP.json'    
response = requests.get(url)    
dict = response.json()

Now you can manipulate the "dict" like a python dictionary.

Paul Roub
  • 36,322
  • 27
  • 84
  • 93
Shaurya Mittal
  • 764
  • 1
  • 8
  • 19
10

json works with Unicode text in Python 3 (JSON format itself is defined only in terms of Unicode text) and therefore you need to decode bytes received in HTTP response. r.headers.get_content_charset('utf-8') gets your the character encoding:

#!/usr/bin/env python3
import io
import json
from urllib.request import urlopen

with urlopen('https://httpbin.org/get') as r, \
     io.TextIOWrapper(r, encoding=r.headers.get_content_charset('utf-8')) as file:
    result = json.load(file)
print(result['headers']['User-Agent'])

It is not necessary to use io.TextIOWrapper here:

#!/usr/bin/env python3
import json
from urllib.request import urlopen

with urlopen('https://httpbin.org/get') as r:
    result = json.loads(r.read().decode(r.headers.get_content_charset('utf-8')))
print(result['headers']['User-Agent'])
Community
  • 1
  • 1
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • In Python 3, use `r.msg.get_content_charset`. https://docs.python.org/3/library/http.client.html#http.client.HTTPResponse.msg – Peppe L-G Apr 19 '16 at 10:54
  • @PeppeL-G: from the `HTTPResponse` source: *"`headers` is used here and supports `urllib`. `msg` is provided as a backwards compatibility layer for http clients."* – jfs Apr 19 '16 at 11:05
  • Oh, sorry, I don't have much experience of Python, but you're probably right. I was working with the `HTTPResponse` class from `http.client` module, and I now see that there are some differences (this class contains both the `msg` field and the `headers` field (same value), but I only found documentation for the `msg` field, so I assumed `headers` was kept for backwards compatibility. My mistake. – Peppe L-G Apr 19 '16 at 11:53
  • @PeppeL-G It is probably a bug in the docs because `headers` is a better name for an attribute that stores HTTP headers than `msg`. If you think others can have the same issue; you could [submit a simple documentation patch](https://docs.python.org/devguide/docquality.html), to mention that `headers` can be used and `msg` exists for backwards compatibility. – jfs Apr 19 '16 at 13:35
2

TL&DR: When you typically get data from a server, it is sent in bytes. The rationale is that these bytes will need to be 'decoded' by the recipient, who should know how to use the data. You should decode the binary upon arrival to not get 'b' (bytes) but instead a string.

Use case:

import requests    
def get_data_from_url(url):
        response = requests.get(url_to_visit)
        response_data_split_by_line = response.content.decode('utf-8').splitlines()
        return response_data_split_by_line

In this example, I decode the content that I received into UTF-8. For my purposes, I then split it by line, so I can loop through each line with a for loop.

FlyingV
  • 2,207
  • 19
  • 18
-1

I guess things have changed in python 3.4. This worked for me:

print("resp:" + json.dumps(resp.json()))
Ajay Gautam
  • 997
  • 12
  • 14
  • 4
    there is no `json` attribute. Don't confuse `requests` library and `urllib.request`. – jfs Oct 31 '15 at 16:07