Как парсить JSON в Python

Введение в JSON в Python

Прежде чем приступить к парсингу JSON в Python, давайте разберемся, что такое JSON и как его использовать в Python.

Что такое JSON?

JSON, сокращенно от JavaScript Object Notation, - это легкий формат обмена данными. Он прост для чтения и записи человеком и легко парсится и генерируется машинами. Это делает его одним из самых популярных форматов данных. В частности, JSON стал "языком веба", поскольку широко используется для передачи данных между серверами и веб-приложениями через API.

Ниже приведен пример JSON:

{
  "name": "Maria Smith",
  "age": 32,
  "isMarried": true,
  "hobbies": ["reading", "jogging"],
  "address": {
    "street": "123 Main St",
    "city": "San Francisco",
    "state": "CA",
    "zip": "12345"
  },
  "phoneNumbers": [
    {
      "type": "home",
      "number": "555-555-1234"
    },
    {
      "type": "work",
      "number": "555-555-5678"
    }
  ],
  "notes": null
}

Как видно, JSON состоит из пар ключ-значение. Каждый ключ - это строка, а каждое значение может быть строкой, числом, булевой функцией, null, массивом или объектом. Несмотря на то, что JSON похож на объект JavaScript, его можно использовать в любом языке программирования, включая Python.

Как работать с JSON в Python

Python поддерживает JSON через модуль json, который является частью стандартной библиотеки Python. Это означает, что для работы с JSON в Python не нужно устанавливать какую-либо дополнительную библиотеку. Импортировать json можно следующим образом:

import json

Встроенная библиотека Python json предоставляет полный API для работы с JSON. В частности, в ней есть две ключевые функции: loads и load. Функция loads позволяет парсить данные JSON из строки. Обратите внимание, что, несмотря на то, что ее название кажется множественным, окончание "s" означает "string". Таким образом, ее следует читать как "load-s". С другой стороны, функция load предназначена для парсинга JSON-данных на байты.

С помощью этих двух методов json позволяет преобразовывать данные JSON в эквивалентные объекты Python, такие как словари и списки, и наоборот. Кроме того, модуль json позволяет создавать собственные кодировщики и декодировщики для работы с конкретными типами данных.

Продолжайте читать и узнайте, как использовать библиотеку json для разбора JSON-данных в Python!

Парсинг JSON-данных с помощью Python

Давайте рассмотрим несколько реальных примеров и научимся парсить данные JSON из различных источников в различные структуры данных Python.

Преобразование строки JSON в словарь Python

Предположим, что у вас есть некоторые данные JSON, хранящиеся в строке, и вы хотите преобразовать их в словарь Python. Вот как выглядят данные в формате JSON:

{
  "name": "iPear 23",
  "colors": ["black", "white", "red", "blue"],
  "price": 999.99,
  "inStock": true
}

А вот его строковое представление на языке Python:

smartphone_json = '{"name": "iPear 23", "colors": ["черный", "белый", "красный", "синий"], "цена": 999.99, "inStock": true}'

Для хранения длинных многострочных строк JSON следует использовать соглашение Python о тройных кавычках.

Проверить, что smartphone содержит корректную строку Python, можно с помощью следующей строки:

print(type(smartphone))

В результате будет выведено:

<class 'str'>

str означает "строка" и означает, что переменная smartphone имеет тип текстовой последовательности.

Парсируйте JSON-строку, содержащуюся в smartphone, на Python-словарь с помощью метода json.loads() следующим образом:

import json

# JSON string
smartphone_json = '{"name": "iPear 23", "colors": ["black", "white", "red", "blue"], "price": 999.99, "inStock": true}'
# from JSON string to Python dict
smartphone_dict = json.loads(smartphone_json)

# verify the type of the resulting variable
print(type(smartphone_dict)) # dict

Если выполнить этот фрагмент, то получится:

<class 'dict'>

Фантастика! Теперь smart_dict содержит корректный словарь Python!

Таким образом, для преобразования JSON-строки в словарь Python достаточно передать корректную JSON-строку в json.loads().

Теперь к полям полученного словаря можно обращаться как обычно:

product = smartphone_dict['product'] # smartphone
priced = smartphone['price'] # 999.99
colors = smartphone['colors'] # ['black', 'white', 'red', 'blue']

Следует помнить, что функция json.loads() не всегда возвращает словарь. В частности, возвращаемый тип данных зависит от входной строки. Например, если строка JSON содержит плоское значение, то оно будет преобразовано в соответствующее значение примитива Python:

