Como posso converter JSON para CSV?

Tenho um ficheiro JSON que quero esconder num ficheiro CSV. Como posso fazer isto com o Python?

Eu tentei.
import json
import csv

f = open('data.json')
data = json.load(f)
f.close()
f = open('data.csv')
csv_file = csv.writer(f)
for item in data:
    f.writerow(item)

f.close()
No entanto, não funcionou. Estou a usar o Django e o erro que recebi foi:
file' object has no attribute 'writerow'
Então, tentei o seguinte:
import json
import csv

f = open('data.json')
data = json.load(f)
f.close()

f = open('data.csv')
csv_file = csv.writer(f)
for item in data:
    csv_file.writerow(item)

f.close()

Então eu recebo o erro:

sequence expected

Amostra do ficheiro json:

[
  {
    "pk": 22,
    "model": "auth.permission",
    "fields": {
      "codename": "add_logentry",
      "name": "Can add log entry",
      "content_type": 8
    }
  },
  {
    "pk": 23,
    "model": "auth.permission",
    "fields": {
      "codename": "change_logentry",
      "name": "Can change log entry",
      "content_type": 8
    }
  },
  {
    "pk": 24,
    "model": "auth.permission",
    "fields": {
      "codename": "delete_logentry",
      "name": "Can delete log entry",
      "content_type": 8
    }
  },
  {
    "pk": 4,
    "model": "auth.permission",
    "fields": {
      "codename": "add_group",
      "name": "Can add group",
      "content_type": 2
    }
  },
  {
    "pk": 10,
    "model": "auth.permission",
    "fields": {
      "codename": "add_message",
      "name": "Can add message",
      "content_type": 4
    }
  }
]
Author: martineau, 2009-12-09

17 answers

Não tenho a certeza se esta questão já está resolvida ou não, mas deixe-me colar o que fiz para referência.

Primeiro, o seu JSON tem objectos aninhados, por isso normalmente não pode ser directamente convertido em CSV. Tens de mudar isso para algo assim.

{
    "pk": 22,
    "model": "auth.permission",
    "codename": "add_logentry",
    "content_type": 8,
    "name": "Can add log entry"
},
......]
Aqui está o meu código para gerar CSV a partir disso.
import csv
import json

x = """[
    {
        "pk": 22,
        "model": "auth.permission",
        "fields": {
            "codename": "add_logentry",
            "name": "Can add log entry",
            "content_type": 8
        }
    },
    {
        "pk": 23,
        "model": "auth.permission",
        "fields": {
            "codename": "change_logentry",
            "name": "Can change log entry",
            "content_type": 8
        }
    },
    {
        "pk": 24,
        "model": "auth.permission",
        "fields": {
            "codename": "delete_logentry",
            "name": "Can delete log entry",
            "content_type": 8
        }
    }
]"""

x = json.loads(x)

f = csv.writer(open("test.csv", "wb+"))

# Write CSV Header, If you dont need that, remove this line
f.writerow(["pk", "model", "codename", "name", "content_type"])

for x in x:
    f.writerow([x["pk"],
                x["model"],
                x["fields"]["codename"],
                x["fields"]["name"],
                x["fields"]["content_type"]])

Obterás a saída como:

pk,model,codename,name,content_type
22,auth.permission,add_logentry,Can add log entry,8
23,auth.permission,change_logentry,Can change log entry,8
24,auth.permission,delete_logentry,Can delete log entry,8
 91
Author: YOU, 2016-09-27 12:04:55
Presumo que o seu ficheiro JSON irá descodificar numa lista de dicionários. Primeiro precisamos de uma função que achate os objetos JSON:
def flattenjson( b, delim ):
    val = {}
    for i in b.keys():
        if isinstance( b[i], dict ):
            get = flattenjson( b[i], delim )
            for j in get.keys():
                val[ i + delim + j ] = get[j]
        else:
            val[i] = b[i]

    return val

O resultado de executar este trecho no seu objecto JSON:

flattenjson( {
    "pk": 22, 
    "model": "auth.permission", 
    "fields": {
      "codename": "add_message", 
      "name": "Can add message", 
      "content_type": 8
    }
  }, "__" )

É

{
    "pk": 22, 
    "model": "auth.permission', 
    "fields__codename": "add_message", 
    "fields__name": "Can add message", 
    "fields__content_type": 8
}

Depois de aplicar esta função a cada dict na matriz de entrada de objectos JSON:

input = map( lambda x: flattenjson( x, "__" ), input )

E encontrar os nomes das colunas relevantes:

