Практическое руководство парсинг JSON в Python

Парсинг JSON в Python

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

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

Стандарт JSON стал все более популярным в последние годы. Это простой и гибкий способ представления данных, который легко понимается и обрабатывается как людьми, так и машинами. JSON состоит из пар «ключ-значение», заключенных в фигурные скобки и разделенных двоеточием.

Python предоставляет различные инструменты, библиотеки и методы для парсинга JSON в Python и управления данными JSON, что делает его популярным выбором для аналитиков данных, веб-разработчиков и специалистов по данным.

В этом руководстве мы рассмотрим синтаксис и типы данных JSON, а также библиотеки и методы Python, используемые для парсинга JSON в Python, включая более продвинутые варианты, такие как JMESPath и ChompJS, которые очень полезны для сбора данных из интернета.

Чтение JSON

Одной из наиболее распространенных задач при работе с данными JSON является чтение их содержимого. Python предоставляет несколько встроенных библиотек для чтения JSON из файлов, API и веб-приложений. Для чтения JSON-данных можно использовать встроенный модуль json (JSON Encoder и Decoder) в Python.

Модуль json предоставляет два метода: loads и load, которые позволяют выполнять парсинг JSON в Python: строк JSON и файлов JSON, соответственно, для преобразования JSON в объекты Python, такие как списки и словари. Далее приведен пример того, как преобразовать строку JSON в объект Python с помощью метода loads.

import json
json_input = '{ "make": "Tesla", "model": "Model 3", "year": 2022, "color": "Red" }'
json_data = json.loads(json_input)
print(json_data) # Вывод: {'make': 'Tesla', 'model': 'Model 3', 'year': 2022, 'color': 'Red'}

content_copydownloadUse code with caution.Python

Далее мы покажем пример использования метода load. Дан файл JSON:

{
    "make": "Tesla",
    "model": "Model 3",
    "year": 2022,
    "color": "Red"
}

content_copydownloadUse code with caution.Json

Мы загружаем данные, используя менеджер контекста with open() и json.load(), чтобы загрузить содержимое JSON-файла в словарь Python.

import json
with open('data.json') as f:
    json_data = json.load(f)
print(json_data)  # Вывод: {'make': 'Tesla', 'model': 'Model 3', 'year': 2022, 'color': 'Red'}

content_copydownloadUse code with caution.Python

Парсинг JSON в Python: анализ данных

После загрузки JSON-данных в Python вы можете получить доступ к определенным элементам данных, используя ключи, предоставленные в структуре JSON. В JSON данные обычно хранятся в массиве или объекте. Для доступа к данным внутри массива JSON можно использовать индексацию массива, а для доступа к данным внутри объекта можно использовать пары «ключ-значение».

import json
json_string ='{"numbers": [1, 2, 3], "car": {"model": "Model X", "year": 2022}}'
json_data = json.loads(json_string)
# Доступ к элементам массива JSON с использованием индексации массива
print(json_data['numbers'][0]) # Вывод: 1
# Доступ к элементам JSON с использованием ключей
print(json_data['car']['model']) # Вывод: Model X

content_copydownloadUse code with caution.Python

В приведенном выше примере внутри структуры JSON есть объект car, который содержит два сопоставления (model и year). Это пример вложенной структуры JSON, где один объект содержится внутри другого объекта. Для доступа к элементам внутри вложенных структур JSON требуется использовать несколько ключей или индексов для прохода по структуре при парсинге JSON в Python.

Взаимозаменяемость объектов JSON и Python

JSON — это строковый формат, используемый для обмена данными, который имеет схожий синтаксис с синтаксисом литералов объектов-словарей Python. Однако важно помнить, что JSON — это не то же самое, что словарь Python. При загрузке JSON-данных в Python они преобразуются в объект Python, обычно словарь или список, и ими можно управлять с помощью стандартных методов объектов Python. Когда данные готовы к сохранению, вам потребуется преобразовать их обратно в формат JSON с помощью функции json.dumps. Важно помнить об этом различии между двумя форматами при парсинге JSON в Python.

Изменение данных JSON

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

Добавление элемента

Чтобы добавить элемент, вы можете изменить соответствующее сопоставление в объекте JSON, используя стандартный синтаксис словаря. Например:

import json
json_string = '{"model": "Model X", "year": 2022}'
json_data = json.loads(json_string)
json_data['color'] = 'red'
print(json_data) # Вывод: {'model': 'Model X', 'year': 2022, 'color': 'red'}

content_copydownloadUse code with caution.Python

Обновление элемента

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