import json
 
json_string = '15.5'
float_var = json.loads(json_string)

print(type(float_var)) # <class 'float'>

Аналогично, строка JSON, содержащая список массивов, превратится в список Python:

import json
 
json_string = '[1, 2, 3]'
list_var = json.loads(json_string)
print(json_string) # <class 'list'>

Посмотрите на приведенную ниже таблицу преобразования, чтобы увидеть, как значения JSON преобразуются в данные Python с помощью json:

Значение JSON Данные Python
string str
number (integer) int
number (real) float
true True
false False
null None
array list
object dict

Преобразование JSON-ответа API в словарь Python

Предположим, что вам необходимо создать API и преобразовать его JSON-ответ в словарь Python. В приведенном ниже примере мы вызовем следующую конечную точку API из проекта {JSON} Placeholder, чтобы получить некоторые фальшивые JSON-данные:

https://jsonplaceholder.typicode.com/todos/1

Этот RESTFul API возвращает приведенный ниже ответ в формате JSON:

{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}

Вы можете вызвать этот API с помощью модуля urllib из стандартной библиотеки и преобразовать полученный JSON в словарь Python следующим образом:

import urllib.request
import json

url = "https://jsonplaceholder.typicode.com/todos/1"

with urllib.request.urlopen(url) as response:
     body_json = response.read()

body_dict = json.loads(body_json)
user_id = body_dict['userId'] # 1

urllib.request.urlopen() выполняет вызов API и возвращает объект HTTPResponse. Его метод read() используется для получения тела ответа body_json, которое содержит ответ API в виде строки JSON. Наконец, эта строка может быть разобрана в Python-словарь с помощью json.loads(), как было описано выше.

Аналогичным образом можно добиться того же результата при работе с запросами:

import requests
import json

url = "https://jsonplaceholder.typicode.com/todos/1"
response = requests.get(url)

body_dict = response.json()
user_id = body_dict['userId'] # 1

Обратите внимание, что метод .json() автоматически преобразует объект ответа, содержащий данные в формате JSON, в соответствующую структуру данных Python.

Отлично! Теперь вы знаете, как разобрать JSON-ответ API на языке Python с помощью urllib и requests.

Загрузка JSON-файла в словарь Python

Предположим, что у вас есть некоторые JSON-данные, хранящиеся в файле smartphone.json, как показано ниже:

{
  "name": "iPear 23",
  "colors": ["black", "white", "red", "blue"],
  "price": 999.99,
  "inStock": true,
  "dimensions": {
    "width": 2.82,
    "height": 5.78,
    "depth": 0.30
  },
  "features": [
    "5G",
    "HD display",
    "Dual camera"
  ]
}

Ваша цель - прочитать JSON-файл и загрузить его в словарь Python. Достичь этого можно с помощью приведенного ниже фрагмента:

import json

with open('smartphone.json') as file:
  smartphone_dict = json.load(file)

print(type(smartphone_dict)) # <class 'dict'>
features = smartphone_dict['features'] # ['5G', 'HD display', 'Dual camera']

Встроенная библиотека open() позволяет загрузить файл и получить соответствующий ему файловый объект. Затем метод json.read() десериализует текстовый или двоичный файл, содержащий JSON-документ, в эквивалентный объект Python. В данном случае smartphone.json становится словарем Python.

Прекрасно, разбор JSON-файла в Python занимает всего несколько строк кода!

От JSON-данных к пользовательскому объекту Python

Теперь необходимо разобрать некоторые данные JSON в пользовательский класс Python. Вот как выглядит ваш пользовательский Python-класс Smartphone:

class Smartphone:
    def __init__(self, name, colors, price, in_stock):
        self.name = name    
        self.colors = colors
        self.price = price
        self.in_stock = in_stock

Здесь задача состоит в том, чтобы преобразовать следующую строку JSON в экземпляр смартфона:

{
  "name": "iPear 23 Plus",
  "colors": ["black", "white", "gold"],
  "price": 1299.99,
  "inStock": false
}

Для решения этой задачи необходимо создать собственный декодер. Для этого необходимо расширить класс JSONDecoder и установить в методе __init__ параметр object_hook. Присвойте ему имя метода класса, содержащего пользовательскую логику парсинга. В этом методе синтаксического анализа можно использовать значения, содержащиеся в стандартном словаре, возвращаемом функцией json.read(), для инстанцирования объекта Smartphone.