columns = [ x for row in input for x in row.keys() ]
columns = list( set( columns ) )
Não é difícil passar isto pelo módulo csv.
with open( fname, 'wb' ) as out_file:
    csv_w = csv.writer( out_file )
    csv_w.writerow( columns )

    for i_r in input:
        csv_w.writerow( map( lambda x: i_r.get( x, "" ), columns ) )
Espero que isto ajuda!
 67
Author: Alec McGail, 2017-04-15 15:28:11

Com a pandas biblioteca, isto é tão fácil como usar dois comandos!

pandas.read_json()

Converter uma cadeia de JSON para um objeto pandas (uma série ou dataframe). Então, assumindo que os resultados foram armazenados como df:

df.to_csv()

Que pode devolver um texto ou escrever directamente para um ficheiro csv.

Com base na verbosidade das respostas anteriores, todos devemos agradecer aos pandas pelo atalho.
 44
Author: vmg, 2016-05-18 18:19:22

JSON pode representar uma grande variedade de estruturas de dados -- um "objeto" JS é mais ou menos como um dict Python (com teclas de cadeia), um array JS mais ou menos como uma lista Python, e você pode aninhá-las desde que os elementos finais da "folha" sejam números ou cadeias.

CSV pode representar essencialmente apenas uma tabela 2-D -- opcionalmente com uma primeira linha de" cabeçalhos", ou seja, "nomes de colunas" , que pode tornar a tabela interpretável como uma lista de dictos, em vez da interpretação normal, uma lista de listas (mais uma vez, elementos "folha" podem ser números ou strings).

Então, no caso geral, você não pode traduzir uma estrutura JSON arbitrária para um CSV. Em alguns casos especiais você pode (array of arrays sem mais nidificação; array of objects which all have exactly the same keys). Qual caso especial, se houver, se aplica ao seu problema? Os detalhes da solução dependem do caso especial que você tem. Tendo em conta o facto surpreendente de nem sequer mencionares qual se aplica, suspeito que não ter considerado a restrição, nenhum caso utilizável na verdade se aplica, e seu problema é impossível de resolver. Mas por favor, esclareça!
 34
Author: Alex Martelli, 2009-12-09 04:27:25

Uma solução genérica que traduz qualquer lista json de objectosplanos para csv.

Passa a entrada.JSON file como primeiro argumento na linha de comando.

import csv, json, sys

input = open(sys.argv[1])
data = json.load(input)
input.close()

output = csv.writer(sys.stdout)

output.writerow(data[0].keys())  # header row

for row in data:
    output.writerow(row.values())
 25
Author: Mike Repass, 2012-10-05 02:55:35

Este código deve funcionar para si, assumindo que os seus dados JSON estão num ficheiro chamado data.json.

import json
import csv

with open("data.json") as file:
    data = json.load(file)

with open("data.csv", "w") as file:
    csv_file = csv.writer(file)
    for item in data:
        csv_file.writerow([item['pk'], item['model']] + item['fields'].values())
 21
Author: Dan Loewenherz, 2013-04-09 18:31:40

Será fácil de usar csv.DictWriter(),a implementação detalhada pode ser assim:

def read_json(filename):
    return json.loads(open(filename).read())
def write_csv(data,filename):
    with open(filename) as outf:
        writer = csv.DictWriter(outf, data[0].keys())
        writer.writeheader()
        for row in data:
            writer.writerow(row)
# implement
write_csv(read_json('test.json'), 'output.csv')

Note que isto assume que todos os seus objectos JSON têm os mesmos campos.

Aqui está a referênciaque pode ajudar-te.

 13
Author: ReturnHttp402, 2016-12-16 06:14:35

Estava a ter problemas com a solução proposta pelo Dan, mas isto funcionou comigo:

import json
import csv 

f = open('test.json')
data = json.load(f)
f.close()

f=csv.writer(open('test.csv','wb+'))

for item in data:
  f.writerow([item['pk'], item['model']] + item['fields'].values())

Onde " teste.json " continha o seguinte:

[ 
{"pk": 22, "model": "auth.permission", "fields": 
  {"codename": "add_logentry", "name": "Can add log entry", "content_type": 8 } }, 
{"pk": 23, "model": "auth.permission", "fields": 
  {"codename": "change_logentry", "name": "Can change log entry", "content_type": 8 } }, {"pk": 24, "model": "auth.permission", "fields": 
  {"codename": "delete_logentry", "name": "Can delete log entry", "content_type": 8 } }
]
 5
Author: Amanda, 2017-05-23 12:10:40

Como mencionado nas respostas anteriores, a dificuldade em converter json para csv é porque um arquivo json pode conter dicionários aninhados e, portanto, ser uma estrutura de dados multidimensional versos um csv que é uma estrutura de dados 2D. No entanto, uma boa maneira de transformar uma estrutura multidimensional para um csv é ter vários CSV que se ligam com chaves primárias.

