【python 让繁琐工作自动化】第16章 处理 CSV 文件和 JSON 数据


Automate the Boring Stuff with Python: Practical Programming for Total Beginners (2nd Edition)
Copyright © 2020 by Al Sweigart.


CSV 和 JSON 文件都是纯文本文件。

  • CSV (Comma-Separated Values):逗号分隔的值。而 CSV 文件是简化的电子表格,存储为纯文本文件。
    Python 的 csv 模块可解析 CSV 文件。csv 模块是 Python 自带的。
  • JSON (JavaScript Object Notation):JavaScript 对象表示法。JSON 文本格式在语法上与创建 JavaScript 对象的代码相同。
    发音为“JAY-sawn”或“Jason”。
    JSON 格式很有用,因为它已在许多 Web 应用程序中使用。使用 JSON 文件不需要懂得 JavaScript 编程语言。
    Python 的 json 模块可以解析 JSO文件。

csv 模块

CSV 文件中的每一行代表电子表格中的一行,并用逗号分隔该行中的单元格。

CSV 文件缺少 Excel 电子表格的许多功能。

CSV 文件的优点是简单。它可以在文本编辑器中查看,是表示电子表格数据的直接方法。许多程序类型都支持 CSV 文件。CSV 格式正如它所展现的那样:它只是一个以逗号分隔的值的文本文件。

由于 CSV 文件只是文本文件,可能会有人使用在第9章中学到的技术来处理该文件。比如,在文本的每一行上调用 split(','),以逗号分隔的值作为字符串列表。
但是,并非 CSV 文件中的每个逗号都代表两个单元格之间的边界。CSV 文件还具有其自己的转义字符集,以允许逗号和其他字符作为值的一部分包括在内。
因此应使用 csv 模块读取和写入 CSV 文件。

reader 对象

example.csv 文件内容如下:

4/5/2015 13:34,Apples,73
4/5/2015 3:41,Cherries,85
4/6/2015 12:46,Pears,14
4/8/2015 8:59,Oranges,52
>>> import csv
>>> exampleFile = open('example.csv')
>>> exampleReader = csv.reader(exampleFile)	# 创建一个 reader 对象
>>> exampleData = list(exampleReader)
>>> exampleData
[['4/5/2015 13:34', 'Apples', '73'], ['4/5/2015 3:41', 'Cherries', '85'], ['4/6/2015 12:46', 'Pears', '14'], ['4/8/2015 8:59', 'Oranges', '52']]
>>> exampleData[0][0]
'4/5/2015 13:34'
>>> exampleData[1][1]
'Cherries'

将 File 对象传递给 csv.reader() 函数,返回一个可供使用的 reader 对象。
使用 list() 将 CSV 文件的内容转换为列表,就可以使用 exampleData[row][col] 来访问指定行和列中的值:

在 for 循环中从 reader 对象读取数据

对于大型CSV文件,可以在 for 循环中使用 reader 对象。这样可以避免将整个文件立即加载到内存中。

>>> import csv
>>> exampleFile = open('example.csv')
>>> exampleReader = csv.reader(exampleFile)
>>> for row in exampleReader:
        print('Row ' + str(exampleReader.line_num) + ' ' + str(row))

Row 1 ['4/5/2015 13:34', 'Apples', '73']
Row 2 ['4/5/2015 3:41', 'Cherries', '85']
Row 3 ['4/6/2015 12:46', 'Pears', '14']
Row 4 ['4/8/2015 8:59', 'Oranges', '52']

可以使用 reader 对象的 line_num 变量获取当前行的编号。

reader 对象只能循环一次。要重新读取CSV文件,必须调用 csv.reader() 创建一个 reader 对象。

writer 对象

>>> import csv
>>> outputFile = open('output.csv', 'w', newline='')
>>> outputWriter = csv.writer(outputFile)
>>> outputWriter.writerow(['spam', 'eggs', 'bacon', 'ham'])
21
>>> outputWriter.writerow(['Hello, world!', 'eggs', 'bacon', 'ham'])
32
>>> outputWriter.writerow([1, 2, 3.141592, 4])
16
>>> outputFile.close()

将 File 对象传递给 csv.writer() 返回一个 writer 对象。
在 Windows 上,还需要为 open() 函数的 newline 关键字参数传递一个空字符串。如果忘记设置 newline 实参,则 output.csv 中的行将以双倍行距显示。

writer 对象的 writerow() 方法接受一个列表实参。列表中的每个值都放置在输出CSV文件的单元格中。writerow() 的返回值是该行写入文件的字符数(包括换行符)。

这段代码生成一个output.csv文件,如下所示:

