Python - скрипт считывает одно значение из json и 200 раз записывает его в csv.

Я пишу сценарий Python, который считывает данные из нескольких файлов JSON и записывает их в один выходной файл CSV. Я написал код, но он неправильный. Я отформатировал здесь JSON для простоты, иначе он находится в одной строке. Каждый «requestId» содержит несколько значений «id». Мой текущий код может читать только один идентификатор и повторять его 200 раз. Не уверен, почему это происходит.

Файл JSON

{  
   "success":true,
   "errors":[  

   ],
   "requestId":"3561c",
   "result":[  
      {  
         "id":257268,
         "name":"02 ",
         "description":"",
         "createdAt":"2017-10-06T11:29:40Z+0000",
         "updatedAt":"2017-11-07T13:38:11Z+0000",
         "url":"https",
         "subject":{  
            "type":"Text",
            "value":"Are you ready"
         },
         "fromName":{  
            "type":"Text",
            "value":"Centre"
         },
         "fromEmail":{  
            "type":"Text",
            "value":"abc@xyz.com"
         },
         "replyEmail":{  
            "type":"Text",
            "value":"noreply@xwz.com"
         },
         "folder":{  
            "type":"Folder",
            "value":8041,
            "folderName":"A"
         },
         "operational":false,
         "textOnly":false,
         "publishToMSI":false,
         "webView":false,
         "status":"approved",
         "template":681,
         "workspace":"R",
         "version":1,
         "autoCopyToText":false
      },
      {  
         "id":257273,
         "name":"02a",
         "description":"",
         "createdAt":"2017-10-06T11:29:46Z+0000",
         "updatedAt":"2017-11-07T13:38:19Z+0000",
         "url":"https:",
         "subject":{  
            "type":"Text",
            "value":"Still have questions?"
         },
         "fromName":{  
            "type":"Text",
            "value":"Centre"
         },
         "fromEmail":{  
            "type":"Text",
            "value":"abc@xyz.com"
         },
         "replyEmail":{  
            "type":"Text",
            "value":"noreply@xwz.com"
         },
         "folder":{  
            "type":"Folder",
            "value":8041,
            "folderName":"A"
         },
         "operational":false,
         "textOnly":false,
         "publishToMSI":false,
         "webView":false,
         "status":"approved",
         "template":681,
         "workspace":"R",
         "version":1,
         "autoCopyToText":false },

Код Python

import json
import csv
import os
import codecs
import sys
reload(sys)
sys.setdefaultencoding('utf8')

file_dir = os.path.normpath('/home/pp/jobs/staging/')
exp_dir = os.path.normpath('/home/pp/jobs/CSV/')
exp_file_name = 'emails.csv'
exp_path = os.path.join(exp_dir, exp_file_name)


my_dict_list =[]
try:
    for f in os.listdir(file_dir):
        if f.endswith('.json') and f.startswith('emails_'):
            file_path = os.path.join(file_dir, f)
            data = open(file_path, 'r')
            for line in data:
                my_dict = {}
                parsed_data = json.loads(line)
                my_dict["REQUEST_ID"] = parsed_data["requestId"]
                my_dict["SUCCESS"] = parsed_data["success"]
                for result in parsed_data["result"]:
                    my_dict["RESULT_ID"] = result["id"]
                    my_dict["NAME"] = result["name"]
                    my_dict["DESCRIPTION"] = result.get("description")
                    my_dict["STATUS"] = result["status"].encode('utf-8')
                    my_dict["FOLDER_TYPE"] = result["folder"]["type"]
                    my_dict["FOLDER_ID"] = result["folder"]["value"]
                    my_dict["FOLDER_NAME"] = result["folder"]["folderName"]
                    my_dict["FROM_EMAIL_TYPE"] = result["fromEmail"]["type"]
                    my_dict["FROM_EMAIL_VALUE"] = result["fromEmail"]["value"]
                    my_dict["FROM_NAME_TYPE"] = result["fromName"]["type"]
                    my_dict["FROM_NAME_VALUE"] = result["fromName"]["value"]
                    my_dict["REPLY_EMAIL_TYPE"] = result["replyEmail"]["type"]
                    my_dict["REPLY_EMAIL_VALUE"] = result["replyEmail"]["value"]
                    my_dict["SUBJECT_TYPE"] = result["subject"]["type"]
                    my_dict["SUBJECT_VALUE"] = result["subject"]["value"]
                    my_dict["OPERATIONAL"] = result["operational"]
                    my_dict["PUBLISH_TO_MSI"] = result["publishToMSI"]
                    my_dict["TEMPLATE"] = result["template"]
                    my_dict["TEXT_ONLY"] = result["textOnly"]
                    my_dict["URL"] = result.get("url")
                    my_dict["WEBVIEW"] = result["webView"]
                    my_dict["CREATED_AT"] = result["createdAt"]
                    my_dict["UPDATED_AT"] = result["updatedAt"]
                    my_dict["WORKSPACE"] = result["workspace"]
                    my_dict_list.append(my_dict)

    csv_columns = ["REQUEST_ID","SUCCESS","RESULT_ID","NAME","DESCRIPTION","STATUS","FOLDER_TYPE","FOLDER_ID","FOLDER_NAME","FROM_EMAIL_TYPE","FROM_EMAIL_VALUE","FROM_NAME_TYPE","FROM_NAME_VALUE","REPLY_EMAIL_TYPE","REPLY_EMAIL_VALUE","SUBJECT_TYPE","SUBJECT_VALUE","OPERATIONAL","PUBLISH_TO_MSI","TEMPLATE","TEXT_ONLY","URL","WEBVIEW","CREATED_AT","UPDATED_AT","WORKSPACE"]
    with open(exp_path,'wb') as csvfile:
                   xz = csv.DictWriter(csvfile,fieldnames=csv_columns)
                   headers = {}
                   for n in xz.fieldnames:
                       headers[n] = n
                   xz.writerow(headers)
                   for data in my_dict_list:
                       xz.writerow(data)
except Exception as exception:
    print("Please check the logs. JSON to CSV conversion failed for Emails: ", exception)
1
0
68
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Посмотрите здесь:

my_dict_list =[]
try:
    for f in os.listdir(file_dir):
        if f.endswith('.json') and f.startswith('emails_'):
            file_path = os.path.join(file_dir, f)
            data = open(file_path, 'r')
            for line in data:
                my_dict = {}
                parsed_data = json.loads(line)
                # ...
                for result in parsed_data["result"]:
                    # ...
                    my_dict_list.append(my_dict)

my_dict - это словарь, который обновляется только на строчном уровне файла. Но то, что вы хотите сделать, похоже, есть на каждом элементе parsed_data["result"]. Если вы добавляете один и тот же словарь в список внутри цикла и изменяете его, вы фактически помещаете в список несколько идентичных копий, а когда вы изменяете, вы изменяете все копии. («копия» - здесь плохой термин в Python, поскольку они на самом деле просто ссылки)

Чтобы решить вашу проблему, попробуйте заменить это:

                my_dict_list.append(my_dict)

с этим:

                my_dict_list.append(dict(my_dict))

это сделает (неглубокую) копию перед помещением в список.

Это обычная проблема в Python. Здесь важно то, что my_dict - это указатель на dict.

Здесь происходит то, что вы определяете my_dict (указатель на dict), обновляете его набором значений и затем добавляете его в список. Затем во второй итерации цикла вы изменяете значения my_dict и добавляете их во вторую позицию в массиве. Однако my_dict также находится в первой позиции массива. Таким образом, значения my_dict теперь обновляются как в индексе 0, так и в индексе 1 массива.

Из-за этого все значения обновляются в каждом слове в списке, а не только в идентификаторе. Это продолжается до последней итерации цикла, когда все записи в списке (все они my_dict) обновляются до значений последнего dict в результате.

Один из способов исправить это - определять новый dict на каждой итерации.

        for line in data:
            parsed_data = json.loads(line)
            for result in parsed_data["result"]:
                my_dict = {}
                my_dict["REQUEST_ID"] = parsed_data["requestId"]
                my_dict["SUCCESS"] = parsed_data["success"]

                my_dict["RESULT_ID"] = result["id"]
                my_dict["NAME"] = result["name"]
                my_dict["DESCRIPTION"] = result.get("description")
                my_dict["STATUS"] = result["status"].encode('utf-8')
                my_dict["FOLDER_TYPE"] = result["folder"]["type"]
                my_dict["FOLDER_ID"] = result["folder"]["value"]
                my_dict["FOLDER_NAME"] = result["folder"]["folderName"]
                my_dict["FROM_EMAIL_TYPE"] = result["fromEmail"]["type"]
                my_dict["FROM_EMAIL_VALUE"] = result["fromEmail"]["value"]
                my_dict["FROM_NAME_TYPE"] = result["fromName"]["type"]
                my_dict["FROM_NAME_VALUE"] = result["fromName"]["value"]
                my_dict["REPLY_EMAIL_TYPE"] = result["replyEmail"]["type"]
                my_dict["REPLY_EMAIL_VALUE"] = result["replyEmail"]["value"]
                my_dict["SUBJECT_TYPE"] = result["subject"]["type"]
                my_dict["SUBJECT_VALUE"] = result["subject"]["value"]
                my_dict["OPERATIONAL"] = result["operational"]
                my_dict["PUBLISH_TO_MSI"] = result["publishToMSI"]
                my_dict["TEMPLATE"] = result["template"]
                my_dict["TEXT_ONLY"] = result["textOnly"]
                my_dict["URL"] = result.get("url")
                my_dict["WEBVIEW"] = result["webView"]
                my_dict["CREATED_AT"] = result["createdAt"]
                my_dict["UPDATED_AT"] = result["updatedAt"]
                my_dict["WORKSPACE"] = result["workspace"]
                my_dict_list.append(my_dict)

Спасибо @amanbirs. Ваше объяснение имеет смысл и решило проблему. В чем разница между созданием нового списка (ваш подход) и тем, что Адриан Тэм разместил ниже. Оба решения работали нормально.

django-unchained 13.09.2018 19:49

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

amanbirs 13.09.2018 19:55

Еще раз спасибо @amanbirs. Я думал добавить обработку исключений в этот конкретный блок. Я хочу записать значения my_dict ["REQUEST_ID"] и my_dict ["RESULT_ID"] в файл журнала, если в данных возникнет какая-либо ошибка. Что-то вроде этого, но не уверен, что это правильно: except ValueError: logger = logging.getLogger(__name__) logger.info("Request ID {my_dict['REQUEST_ID']} and Result ID {my_dict['SUCCESS']}", exc_info=True)

django-unchained 14.09.2018 20:55

@ django-unchained не уверен, что понимаю вопрос. Может быть, сделать это новым сообщением, чтобы вы могли четко объяснить его, а другие люди также могли предлагать ответы?

amanbirs 17.09.2018 14:52

Вот он: [stackoverflow.com/questions/52338125/…

django-unchained 17.09.2018 19:32

Почему вы читаете строки из файла, если в каждом файле уже есть одна строка?
Эта часть:

data = open(file_path, 'r')
    for line in data:
        my_dict = {}
        parsed_data = json.loads(line)

сводится к:

my_dict = {}
parsed_data = json.loads(open(file_path, 'r').read())

Другие вопросы по теме