import json
json_string = '{"model": "Model X", "year": 2022}'
json_data = json.loads(json_string)
json_data['year'] = 2023
print(json_data) # Вывод: {'model': 'Model X', 'year': 2023}

content_copydownloadUse code with caution.Python

Другой подход к добавлению и/или обновлению значений в словаре Python — использование метода update(). Он добавляет или обновляет элементы в словаре, используя значения из другого словаря или из итерируемого объекта, содержащего пары «ключ-значение».

import json
json_string = '{"model": "Model X", "year": 2022}'
json_data = json.loads(json_string)
more_json_string = '{"model": "Model S", "color": "Red"}'
more_json_data = json.loads(more_json_string)
json_data.update(more_json_data)
print(json_data) # Вывод: {'model': 'Model S', 'year': 2022, 'color': 'Red'}

content_copydownloadUse code with caution.Python

Удаление элемента

Чтобы удалить элемент из объекта JSON, можно использовать ключевое слово del для удаления соответствующего значения при парсинге JSON в Python.

import json
json_string = '{"model": "Model X", "year": 2022}'
json_data = json.loads(json_string)
del json_data['year']

content_copydownloadUse code with caution.Python

Другой подход к удалению элемента из словаря с данными JSON — использование метода pop, который позволяет получить значение и использовать его одновременно с удалением.

import json
json_string = '{"model": "Model X", "year": 2022}'
json_data = json.loads(json_string)
year = json_data.pop('year')
print(year) # Вывод: 2022
print(json_data) # Вывод: {'model': 'Model X'}

content_copydownloadUse code with caution.Python

Будьте осторожны, попытка удалить элемент с помощью del, когда элемента нет, вызовет исключение KeyError. Метод pop, с другой стороны, вернет None, если не найдет ключ. Способы использования del, когда вы не уверены в наличии ключа, — это либо проверить, существует ли ключ, при парсинге JSON в Python.

import json
json_string = '{"model": "Model X", "year": 2022}'
json_data = json.loads(json_string)
if 'year' in json_data:
    del json_data['year']
else:
    print('Ключ не найден')
# или обернуть операцию del блоком try/catch
json_string = '{"model": "Model X", "year": 2022}'
json_data = json.loads(json_string)
try:
    del json_data['year']
except KeyError:
    print('Ключ не найден')

content_copydownloadUse code with caution.Python

Обработка ошибок Python: проверять или просить?

Когда дело доходит до обработки ошибок в Python, есть два метода: «проверяй перед тем, как прыгать» и «проси прощения». Первый включает проверку состояния программы перед выполнением каждой операции, а второй пытается выполнить операцию и перехватывает любые исключения в случае сбоя при парсинге JSON в Python.

Подход «проси прощения» чаще используется в Python и предполагает, что ошибки являются обычной частью потока программы. Этот подход обеспечивает корректный способ обработки ошибок, делая код более легким для чтения и написания. Хотя он может быть менее эффективным, чем подход «проверяй перед тем, как прыгать», обработка исключений в Python оптимизирована для него, и разница в производительности обычно незначительна при парсинге JSON в Python.

Сохранение JSON

После изменения предыдущего JSON-файла или JSON-строки вы можете захотеть сохранить измененные данные обратно в JSON-файл или экспортировать их как JSON-строку для хранения данных. Метод json.dump() позволяет сохранить JSON-объект в файл, а json.dumps() возвращает строковое представление JSON-объекта.

Сохранение JSON в файл с помощью json.dump() и менеджера контекста with open() с настройкой режима записи («w»):

import json
data = '{"model": "Model X", "year": 2022}'
# Сохраняет словарь с именем data как объект JSON в файл data.json
with open("data.json", "w") as f:
    json.dump(data, f)

content_copydownloadUse code with caution.Python

Преобразование объекта Python в JSON-строку с помощью json.dumps():

import json
data = {"model": "Model X", "year": 2022}
# Преобразует словарь data в строковое представление JSON
json_string = json.dumps(data)
print(json_string) # Вывод: {"model": "Model X", "year": 2022}

content_copydownloadUse code with caution.Python

Продвинутые методы парсинга JSON в Python

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

JMESPath

JMESPath — это язык запросов, разработанный для работы с данными JSON. Он позволяет извлекать определенные части структуры JSON на основе поискового запроса. JMESPath хорошо подходит для продвинутых задач парсинга JSON в Python, поскольку он может с легкостью обрабатывать сложные, вложенные структуры JSON. В то же время он прост в использовании на начальном уровне, что делает его доступным инструментом для всех, кто работает с данными JSON.

