【问题标题】:Saving python dictionary (or JSON?) as CSV将 python 字典(或 JSON?)保存为 CSV
【发布时间】:2021-07-07 00:23:27
【问题描述】:

我一直在尝试将 Google Search Console API 的输出保存为 CSV 文件。最初,我使用 sys.stdout 来保存从他们提供的示例代码中打印出来的内容。但是,在第三次左右的尝试中,我开始收到此错误:

File "C:\python39\lib\encodings\cp1252.py", line 19, in encode
    return codecs.charmap_encode(input,self.errors,encoding_table)[0]
UnicodeEncodeError: 'charmap' codec can't encode character '\uff1a' in position 13: character maps to <undefined>

之后我尝试切换到使用 Pandas 到 csv 函数。结果不是我所希望的,但至少更接近:

> ,rows,responseAggregationType
0,"{'keys': ['amp pwa'], 'clicks': 1, 'impressions': 4, 'ctr': 0.25, 'position': 7.25}",byProperty
1,"{'keys': ['convert desktop site to mobile'], 'clicks': 1, 'impressions': 2, 'ctr': 0.5, 'position': 1.5}",byProperty

我对 python 很陌生,但我认为这与 API pull 的输出不是标准的 dict 对象格式有关。

我也尝试使用 csv.write 函数(我在来到这里之前删除了该代码,所以我没有示例)但结果与无法编码问题的结果相同,因为无法对来自 sys.stdout 的问题进行编码。

这是完全按照我的需要打印输出的代码,我只需要能够将它保存在可以在电子表格中使用它的地方。

#!/usr/bin/python
# -*- coding: utf-8 -*-


from __future__ import print_function

import argparse
import sys
from googleapiclient import sample_tools

# Declare command-line flags.
argparser = argparse.ArgumentParser(add_help=False)
argparser.add_argument('property_uri', type=str,
                       help=('Site or app URI to query data for (including '
                             'trailing slash).'))
argparser.add_argument('start_date', type=str,
                       help=('Start date of the requested date range in '
                             'YYYY-MM-DD format.'))
argparser.add_argument('end_date', type=str,
                       help=('End date of the requested date range in '
                             'YYYY-MM-DD format.'))


def main(argv):
  service, flags = sample_tools.init(
      argv, 'searchconsole', 'v1', __doc__, __file__, parents=[argparser],
      scope='https://www.googleapis.com/auth/webmasters.readonly')

  # Get top 10 queries for the date range, sorted by click count, descending.
  request = {
      'startDate': flags.start_date,
      'endDate': flags.end_date,
      'dimensions': ['query'],
      'rowLimit': 10
  }
  response = execute_request(service, flags.property_uri, request)
  print_table(response, 'Top Queries')


def execute_request(service, property_uri, request):
  """Executes a searchAnalytics.query request.

  Args:
    service: The searchconsole service to use when executing the query.
    property_uri: The site or app URI to request data for.
    request: The request to be executed.

  Returns:
    An array of response rows.
  """
  return service.searchanalytics().query(
      siteUrl=property_uri, body=request).execute()


def print_table(response, title):
  """Prints out a response table.

  Each row contains key(s), clicks, impressions, CTR, and average position.

  Args:
    response: The server response to be printed as a table.
    title: The title of the table.
  """
  print('\n --' + title + ':')
  
  if 'rows' not in response:
    print('Empty response')
    return

  rows = response['rows']
  row_format = '{:<20}' + '{:>20}' * 4
  print(row_format.format('Keys', 'Clicks', 'Impressions', 'CTR', 'Position'))
  for row in rows:
    keys = ''
    # Keys are returned only if one or more dimensions are requested.
    if 'keys' in row:
      keys = u','.join(row['keys']).encode('utf-8').decode()
    print(row_format.format(
        keys, row['clicks'], row['impressions'], row['ctr'], row['position']))

if __name__ == '__main__':
  main(sys.argv)

这是我想要的输出,但逗号分隔:

