这是“Python1024-自动化办公”的第一篇。
在Python1024的基础篇中,我们已经介绍过文件管理和文本文件的读写:
《编程的第一个应用,往往都从文件读写开始》
在路径处理方面,Python3.6版本后,建议采用pathlib
,它采用面向对象封装接口,使用起来比os.path
更人性化。
在学习后续章节前,有必要先介绍几个基本的概念,方便后续的理解。
1、常见文件格式
文本文件其实是一种特殊的二进制文件,它约定了以字符格式的读取方式,字符符合ASCII或UNICODE等编码标准。因为文本文件太通用、太特殊(字符形式)了,我们把它单独列出对待。
其他二进制文件则随应用的不同,有各式各样的读写规则。比如:
- PDF(Portable Document Format)文件,定义了一种独立于应用程序、硬件、操作系统的方式呈现文档的文件格式。它内部包含了大量类型的子元素,通过索引的方式嵌入在不同的位置。只有符合其规范的软件,才能读取,甚至修改PDF文件。PDF文件格式标准由Adobe定义:参考官网。
- 微软2007版本之后的Office文件,比如docx、pptx、xlsx,属于OpenXML文件格式。它是由微软开发的基于XML和ZIP压缩技术的文件规范,并于2008年正式成为国际标准。可以用解压缩软件打开这类文件,就能看到其内部结构。目前除了微软Office软件,我们也可以通过金山WPS、OpenOffice等软件打开其文档。
- 图像文件的格式非常多,根据压缩算法不同,会设计不同的文件格式,比如:JPEG、TIFF、RAW、BMP、GIF、PNG。归根到底,图像代表的是色彩数据,是整块的内容,而描述其数据结构的,是文件头部信息。
- 音频文件,保存的是声音信号的数字化,和图像一样,文件不同主要源于压缩算法的不同。我们常见的音频文件格式如:MP3、WAV、AAC、FLAC等,声音信号被数字化后保存在数据块中,同时由文件头描述数据块。关键信息如采样率、比特率、声道数。采样率是时间相关的概念,即每秒收集多少次声音样本信息。
- 视频文件,保存的是图像和音频的合集,即视频文件可以拆分为视频和音频,其中视频是图像的合集,每秒钟有多少张图像,就是它的FPS帧率,帧率决定了视频的流畅度。此外,每张图像的分辨率多大,决定了它的清晰度,比如我们常说的720P代表1280×720的分辨率,1080P就是1920×1080的分辨率,即代表用1920×1080个像素点来表达一张图片信息。所以,视频文件尤其大,根据压缩算法的不同,常见的文件格式如:MP4、MOV、FLV、WMV、WEBM等。
当然,除了上面这些文件格式外,我们还可以定义自己的格式,只要定义的格式,能被对应软件支持打开和写入即可。换句话说,如果你定义的文件格式,没人写出软件去支持,那就没办法应用。或者,你自己写了软件,但大部分人都不知道,或不想用,那它也就丧失了应用价值。在软件行业向互联网演进过程中,淘汰了大量的软件,才诞生出目前相对稳定的互联网基础设施。
Youtube上有人做了一个视频,呈现了90年代到2020年间最受欢迎浏览器的更替:视频地址。
2、文本文件也有分类
文本文件,本质上是用于存储字符的文件。
所有那些以字符保存的文件格式,其实都是文本文件,只不过文本内容还有额外特定含义。
常见的比如:CSV、HTML、XML、JSON、JS、CSS,还有各种语言的代码。
2.1 CSV文件
CSV文件常用于保存数据表格,比如:
姓名, 电话, 地址
张三, 18900000001, 上海
李四, 13800000002, 广州
这就是一个典型的CSV文件,我们可以用文本编辑器打开它,也可以用Excel等软件打开它。如果用Excel软件打开它,Excel会把英文逗号作为分隔符号,提取单元格内容,我们看到的就是一张表格。
Python同样也支持这类文件的读写,通过csv模块:
import pathlib
import csv
path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/001basic')
csv_path = path.joinpath('hello.csv')
with open(csv_path ,'r') as f:
f_csv = csv.reader(f)
headers = next(f_csv)
for row in f_csv:
for h, v in zip(headers, row):
print(f'{h.strip()}: {v.strip()}')
这样就能读出CSV文件中的数据:
姓名: 张三
电话: 18900000001
地址: 上海
姓名: 李四
电话: 13800000002
地址: 广州
2.2 XML文件
XML(eXtensible Markup Language)是为了结构化存储和传输数据而生,是一种标记语言。
<?xml version="1.0" encoding="UTF-8"?>
<mail>
<to>World</to>
<from>程一初</from>
<title>Hello World</title>
<body>Welcome to Python1024!</body>
</mail>
我们可以自定义标记,一种定义就是一种数据格式。在Python中,有三种处理数据的方式:
- DOM(Document Object Model):文档对象模型,是W3C组织推荐的标准编程接口。它把XML文件映射到内存中,以“树”的数据结构来操作数据。
- SAX(simple API for XML):事件驱动模型,虽然不是标准,但应用广泛。它逐行扫描文档,边扫边解析,这样就不用一下子全装载到内存了。
- ElementTree:性能介乎于DOM和SAX之间,使用门槛低。
以ElementTree为例:
import pathlib
import xml.etree.ElementTree as ET
path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/001basic')
xml_path = path.joinpath('hello.xml')
tree = ET.parse(xml_path)
root = tree.getroot() # 根节点
print('root_tag:', root.tag) # 根标签:mail
for elem in root:
print(f'{elem.tag}: {elem.text}')
此外,HTML是XML的一个子集,即HTML是一种特殊的XML,它定义了一整套标签规范,比如文字、超链接、表单等,按照这套规范来呈现HTML的应用就是浏览器了。当然,浏览器除了支持HTML,还需要支持JS脚本、CSS样式文件等其他规范。
2.3 JSON文件
相比XML文件格式,JSON文件格式更精简。同样的信息可以用更少的字符表示:
{
"mail": {
"to": "World",
"from": "程一初",
"title": "Hello World",
"body": "Welcome to Python1024!"
}
}
少了对称标记以及<>符号,JSON需要占用更少标记数据,所以它更常被用于互联网应用的数据传输。Python也内置了处理模块json:
import pathlib
import json
path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/001basic')
json_path = path.joinpath('hello.json')
with open(json_path, 'r') as f:
data = json.loads(f.read())
print(f'root_tag: {list(data.keys())[0]}')
for k, v in data['mail'].items():
print(f'{k}: {v}')
2.4 代码文件
代码文件,比如Python代码文件,也是一种文本文件。所以,有一些静态代码检测工具,如pylint、pep8、flake8等,可以读取代码文件后检查编写质量。甚至,你可以编写程序自动生成代码,在自动化测试中用的比较多。
代码文件的读取和其他文本文件一样,都可以用open()函数打开,但是要解析代码文件,就必须用“语法树”来解析,它也提供了对应的ast模块。比如我们写一个简单的Python代码文件,包含一个注释块,以及一行代码:
'''
Author: 程一初
'''
print('hello world!')
然后用语法树解析它:
import pathlib
import ast
path = list(pathlib.Path.cwd().parents)[1].joinpath('data/automate/001basic')
py_path = path.joinpath('hello.py')
with open(py_path, 'r') as f:
node = ast.parse(f.read())
# 获取文档注释
print(ast.get_docstring(node))
class MyVisitor(ast.NodeVisitor):
# 定义一个遍历代码节点的类
# 按需重定义generic_visit函数
def generic_visit(self, node):
print(node)
super(MyVisitor, self).generic_visit(node)
v = MyVisitor()
v.visit(node)
会得到这样的结果:
Author: 程一初
<_ast.Module object at 0x118e4e890>
<_ast.Expr object at 0x118ce1b50>
<_ast.Str object at 0x118ce1f90>
<_ast.Expr object at 0x118ce1d90>
<_ast.Call object at 0x118ce1ad0>
<_ast.Name object at 0x118ce1b10>
<_ast.Load object at 0x103ed39d0>
<_ast.Str object at 0x118eb3ed0>
第一行为注释文档,剩下8行为ast内部对象,包括模块、表达式、字符串、函数名、调用等。这里只是举个例子,说明代码文件也是一种特殊的文本文件。平时应用中,我们几乎不会这么去读写代码文件,而是直接用Python解释器来执行代码。
总结
了解什么是文件,就能明白每个文件都有自己的规范,想要处理某类文件,就得先找到符合规范的应用,或者模块。找到模块后,就可以通过结构化的代码来批量处理了。
常见的自动化处理无非就这几种:
- 批量文件处理,比如某个文件夹下的所有文件统一重命名;
- 根据提前设定的规则,自动分类处理文件,比如按文件后缀调用不同程序处理;
- 组成流水线:把重复的工作提炼出标准,把每一步程序化后再组装起来。
Python的魅力,就是能把所有重复的工作,按模块组织起来,形成流水线,一个人发挥N个人的效率。