spam,eggs,bacon,ham
"Hello, world!",eggs,bacon,ham
1,2,3.141592,4

注意,writer 对象自动在CSV文件中使用双引号对值 “Hello, world!” 中的逗号进行转义。

delimiter 和 lineterminator 关键字参数

下面的程序使用制表符而不是逗号来分隔单元格,并且将行以双倍行距隔开:

>>> import csv
>>> csvFile = open('example.tsv', 'w', newline='')
>>> csvWriter = csv.writer(csvFile, delimiter='\t', lineterminator='\n\n')
>>> csvWriter.writerow(['apples', 'oranges', 'grapes'])
24
>>> csvWriter.writerow(['eggs', 'bacon', 'ham'])
17
>>> csvFile.close()

定界符(delimiter)是出现在一行单元格之间的字符。默认情况下,CSV文件的定界符是逗号。
行终止符(line terminator)是一行结尾处的字符。默认情况下,行终止符是换行符。

上面的程序生成一个名为 example.tsv 的文件,单元格由制表符分隔。文件扩展名 .tsv 用于制表符分隔的值(Tab-Separated Values)。

DictReader 和 DictWriter 对象

对于包含标题行的CSV文件,使用 DictReader 和 DictWriter 对象通常更方便。
reader 和 writer 对象通过使用列表读取和写入CSV文件行。DictReader 和 DictWriter 对象执行相同的功能,但改用字典,它们使用CSV文件的第一行作为这些字典的键。

exampleWithHeader.csv 文件内容如下:

Timestamp,Fruit,Quantity
4/5/2015 13:34,Apples,73
4/5/2015 3:41,Cherries,85
4/6/2015 12:46,Pears,14
4/8/2015 8:59,Oranges,52
>>> import csv
>>> exampleFile = open('exampleWithHeader.csv')
>>> exampleDictReader = csv.DictReader(exampleFile)
>>> for row in exampleDictReader:
...     print(row['Timestamp'], row['Fruit'], row['Quantity'])
...
4/5/2015 13:34 Apples 73
4/5/2015 3:41 Cherries 85
4/6/2015 12:46 Pears 14
4/8/2015 8:59 Oranges 52

使用 DictReader 对象不需要其他代码来跳过第一行的标题信息,DictReader 对象可以完成此操作。

如果尝试将 DictReader 对象与 example.csv 一起使用,则 DictReader 对象将使用 '4/5/2015 13:34''Apples''73' 作为字典的键。为了避免这种情况,可以为 DictReader() 函数提供第二个参数,该参数包含虚构的标题名称:

>>> import csv
>>> exampleFile = open('example.csv')
>>> exampleDictReader = csv.DictReader(exampleFile, ['time', 'name', 'amount'])
>>> for row in exampleDictReader:
...     print(row['time'], row['name'], row['amount'])
...
4/5/2015 13:34 Apples 73
4/5/2015 3:41 Cherries 85
4/6/2015 12:46 Pears 14
4/8/2015 8:59 Oranges 52

DictWriter 对象使用字典创建CSV文件。

>>> import csv
>>> outputFile = open('output.csv', 'w', newline='')
>>> outputDictWriter = csv.DictWriter(outputFile, ['Name', 'Pet', 'Phone'])
>>> outputDictWriter.writeheader() # 写入标题行
>>> outputDictWriter.writerow({'Name': 'Alice', 'Pet': 'cat', 'Phone': '555-1234'})
20
>>> outputDictWriter.writerow({'Name': 'Bob', 'Phone': '555-9999'})
15
>>> outputDictWriter.writerow({'Phone': '555-5555', 'Name': 'Carol', 'Pet': 'dog'})
20
>>> outputFile.close()

如果希望文件包含标题行,可通过调用 writeheader() 将该行写入。

此代码创建的 output.csv 文件如下所示:

Name,Pet,Phone
Alice,cat,555-1234
Bob,,555-9999
Carol,dog,555-5555

注意:

  • 传递给 writerow() 的字典中的键值对的顺序无关紧要:它们是按赋予 DictWriter() 的键的顺序进行写入的。
  • 所有丢失的键在CSV文件中都将为空。比如 {'Name':'Bob','Phone':'555-9999'} 中的 'Pet'

项目:从CSV文件中删除标题

实现功能:该程序将需要在当前工作目录中打开每个扩展名为.csv的文件,读取CSV文件的内容,并将内容(不包括第一行)重写为同名文件。

警告:每当编写修改文件的程序时,请确保首先备份文件,以防万一程序无法按预期运行,意外删除原始文件。

