Automate the Boring Stuff with Python: Practical Programming for Total Beginners (2nd Edition)
Copyright © 2020 by Al Sweigart.
CSV 和 JSON 文件都是纯文本文件。
csv
模块可解析 CSV 文件。csv
模块是 Python 自带的。json
模块可以解析 JSO文件。CSV 文件中的每一行代表电子表格中的一行,并用逗号分隔该行中的单元格。
CSV 文件缺少 Excel 电子表格的许多功能。
CSV 文件的优点是简单。它可以在文本编辑器中查看,是表示电子表格数据的直接方法。许多程序类型都支持 CSV 文件。CSV 格式正如它所展现的那样:它只是一个以逗号分隔的值的文本文件。
由于 CSV 文件只是文本文件,可能会有人使用在第9章中学到的技术来处理该文件。比如,在文本的每一行上调用 split(',')
,以逗号分隔的值作为字符串列表。
但是,并非 CSV 文件中的每个逗号都代表两个单元格之间的边界。CSV 文件还具有其自己的转义字符集,以允许逗号和其他字符作为值的一部分包括在内。
因此应使用 csv
模块读取和写入 CSV 文件。
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]
来访问指定行和列中的值:
对于大型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 对象。
>>> 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!” 中的逗号进行转义。
下面的程序使用制表符而不是逗号来分隔单元格,并且将行以双倍行距隔开:
>>> 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)。
对于包含标题行的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()
的键的顺序进行写入的。{'Name':'Bob','Phone':'555-9999'}
中的 'Pet'
。实现功能:该程序将需要在当前工作目录中打开每个扩展名为.csv的文件,读取CSV文件的内容,并将内容(不包括第一行)重写为同名文件。
警告:每当编写修改文件的程序时,请确保首先备份文件,以防万一程序无法按预期运行,意外删除原始文件。
程序必须执行以下操作:
在代码级别,这意味着程序将需要执行以下操作:
os.listdir()
循环遍历文件列表,跳过非CSV文件。line_num
属性确定要跳过的行。#! 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 是一种将数据格式化为人类可读字符串的流行方式。
这是一个格式为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的一些示例。
Python 中的 json 模块可以处理带有JSON数据的字符串和Python值之间转换的细节。
JSON不能存储每种Python值。它只能包含以下数据类型的值:字符串,整数,浮点数,布尔值,列表,字典和 NoneType。
函数名 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
意思是 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下载数据。
该程序执行以下操作:
因此,代码需要执行以下操作:
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
使用第13章中的 openpyxl
模块,编写一个程序,该程序读取当前工作目录中的所有 Excel 文件并将其输出为 CSV 文件。
一个 Excel 文件可能包含多个工作表,必须为每个表创建一个 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/