Вот пример использования библиотеки jmespath в Python для извлечения данных:

import json
import jmespath
json_string = '{"numbers": [1, 2, 3], "car": {"model": "Model X", "year": 2022}}'
json_data = json.loads(json_string)
# Доступ к вложенному JSON
name = jmespath.search('car.model', json_data) # Результат: Model X
# Получение первого числа из numbers
first_number = jmespath.search('numbers[0]', json_data) # Результат: 1

content_copydownloadUse code with caution.Python

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

Предположим, у нас есть массив JSON с объектами автомобилей, каждый из которых содержит такую информацию, как марка автомобиля, модель, год выпуска и цена:

cars = [
    {"make": "Toyota", "model": "Corolla", "year": 2018, "price": 15000},
    {"make": "Honda", "model": "Civic", "year": 2020, "price": 20000},
    {"make": "Ford", "model": "Mustang", "year": 2015, "price": 25000},
    {"make": "Tesla", "model": "Model S", "year": 2021, "price": 50000}
]

content_copydownloadUse code with caution.Python

Мы можем использовать JMESPath для фильтрации этого списка и возврата только тех автомобилей, которые находятся в определенном ценовом диапазоне, и преобразовать результат в новую структуру, которая содержит только марку, модель и год выпуска автомобиля при парсинге JSON в Python:

import jmespath
result = jmespath.search("""
    [?price <= `25000`].{
        Make: make,
        Model: model,
        Year: year
    }
""", cars)

content_copydownloadUse code with caution.Python

Вывод этого кода будет следующим:

[
    {'Make': 'Toyota', 'Model': 'Corolla', 'Year': 2018},
    {'Make': 'Honda', 'Model': 'Civic', 'Year': 2020},
    {'Make': 'Ford', 'Model': 'Mustang', 'Year': 2015}
]

content_copydownloadUse code with caution.Python

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

JMESPath доступен не только для Python, но и для многих других языков программирования, таких как Java и Ruby. Чтобы узнать больше о JMESPath и его функциях, посетите официальный веб-сайт.

ChompJS

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

  • «{‘a’: ‘b’}» — не является допустимым JSON, поскольку для заключения в кавычки используется символ ‘.
  • ‘{a: «b»}’ — не является допустимым JSON, поскольку имя свойства не заключено в кавычки.
  • ‘{«a»: [1, 2, 3,]}’— не является допустимым JSON, поскольку в конце массива есть лишний символ ,.
  • ‘{«a»: .99}’ — не является допустимым JSON, поскольку в значении с плавающей запятой отсутствует ведущий 0.

Библиотека chompjs была разработана для обхода этого ограничения и позволяет преобразовывать такие объекты JavaScript в правильные словари Python при парсинге JSON в Python:

import chompjs
chompjs.parse_js_object("{'a': 'b'}") # Вывод: {u'a': u'b'}
chompjs.parse_js_object('{a: "b"}') # Вывод: {u'a': u'b'}
chompjs.parse_js_object('{"a": [1, 2, 3,]}') # Вывод: {u'a': [1, 2, 3]}

content_copydownloadUse code with caution.Python

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

Одним из преимуществ использования chompjs по сравнению с json.loads является то, что он может обрабатывать более широкий спектр объектов JavaScript. Например, chompjs может обрабатывать объекты, которые используют одинарные кавычки вместо двойных для имен свойств и значений. Он также может обрабатывать объекты, которые имеют лишние запятые в конце массивов или объектов при парсинге JSON в Python.

Работа с пользовательскими объектами Python

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

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

Одним из примеров пользовательского объекта Python является класс Car:

class Car:
    def __init__(self, make, model, year, price):
        self.make = make
        self.model = model
        self.year = year
        self.price = price

content_copydownloadUse code with caution.Python

Чтобы создать новый объект Car, мы можем просто вызвать конструктор Car с соответствующими аргументами при парсинге JSON в Python:

car = Car("Toyota", "Camry", 2022, 25000)

content_copydownloadUse code with caution.Python

Если мы попытаемся сериализовать объект Car как есть, мы получим TypeError:

car_json = json.dumps(car)
# TypeError: Объект типа 'Car' не является сериализуемым для JSON

content_copydownloadUse code with caution.Python

Эта ошибка возникает из-за того, что json.dumps() не знает, как сериализовать наш объект Car. По умолчанию модуль json в Python может сериализовать только определенные типы объектов, такие как строки, числа и списки/словари. Чтобы сериализовать наш объект Car в строку JSON, нам нужно создать пользовательский класс кодирования для парсинга JSON в Python.