程序必须执行以下操作:

  • 在当前工作目录中找到所有CSV文件。
  • 读入每个文件的全部内容。
  • 将内容(跳过第一行)写到新的CSV文件中。

在代码级别,这意味着程序将需要执行以下操作:

  • os.listdir() 循环遍历文件列表,跳过非CSV文件。
  • 创建一个CSV reader 对象读取文件的内容,并使用 line_num 属性确定要跳过的行。
  • 创建一个CSV writer 对象,并将读入的数据写到新文件中。
#! python3
# removeCsvHeader.py - Removes the header from all CSV files in the current working directory.

import csv, os

os.makedirs('headerRemoved', exist_ok=True)

# Loop through every file in the current working directory.
for csvFilename in os.listdir('.'):
	if not csvFilename.endswith('.csv'):
		continue    # skip non-csv files
		
	print('Removing header from ' + csvFilename + '...')
	
	# Read the CSV file in (skipping first row).
	csvRows = []
	csvFileObj = open(csvFilename)
	readerObj = csv.reader(csvFileObj)
	for row in readerObj:
		if readerObj.line_num == 1:
			continue    # skip first row
		csvRows.append(row)
	csvFileObj.close()
	
	# Write out the CSV file.
	csvFileObj = open(os.path.join('headerRemoved', csvFilename), 'w', newline='')
	csvWriter = csv.writer(csvFileObj)
	for row in csvRows:
		csvWriter.writerow(row)
	csvFileObj.close()

JSON 和 API

JSON 是一种将数据格式化为人类可读字符串的流行方式。

这是一个格式为JSON的数据示例:

{"name": "Zophie", "isCat": true,
 "miceCaught": 0, "napsTaken": 37.5,
 "felineIQ": null}

JSON非常有用,因为许多网站都提供JSON内容作为程序与网站进行交互的方式。这被称为提供应用程序编程接口(API: Application Programming Interface)。
访问API与通过URL访问任何其他网页相同。不同之处在于,API返回的数据是针对计算机格式化的(例如,使用JSON);API对于人们来说不易阅读。

许多网站都以JSON格式提供其数据。Google等许多流行网站都提供 API 以供程序使用。你需要找到相关文档,了解程序需要请求哪些URL才能获取所需的数据,以及返回的JSON数据结构的常规格式。该文档应由提供API的任何网站提供;如果他们有“开发人员”页面,请在此处查找文档。

可以在 https://nostarch.com/automatestuff2/ 的资源中看到JSON API的一些示例。


json 模块

Python 中的 json 模块可以处理带有JSON数据的字符串和Python值之间转换的细节。
JSON不能存储每种Python值。它只能包含以下数据类型的值:字符串,整数,浮点数,布尔值,列表,字典和 NoneType。

使用 load() 函数读取JSON

函数名 loads 意思是 load string

>>> stringOfJsonData = '{"name": "Zophie", "isCat": true, "miceCaught": 0, "felineIQ": null}'
>>> import json
>>> jsonDataAsPythonValue = json.loads(stringOfJsonData)
>>> jsonDataAsPythonValue
{'isCat': True, 'miceCaught': 0, 'name': 'Zophie', 'felineIQ': None}

注意,JSON字符串始终使用双引号。

使用 dumps() 函数编写JSON

函数名 dumps 意思是 dump string

>>> pythonValue = {'isCat': True, 'miceCaught': 0, 'name': 'Zophie', 'felineIQ': None}
>>> import json
>>> stringOfJsonData = json.dumps(pythonValue)
>>> stringOfJsonData
'{"isCat": true, "felineIQ": null, "miceCaught": 0, "name": "Zophie" }'

项目:获取当前天气数据

实现功能:下载接下来几天的天气预报并将其打印为纯文本,查询天气。该程序使用第12章中的 requests 模块从Web下载数据。

该程序执行以下操作:

  • 从命令行读取请求的位置。
  • 从 OpenWeatherMap.org 下载JSON天气数据。
  • 将 JSON 数据的字符串转换为 Python 数据结构。
  • 打印今天和接下来两天的天气。

因此,代码需要执行以下操作:

  • sys.argv 中加入字符串以获取位置。
  • 调用 request.get() 以下载天气数据。
  • 调用 json.loads() 将JSON数据转换为Python数据结构。
  • 打印天气预报。

在浏览器中访问 https://openweathermap.org/api/ 并注册一个免费帐户以获取API密钥(也称为应用程序ID),对于 OpenWeatherMap 服务,该密钥是一个类似于 '30144aba38018987d84710d0e319281e" 的字符串代码。对API密钥保密;任何知道它的人都可以编写脚本使用你帐户的使用配额。