Определите пользовательский SmartphoneDecoder, как показано ниже:

import json
 
class SmartphoneDecoder(json.JSONDecoder):
    def __init__(self, object_hook=None, *args, **kwargs):
        # set the custom object_hook method
        super().__init__(object_hook=self.object_hook, *args, **kwargs)

    # class method containing the 
    # custom parsing logic
    def object_hook(self, json_dict):
        new_smartphone = Smartphone(
            json_dict.get('name'), 
            json_dict.get('colors'), 
            json_dict.get('price'),
            json_dict.get('inStock'),            
        )

        return new_smartphone

Обратите внимание, что для чтения значений словаря в пользовательском методе object_hook() следует использовать метод get(). Это позволит избежать ошибок KeyErrors при отсутствии ключа в словаре. Вместо этого будут возвращены значения None.

Теперь можно передать класс SmartphoneDecoder в параметр cls в json.loads() для преобразования JSON-строки в объект Smartphone:

import json

# class Smartphone:
# ...

# class SmartphoneDecoder(json.JSONDecoder): 
# ...

smartphone_json = '{"name": "iPear 23 Plus", "colors": ["black", "white", "gold"], "price": 1299.99, "inStock": false}'

smartphone = json.loads(smartphone_json, cls=SmartphoneDecoder)
print(type(smartphone)) # <class '__main__.Smartphone'>
name = smartphone.name # iPear 23 Plus

Аналогично можно использовать SmartphoneDecoder с json.load():

smartphone = json.load(smartphone_json_file, cls=SmartphoneDecoder)

Теперь вы знаете, как парсить данные JSON в пользовательские объекты Python!

Преобразование данных Python в JSON

Можно пойти и по другому пути и преобразовать структуры и примитивы данных Python в JSON. Это возможно благодаря функциям json.dump() и json.dumps(), которые следуют приведенной ниже таблице преобразования:

Данные Python Значение JSON
str string
int number (integer)
float number (real)
True true
False false
None null
list array
dict object
Null None

json.dump() позволяет записать строку JSON в файл, как в следующем примере:

import json

user_dict = {
    "name": "John",
    "surname": "Williams",
    "age": 48,
    "city": "New York"
}

# serializing the sample dictionary to a JSON file
with open("user.json", "w") as json_file:
    json.dump(user_dict, json_file)

Этот фрагмент сериализует переменную Python user_dict в файл user.json.

Аналогично, json.dumps() преобразует переменную Python в эквивалентную ей строку JSON:

import json

user_dict = {
    "name": "John",
    "surname": "Williams",
    "age": 48,
    "city": "New York"
}

user_json_string = json.dumps(user_dict)

print(user_json_string)

Обратите внимание, что вы также можете указать собственный кодировщик, но показ того, как это сделать, не является целью данной статьи. Для получения более подробной информации обратитесь к официальной документации.

Является ли стандартный модуль json лучшим ресурсом для парсинга JSON в Python?

Как и в случае с разбором данных, при парсинге JSON возникают проблемы, которые нельзя упускать из виду. Например, в случае недействительного, неполного или нестандартного JSON модуль Python json окажется неэффективным.

Кроме того, необходимо быть осторожным при парсинге JSON-данных из недоверенных источников. Это связано с тем, что вредоносная JSON-строка может привести к поломке парсера или потреблению большого количества ресурсов. Это лишь одна из проблем, которые должен учитывать парсер JSON на Python.

Для решения этих проблем можно использовать пользовательскую логику. В то же время это может занять слишком много времени и привести к появлению сложного и ненадежного кода. Поэтому следует обратить внимание на коммерческий инструмент, облегчающий парсинг JSON, например, Web Scraper IDE.

Web Scraping IDE создана специально для разработчиков и обладает широким набором функций для парсинга JSON-контента и не только. Этот инструмент поможет вам сэкономить массу времени и обеспечить безопасность процесса парсинга JSON. 

Заключение

Python позволяет парсить данные в формате JSON с помощью стандартного модуля json. Он предоставляет мощный API для сериализации и десериализации JSON-контента. В частности, он предлагает методы json.read() и json.reads() для работы с файлами JSON и строками JSON, соответственно. Здесь на нескольких реальных примерах вы увидели, как использовать их для парсинга JSON-данных в Python. В то же время вы поняли и ограничения такого подхода.

 

См. также: Разработка ПО, Разработка веб-приложений: Ресурсы, лучшие практики и как это сделать, Облачная разработка / интеграция.