No seu exemplo, a primeira saída do csv tem as colunas "pk","model","fields" como suas colunas. Valores para "pk", e "model "são fáceis de obter, mas como a coluna" fields "contém um dicionário, Ele deve ser seu próprio csv e porque" codenome "parece ser a chave primária, você pode usar como a entrada para" fields " para completar o primeiro csv. O segundo csv contém o dicionário da coluna "fields" com o nome de código como a chave primária que pode ser usada para amarrar os 2 csvs juntos.

Aqui está uma solução para o seu ficheiro json que converte um dicionário aninhado para 2 csvs.

import csv
import json

def readAndWrite(inputFileName, primaryKey=""):
    input = open(inputFileName+".json")
    data = json.load(input)
    input.close()

    header = set()

    if primaryKey != "":
        outputFileName = inputFileName+"-"+primaryKey
        if inputFileName == "data":
            for i in data:
                for j in i["fields"].keys():
                    if j not in header:
                        header.add(j)
    else:
        outputFileName = inputFileName
        for i in data:
            for j in i.keys():
                if j not in header:
                    header.add(j)

    with open(outputFileName+".csv", 'wb') as output_file:
        fieldnames = list(header)
        writer = csv.DictWriter(output_file, fieldnames, delimiter=',', quotechar='"')
        writer.writeheader()
        for x in data:
            row_value = {}
            if primaryKey == "":
                for y in x.keys():
                    yValue = x.get(y)
                    if type(yValue) == int or type(yValue) == bool or type(yValue) == float or type(yValue) == list:
                        row_value[y] = str(yValue).encode('utf8')
                    elif type(yValue) != dict:
                        row_value[y] = yValue.encode('utf8')
                    else:
                        if inputFileName == "data":
                            row_value[y] = yValue["codename"].encode('utf8')
                            readAndWrite(inputFileName, primaryKey="codename")
                writer.writerow(row_value)
            elif primaryKey == "codename":
                for y in x["fields"].keys():
                    yValue = x["fields"].get(y)
                    if type(yValue) == int or type(yValue) == bool or type(yValue) == float or type(yValue) == list:
                        row_value[y] = str(yValue).encode('utf8')
                    elif type(yValue) != dict:
                        row_value[y] = yValue.encode('utf8')
                writer.writerow(row_value)

readAndWrite("data")
 4
Author: dmathewwws, 2014-02-18 01:19:24

Eu sei que já passou muito tempo desde que esta pergunta foi feita, mas eu pensei que eu poderia adicionar a resposta de todos os outros e compartilhar um post no blog que eu acho que explicar a solução de uma forma muito concisa.

Aqui está a ligação

Abra um ficheiro para escrita

employ_data = open('/tmp/EmployData.csv', 'w')

Criar o objecto do escritor csv

csvwriter = csv.writer(employ_data)
count = 0
for emp in emp_data:
      if count == 0:
             header = emp.keys()
             csvwriter.writerow(header)
             count += 1
      csvwriter.writerow(emp.values())

Certifique-se de fechar o ficheiro para gravar o conteúdo

employ_data.close()
 3
Author: user3768804, 2017-03-26 12:00:26
Isto funciona relativamente bem. Ele achata o json para escrevê-lo em um arquivo csv. Os elementos aninhados são geridos:) Isso é para o python 3
import json

o = json.loads('your json string') # Be careful, o must be a list, each of its objects will make a line of the csv.

def flatten(o, k='/'):
    global l, c_line
    if isinstance(o, dict):
        for key, value in o.items():
            flatten(value, k + '/' + key)
    elif isinstance(o, list):
        for ov in o:
            flatten(ov, '')
    elif isinstance(o, str):
        o = o.replace('\r',' ').replace('\n',' ').replace(';', ',')
        if not k in l:
            l[k]={}
        l[k][c_line]=o

def render_csv(l):
    ftime = True

    for i in range(100): #len(l[list(l.keys())[0]])
        for k in l:
            if ftime :
                print('%s;' % k, end='')
                continue
            v = l[k]
            try:
                print('%s;' % v[i], end='')
            except:
                print(';', end='')
        print()
        ftime = False
        i = 0

def json_to_csv(object_list):
    global l, c_line
    l = {}
    c_line = 0
    for ov in object_list : # Assumes json is a list of objects
        flatten(ov)
        c_line += 1
    render_csv(l)

json_to_csv(o)
Divirtam-se.
 2
Author: Loïc, 2016-04-24 02:17:23
A minha maneira simples de resolver isto:

Crie um novo ficheiro Python como: json_to_csv.py

Adicionar este código:

