Парсинг неправильного JSON | Python

Имеется вот такая неправильная выгруженная коллекция из MongoDB:

"title": "Инструкция конец"
"createdBy": "[email protected]"
"dateCreated": ISODate("2022-08-03T12:14:09.881+0000")
"fileName": "Инструкция конец.docx"
"title": "Blueprint"
"createdBy": "[email protected]"
"dateCreated": ISODate("2022-08-03T11:59:03.109+0000")
"fileName": "Blueprint.pdf"
"title": "Contract"
"createdBy": "[email protected]"
"dateCreated": ISODate("2022-08-03T11:59:03.552+0000")
"fileName": "Contract.docx"
"title": "Marketing report"
"createdBy": "[email protected]"
"dateCreated": ISODate("2022-08-03T11:59:03.755+0000")
"fileName": "Marketing report.pptx"
"title": "Blueprint"

Легко открывается вот так:

with open('C:\base.json', "r", encoding="utf8") as file:

Нативным JSON, разумеется, не является. По сути это простой TXT. Нужно получить полную выборку, желательно в виде табличного файла, например, CSV. Требуются все(4) "ключи" и из значения:

"dateCreated": | "fileName" | "title": | "createdBy":

Будет круто, если можно будет присвоить свои ключи и подставить к ним значения, чтобы получилось подобное:

пример

Буду благодарен, если подскажите, как лучше реализовать. Получится ли такое вообще в нормальный словарь преобразовать с небольшими усилиями?


Ответы (1 шт):

Автор решения: DiMithras

Не надо называть json то что json не является. Но опустим пока эту несуразицу.
То что не парсит json, обычно довольно неплохо парсит :

import yaml
d = {}
with open('base.json', "r", encoding="utf8") as f:
    for line in f:
        for k,v in yaml.safe_load(line).items():
            d.setdefault(k,[]).append(v)
Переменная d:
{'title': ['Инструкция конец',
  'Blueprint',
  'Contract',
  'Marketing report',
  'Blueprint'],
 'createdBy': ['[email protected]', '[email protected]', '[email protected]', '[email protected]'],
 'dateCreated': ['ISODate("2022-08-03T12:14:09.881+0000")',
  'ISODate("2022-08-03T11:59:03.109+0000")',
  'ISODate("2022-08-03T11:59:03.552+0000")',
  'ISODate("2022-08-03T11:59:03.755+0000")'],
 'fileName': ['Инструкция конец.docx',
  'Blueprint.pdf',
  'Contract.docx',
  'Marketing report.pptx']}

В таблицу

Получили довольно внятный словарь, который теперь можно запихнуть в . Ну почти. В приведённом примере последнему title (Blueprint) нет соответствующих строк в других колонках, DataFrame так не собрать. Надеюсь у тебя данные более полные.
Но пока на данном этапе уберём данную оплошность:

max_rows = max(len(l) for l in d.values())
for v in d.values():
    while len(v) < max_rows:
        v.append(None)

Отлично, теперь можно собрать DataFrame.

import pandas as pd
df = pd.DataFrame.from_dict(d)

# Приводим колонку с датой к datetime
df['dateCreated'] = pd.to_datetime(df.dateCreated.str.replace(r'.*\("|"\)',''))

# Переименовываем колонки
df = df.rename(columns={'title': 'Заголовок',
                        'createdBy': 'Загрузил',
                        'dateCreated': 'Дата загрузки',
                        'fileName': 'Название файла'})

# Сортируем колонки
cols = df.columns.tolist()
cols = cols[-2:] + cols[:2]
df = df[cols]

# Длинную дату вида "2022-08-03 12:14:09.881000+00:00" приводим к короткому варианту
df['Дата загрузки'] = df['Дата загрузки'].dt.date

print(df)
Вывод:
  Дата загрузки         Название файла         Заголовок      Загрузил
0    2022-08-03  Инструкция конец.docx  Инструкция конец  [email protected]
1    2022-08-03          Blueprint.pdf         Blueprint  [email protected]
2    2022-08-03          Contract.docx          Contract  [email protected]
3    2022-08-03  Marketing report.pptx  Marketing report  [email protected]
4           NaT                   None         Blueprint          None

Дальше DataFrame можно сохранить в csv одной строчкой:

df.to_csv('out.csv', index=False)

Вместо csv можно сохранить в excel:

df.to_excel("out.xlsx") 
→ Ссылка