OpenWeatherMap 服务要求查询的格式设置为:城市名称,逗号和两个字母的国家/地区代码(例如美国的 “US”)。可以在 https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 中找到这些代码的列表。

#! python3
# getOpenWeather.py - Prints the weather for a location from the command line.

APPID = 'YOUR_APPID_HERE'

import json, requests, sys

# Compute location from command line arguments.
if len(sys.argv) < 2:
	print('Usage: getOpenWeather.py city_name, 2-letter_country_code')
	sys.exit()
location = ' '.join(sys.argv[1:])

# Download the JSON data from OpenWeatherMap.org's API.
url ='https://api.openweathermap.org/data/2.5/forecast/daily?q=%s&cnt=3&APPID=%s ' % (location, APPID)
response = requests.get(url)
response.raise_for_status()	# check for errors

# Uncomment to see the raw JSON text:
#print(response.text)  

# Load JSON data into a Python variable.
weatherData = json.loads(response.text)

# Print weather descriptions.
w = weatherData['list']
print('Current weather in %s:' % (location))
print(w[0]['weather'][0]['main'], '-', w[0]['weather'][0]['description'])
print()
print('Tomorrow:')
print(w[1]['weather'][0]['main'], '-', w[1]['weather'][0]['description'])
print()
print('Day after tomorrow:')
print(w[2]['weather'][0]['main'], '-', w[2]['weather'][0]['description'])

命令行参数在空格上分割。命令行参数 San Francisco, US 将使 sys.argv 存储['getOpenWeather.py', 'San', 'Francisco,', 'US']。因此,调用 join() 方法以连接除 sys.argv 中的第一个字符串以外的所有字符串。将此连接的字符串存储在名为 location 的变量中。

response.text 成员变量存储JSON格式数据的字符串。JSON数据类似下面这样:

{'city': {'coord': {'lat': 37.7771, 'lon': -122.42},
          'country': 'United States of America',
          'id': '5391959',
          'name': 'San Francisco',
          'population': 0},
 'cnt': 3,
 'cod': '200',
 'list': [{'clouds': 0,
           'deg': 233,
           'dt': 1402344000,
           'humidity': 58,
           'pressure': 1012.23,
           'speed': 1.96,
           'temp': {'day': 302.29,
                    'eve': 296.46,
                    'max': 302.29,
                    'min': 289.77,
                    'morn': 294.59,
                    'night': 289.77},
           'weather': [{'description': 'sky is clear',
                        'icon': '01d',
--snip--

当使用命令行参数 getOpenWeather.py San Francisco, CA 运行该程序时,输出看起来像这样:

Current weather in San Francisco, CA:
Clear - sky is clear

Tomorrow:
Clouds - few clouds

Day after tomorrow:
Clear - sky is clear

实践项目

Excel 转 CSV 工具

使用第13章中的 openpyxl 模块,编写一个程序,该程序读取当前工作目录中的所有 Excel 文件并将其输出为 CSV 文件。
一个 Excel 文件可能包含多个工作表,必须为每个表创建一个 CSV 文件。CSV文件的文件名应为 _ .csv

import openpyxl, csv

for xlsxFilename in os.listdir('.'):
	# Skip non-xlsx files, load the workbook object.
	if not xlsxFilename.endswith('.xlsx'):
		continue    # skip non-xlsx files
	wb = openpyxl.load_workbook(xlsxFilename)
	
	for sheetName in wb.sheetnames:
		# Loop through every sheet in the workbook.
		sheet = wb[sheetName]
		
		# Create the CSV filename from the Excel filename and sheet title.
		csvFileName = xlsxFilename[:-5] + "_" + sheet.title + ".csv"	# the filename of the Excel file without the file extension
		
		# Create the csv.writer object for this CSV file.
		csvFile = open(csvFileName, 'w', newline='')
		csvWriter = csv.writer(csvFile)
		
		# Loop through every row in the sheet.
		for rowNum in range(1, sheet.max_row + 1):
			rowData = []    # append each cell to this list
			# Loop through each cell in the row.
			for colNum in range(1, sheet.max_column + 1):
				# Append each cell's data to rowData.
				rowData.append(sheet.cell(row=rowNum, column=colNum).value)
				
			# Write the rowData list to the CSV file.
			csvWriter.writerow(rowData)
			
		csvFile.close()

从 https://nostarch.com/automatestuff2/ 下载文件 excelSpreadsheets.zip,将它们用作测试程序的文件。


【python 让繁琐工作自动化】目录
学习网站:https://automatetheboringstuff.com/2e/chapter16/

你可能感兴趣的:(Python学习笔记,python,csv,json)