import csv, json, sys
#if you are not using utf-8 files, remove the next line
sys.setdefaultencoding("UTF-8")
#check if you pass the input file and output file
if sys.argv[1] is not None and sys.argv[2] is not None:

    fileInput = sys.argv[1]
    fileOutput = sys.argv[2]

    inputFile = open(fileInput)
    outputFile = open(fileOutput, 'w')
    data = json.load(inputFile)
    inputFile.close()

    output = csv.writer(outputFile)

    output.writerow(data[0].keys())  # header row

    for row in data:
        output.writerow(row.values())

Depois de adicionar este código, grave o ficheiro e execute no terminal:

Python json_to_csv.py entrada.saída txt.csv

Espero que isto te ajude. Até logo!
 2
Author: Gabriel Pires, 2016-12-13 02:05:02
Modificou a resposta do Alec McGail para apoiar o JSON com listas dentro.
    def flattenjson(self, mp, delim="|"):
            ret = []
            if isinstance(mp, dict):
                    for k in mp.keys():
                            csvs = self.flattenjson(mp[k], delim)
                            for csv in csvs:
                                    ret.append(k + delim + csv)
            elif isinstance(mp, list):
                    for k in mp:
                            csvs = self.flattenjson(k, delim)
                            for csv in csvs:
                                    ret.append(csv)
            else:
                    ret.append(mp)

            return ret
Obrigado!
 1
Author: Sawan Vaidya, 2016-07-12 11:56:02
Não é uma maneira muito inteligente de o fazer, mas tive o mesmo problema e isto funcionou comigo.
import csv

f = open('data.json')
data = json.load(f)
f.close()

new_data = []

for i in data:
   flat = {}
   names = i.keys()
   for n in names:
      try:
         if len(i[n].keys()) > 0:
            for ii in i[n].keys():
               flat[n+"_"+ii] = i[n][ii]
      except:
         flat[n] = i[n]
   new_data.append(flat)  

f = open(filename, "r")
writer = csv.DictWriter(f, new_data[0].keys())
writer.writeheader()
for row in new_data:
   writer.writerow(row)
f.close()
 1
Author: Juan Luis Martinez, 2018-03-05 10:36:36

Uma vez que os dados parecem estar num formato de dicionário, parece que você deve realmente usar csv.DictWriter () para realmente output as linhas com a informação de cabeçalho apropriado. Isto deve permitir que a conversão seja um pouco mais fácil. The fieldnames parameter would then set up the order properly while the output of the first line as the headers would allow it to be read and processed later by csv.DictReader ().

Por exemplo, Mike Repass
output = csv.writer(sys.stdout)

output.writerow(data[0].keys())  # header row

for row in data:
  output.writerow(row.values())

No entanto, basta mudar a configuração inicial para output = csv.DictWriter (filesetting, fieldnames=data[0].chaves ()

Note que uma vez que a ordem dos elementos de um dicionário não está definida, você pode ter que criar entradas de nomes de campo explicitamente. Assim que fizeres isso, o writerow vai funcionar. As escritas então funcionam como mostrado originalmente.

 0
Author: sabbahillel, 2014-02-10 14:50:04
Infelizmente, Não tenho reputação de dar uma pequena contribuição para a incrível resposta @Alec McGail. Eu estava usando Python3 e eu tive que converter o mapa para uma lista seguindo o comentário de @ Alexis R.

Additionaly eu encontrei o escritor csv estava adicionando um CR extra para o arquivo (eu tenho uma linha vazia para cada linha com dados dentro do arquivo csv). A solução foi muito fácil seguindo a resposta @Jason R. Coombs a este tópico: CSV em Python adicionando um extra regresso da carruagem

Você precisa simplesmente adicionar o parâmetro lineter = '\n ' ao csv.escritor. Será: csv_w = csv.writer( out_file, lineterminator='\n' )

 0
Author: derwyddon, 2018-06-29 09:42:51
([[2]}surpreendentemente, eu descobri que nenhuma das respostas postadas aqui até agora corretamente lidar com todos os cenários possíveis (por exemplo, dicionários aninhados, listas aninhadas, nenhum valor, etc).

Esta solução deve funcionar em todos os cenários:

def flatten_json(json):
    def process_value(keys, value, flattened):
        if isinstance(value, dict):
            for key in value.keys():
                process_value(keys + [key], value[key], flattened)
        elif isinstance(value, list):
            for idx, v in enumerate(value):
                process_value(keys + [str(idx)], v, flattened)
        else:
            flattened['__'.join(keys)] = value

    flattened = {}
    for key in json.keys():
        process_value([key], json[key], flattened)
    return flattened
 0
Author: Max Berman, 2018-09-18 20:22:33