Кодирование

Мы можем создать собственный кодировщик, унаследовав его от json.JSONEncoder и переопределив метод default. Это позволяет нам преобразовывать объекты Python в строки JSON. Метод default вызывается кодировщиком JSON для объектов, которые не являются сериализуемыми по умолчанию при парсинге JSON в Python.

import json
class CarEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, Car):
            return {"make": obj.make, "model": obj.model, "year": obj.year, "price": obj.price}
        return super().default(obj)

content_copydownloadUse code with caution.Python

Внутри метода default мы проверяем, является ли кодируемый объект экземпляром класса Car. Если да, мы возвращаем словарь с атрибутами. Если это не экземпляр класса Car, мы вызываем метод default родительского класса для обработки кодирования при парсинге JSON в Python.

car = Car("Toyota", "Camry", 2022, 25000)
car_json = CarEncoder().encode(car)
print(car_json) # Вывод: {"make": "Toyota", "model": "Camry", "year": 2022, "price": 25000}

content_copydownloadUse code with caution.Python

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

Декодирование

Точно так же, как мы можем использовать пользовательские классы кодирования для сериализации пользовательских объектов в JSON, мы также можем использовать пользовательские классы декодирования для декодирования строк JSON обратно в наши пользовательские объекты при парсинге JSON в Python.

В текущем состоянии нашего CarEncoder мы не занимаемся декодированием объекта обратно в его пользовательский объект. Если мы используем метод decode, мы получим только словарь со значениями, а не объект Car.

car_json = '{"make": "Toyota", "model": "Camry", "year": 2022, "price": 25000}'
car_dict = json.loads(car_json)
print(car_dict) # Вывод: {"make": "Toyota", "model": "Camry", "year": 2022, "price": 25000}

content_copydownloadUse code with caution.Python

Как видите, вывод — это просто словарь с атрибутами объекта Car. Если мы хотим преобразовать этот словарь обратно в объект Car, нам нужно создать собственный класс декодера для использования в методе json.loads() при парсинге JSON в Python.

Добавление метаданных

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

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

if isinstance(obj, Car):
    return {"make": obj.make, "model": obj.model, "year": obj.year, "price": obj.price}

content_copydownloadUse code with caution.Python

Добавим к нашему предыдущему CarEncoder метаданные типа при парсинге JSON в Python:

if isinstance(obj, Car):
    return {"__type__": "Car", "make": obj.make, "model": obj.model, "year": obj.year, "price": obj.price}

content_copydownloadUse code with caution.Python

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

car = Car("Toyota", "Camry", 2022, 25000)
car_json = json.dumps(car, cls=CarEncoder)
print(car_json) # Вывод: {"__type__": "Car", "make": "Toyota", "model": "Camry", "year": 2022, "price": 25000}

content_copydownloadUse code with caution.Python

Вот класс CarDecoder, который позволит нам передавать данные в виде строки JSON и возвращать пользовательский объект Python при парсинге JSON в Python.

class CarDecoder(json.JSONDecoder):
    def __init__(self, *args, **kwargs):
        super().__init__(object_hook=self.object_hook, *args, **kwargs)

    def object_hook(self, dct):
        if '__type__' in dct and dct['__type__'] == 'Car':
            return Car(dct['make'], dct['model'], dct['year'], dct['price'])
        return dct

content_copydownloadUse code with caution.Python

Затем мы можем использовать CarDecoder в методе json.loads() в качестве параметра cls при парсинге JSON в Python.

car_json = '{"__type__": "Car", "make": "Toyota", "model": "Camry", "year": 2022, "price": 25000}'
car = json.loads(car_json, cls=CarDecoder)
print(car.make)   # Вывод: "Toyota"
print(car.model)  # Вывод: "Camry"
print(car.year)   # Вывод: 2022
print(car.price)  # Вывод: 25000

content_copydownloadUse code with caution.Python

Заключение

В этом руководстве мы рассмотрели основы чтения и парсинга JSON в Python, а также способы доступа к данным JSON и их изменения с помощью встроенного пакета json в Python. Мы также обсудили более продвинутые варианты парсинга JSON в Python, такие как JMESPath и ChompJS, которые полезны для сбора данных из интернета. Благодаря знаниям, полученным из этого руководства, вы сможете эффективно работать с данными JSON в Python и интегрировать их в свой рабочий процесс разработчика. Если у вас есть вопросы, <a href=»mailto:info@datalopata.ru»>свяжитесь с нами</a>.