Keys                              Clicks         Impressions                 CTR            Position
amp pwa                                1                   4                0.25                7.25
convert desktop site to mobile                   1                   2                 0.5                 1.5

这是仅打印结果对象的结果:

{'rows': [{'keys': ['amp pwa'], 'clicks': 1, 'impressions': 4, 'ctr': 0.25, 'position': 7.25}, {'keys': ['convert desktop site to mobile'], 'clicks': 1, 'impressions': 2, 'ctr': 0.5, 'position': 1.5}], 'responseAggregationType': 'byProperty'}

我希望我已经提供了足够的信息,在提出问题之前,我尝试了此处和其他网站上推荐的所有解决方案。它看起来像是一个格式奇怪的 json/字典对象。

非常感谢任何帮助。

更新,解决方案:

调整后的输出代码为:

  import csv
  with open("out.csv", "w", encoding="utf8", newline='') as f:
      rows = response['rows']
      writer = csv.writer(f)
      headers = ["Keys", "Clicks", "Impressions", "CTR", "Position"]
      writer.writerow(headers)
      
      for row in rows:
        keys = ''
        # Keys are returned only if one or more dimensions are requested.
        if 'keys' in row:
          keys = u','.join(row['keys']).encode('utf-8').decode()
          # Looks like your data has the keys in lowercase
        writer.writerow([keys, row['clicks'], row['impressions'], row['ctr'], row['position']])

【问题讨论】:

  • “最初,我使用 sys.stdout 来保存从他们提供的示例代码中打印出来的内容。” print_table 打印的不是有效的 CSV 数据;这是表格的格式很好的表示,旨在在终端中正确显示。无论如何,当您尝试将 result['rows'] 转换为 CSV 时发生了什么?

标签: python json dictionary google-search-api


【解决方案1】:

可能只是输出文件的编码有问题。

看起来您从响应中获得的行是一系列类似 dict 的对象,所以这应该可以:

import csv
with open("out.csv", "w", encoding="utf8") as f:
    writer = csv.writer(f)
    headers = ["Keys", "Clicks", "Impressions", "CTR", "Position"]
    writer.writerow(headers)
    for row in rows:
        writer.writerow(
            [
                ", ".join(row.get("keys", [])),
                row["clicks"],
                row["impressions"],
                row["ctr"],
                row["postition"],
            ]
        )

writer 对象接受多个参数来控制行分隔符并在输出 csv 中引用。详情请查看module docs

【讨论】:

  • 这帮助很大!你让我不必告诉我的老板 8 小时没有结果。
  • 然而,现在的输出看起来像:Keys,Clicks,Impressions,CTR,Position ['amp pwa'],1,4,0​​.25,7.25 ['convert desktop site to mobile'],1 ,2,0.5,1.5 这是一个巨大的改进,以后很容易清理。我尝试从原始 print 语句中为 row in rows 复制一些代码:# Looks like your data have the keys in lowercase writer.writerow([row[field.lower()] for field in headers]) if 'keys'在行中:keys = u','.join(row['keys']).encode('utf-8').decode() 但没有运气,与您的代码输出完全相同。
  • 我已更新答案以明确选择每个输出字段,而不是使用列表推导 - 它使您在处理行时如何更改其中一个更清楚。
  • 谢谢马尔科姆。我在输出多个“键”时遇到问题,周围有双引号。我在another question 上发帖,以避免在这里过多地进入 cmets。
  • 这就是您使用的 csv 格式所期望的 - 字段分隔符 [,] 是键字段的一部分,因此该字段被引用字符 ["] 引用。您可以更改分隔符到另一个不属于您的数据的字符以避免引用 - 将其替换为制表符,例如,您将拥有一个 tsv 文件。
猜你喜欢
  • 2022-12-15
  • 1970-01-01
  • 1970-01-01
  • 2015-04-16
  • 1970-01-01
  • 2021-08-08
  • 2018-12-20
  • 2018-07-25
  • 1970-01-01
相关资源
最近更新 更多