This commit is contained in:
louzefeng
2024-07-11 05:50:32 +00:00
parent bf99793fd0
commit d3828a7aee
6071 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,248 @@
<audio id="audio" title="01 | 拆分与合并如何快速地批量处理内容相似的Excel" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/02/b4/025800e6494b54df2b6bc607b0cf0ab4.mp3"></audio>
你好我是尹会生。今天是咱们的第一节课我先带你学习下如何用Python操作Excel。
Excel是我们在工作中用到的最频繁的软件之一它有着强大的计算能力和便捷的图表功能。如果我们要在同一个Excel文件中进行操作手工进行也很方便但问题是如果我们需要同时操作多个Excel文件就是一件非常耗时的事情了。
在工作场景中需要同时操作多个Excel的情况主要有2种批量合并和批量拆分。我来带你看2个场景。
- 批量合并。假设你需要对某些工作内容进行问卷调查这时你用Excel做了调查问卷模版。我想你会这样做先把Excel通过工作群分发给所有员工再把群里收集到的反馈附件汇总成一个文件。
- 批量拆分。假设你是公司的财务人员你需要使用Excel对员工工资进行核算之后再打印出来。但是公司要求员工薪水保密所以每个员工的工资需要拆分成一个独立的文件最后还需要打印出来。
无论是合并还是拆分我们都面临着一个困境没有现成的软件可以实现多个Excel文件的合并和拆分操作所以你只好对每一个Excel文件都进行“打开-复制粘贴-保存”的工作。
很多人在面对这样的工作需求时,都**忍不住立马去做,却很少停下来分析问题**。其实,这三步是很简单的工作,不过也是无意义的重复工作,既浪费了时间,又没有真正产生价值。
幸运的是这些工作都可以通过Python来解决。今天我就给你介绍一下用Python实现重复工作自动化快速实现Excel的合并和拆分的方法。
## 如何用Python手工操作一个Excel文件
刚刚我们说到批量处理其实也就是逐一处理多个文件。如果我们想要提升这类工作的效率就可以先借助Python把每一次处理都自动化。所以在讲具体的合并和拆分方法前我们要解决的第一个问题就是用Python代替你的双手来操作一个Excel文件。
如果要用Python操作Excel文件首先就要支持读写Excel的功能。**在Python中要想实现对某一个功能的支持就需要安装扩展库。**
支持Excel读取的扩展库叫做xlrd库支持Excel写入的扩展库叫做xlwt库。我们可以使用下面的命令行进行安装
```
pip3 install xlrd
pip3 install xlwt
```
那么如何使用Python读取Excel文件的内容呢你可以使用这样的代码实现
```
import xlrd
file = '/Users/user1/Desktop/a.xlsx'
data = xlrd.open_workbook(file)
table = data.sheets()[0]
value = table.cell_value(rowx=4, colx=4)
```
虽然代码很简单但是它对自动化操作Excel的意义非常大。
试想一下如果能够使用Python替代全部的手工操作大批量的文件就可以使用Python的循环功能自动化完成对每一个文件的自动处理工作了。
对于编程语言来说,文件合并的步骤可以分解为读取第一个文件,读取第二个文件,将第一个文件的内容追加到第二个文件下方。
所以在我们学会使用Python读取Excel文件之后我们还需要掌握如何将读取的内容写入到Excel文件。写入文件的代码如下
```
import xlwt
dst_file = '/Users/edz/Desktop/文章1/result/结果.xlsx'
workbook = xlwt.Workbook(encoding='utf-8')
xlsheet = workbook.add_sheet(&quot;统计结果&quot;)
# 写入内容,假设取出的内容是value
xlsheet.write(0, 0, value)
# 保存文件
workbook.save(dst_file)
```
可以看到写入文件的时候我们使用了一个叫做write的函数。它的前两个参数代表的写入位置分别是指定写入的行和列坐标。无需多言这个写入位置非常重要。如果按照上面的代码方式写入也就是前两个参数均指定为0就会覆盖这个Excel文件中的现有内容了。
所以你如果想完成合并操作的话就要实现对现有Excel内容进行追加写入。通常我们会先获取现有的内容一共有多少行、多少列然后向后移动一个位置再进行写入。
这种追加写入的方式在我们的工作场景中非常常见。接下来我们就看看怎么用追加写入的方式实现多个Excel文件的合并吧。
## 怎样实现Excel的合并
我们还是用前面提到的做调查问卷模板的场景来具体讲一讲怎么实现Excel的合并。这里我们就要用到一个重要功能了**循环功能**。
循环功能的核心代码是:
```
from pathlib import Path, PurePath
# 指定要合并excel的路径
src_path = '/Users/edz/Desktop/文章1/调查问卷'
# 取得该目录下所有的xlsx格式文件
p = Path(src_path)
files = [x for x in p.iterdir() if PurePath(x).match('*.xlsx')]
```
在这段代码中我使用了for语句实现了Python的循环功能。通过这样的功能我可以依次获取src_path变量指向的路径下所有的文件。同时为了避免这个目录里的文件类型过多我使用一个if语句用于条件判断只提取.xlsx结尾的文件。
现在用Excel实现调查问卷自动化的主要功能已经都实现了。接下来我们看看怎样实现整个工作过程。我把它们的工作流程定义为三个步骤
1. 找到整个工作过程当中重复操作的部分;
1. 将重复操作的部分需要哪些手工操作找出来使用Python编写程序代替手工操作的部分
1. 对重复的部分,使用循环语句进行批量处理。
如果你对把手工操作改成Python程序还没有任何经验的话我还可以为你提供更直观的办法叫做**画时序图**。
我先带你看看时序图是什么呢?如下图表:
<img src="https://static001.geekbang.org/resource/image/b2/90/b2da3c7bfd1c80888d0c4776317fb090.png" alt="">
简单来讲,**时序图就是以时间顺序来排列程序中的事件的图表**。通过上图,你应该很容易看出重复操作的这四个事件:
1. 打开文件;
1. 提取用户填写内容;
1. 粘贴到汇总文件;
1. 关闭文件。
下面我们就用Python来替代这四个事件。由于它们是重复的所以我会使用for循环对它们依次进行处理。
回到我们的Excel做调查问卷的场景。当我们回收了调查问卷之后每份问卷的格式是完全相同的刚好可以利用上面提到的循环功能处理每份问卷。而问卷的选项则是我们需要提取出来用于汇总的所以我们要使用Python实现读取Excel调查问卷的功能最后再写入到一个新的Excel中。
好了我们来看下这一功能的Python代码是如何编写的
```
import xlrd
import xlwt
from pathlib import Path, PurePath
# 导入excel和文件操作库
# 指定要合并excel的路径
src_path = '/Users/edz/Desktop/文章1/调查问卷'
# 指定合并完成的路径
dst_file = '/Users/edz/Desktop/文章1/result/结果.xlsx'
# 取得该目录下所有的xlsx格式文件
p = Path(src_path)
files = [x for x in p.iterdir() if PurePath(x).match('*.xlsx')]
# 准备一个列表存放读取结果
content = []
# 对每一个文件进行重复处理
for file in files:
# 用文件名作为每个用户的标识
username = file.stem
data = xlrd.open_workbook(file)
table = data.sheets()[0]
# 取得每一项的结果
answer1 = table.cell_value(rowx=4, colx=4)
answer2 = table.cell_value(rowx=10, colx=4)
temp = f'{username},{answer1},{answer2}'
# 合并为一行先存储起来
content.append(temp.split(','))
print(temp)
# 输出
# 韩梅梅,D,B
# 李雷,D,C
# 准备写入文件的表头
table_header = ['员工姓名', '第一题', '第二题']
workbook = xlwt.Workbook(encoding='utf-8')
xlsheet = workbook.add_sheet(&quot;统计结果&quot;)
# 写入表头
row = 0
col = 0
for cell_header in table_header:
xlsheet.write(row, col, cell_header)
col += 1
# 向下移动一行
row += 1
# 取出每一行内容
for line in content:
col = 0
# 取出每个单元格内容
for cell in line:
# 写入内容
xlsheet.write(row, col, cell)
# 向右移动一个单元格
col += 1
# 向下移动一行
row += 1
# 保存最终结果
workbook.save(dst_file)
```
在这段代码中Excel的读取和写入操作、for循环操作都派上了用场它的整个工作过程就像我画的时序图一样**先打开用来汇总的Excel文件依次对多个调查问卷进行读取最后逐行写入到新建立的汇总文件中**。
合并后的效果如下图:
<img src="https://static001.geekbang.org/resource/image/a1/f4/a1ef2630faec5458fbcef4c7b45aaaf4.png" alt="">
有一点需要你注意的是为了让你更好地理解Python循环的工作过程我没有严格遵守编码规范而是尽可能地让程序按照从上到下的顺序执行。如果你已经不是第一次使用Python可以根据你的使用习惯将程序进一步封装成Python里的函数更加优雅地去实现它。
总的来说在使用Python对Excel的合并操作时需要你掌握的最核心的操作就是读写文件和行列坐标控制。熟练掌握这两个功能你才能一次得到想要合并成的样子不用再对Excel进行二次的手工操作。
## 怎样实现Excel的拆分
对于批量操作Excel还有一种情况是批量拆分。比如很多公司会用Excel记录和统计员工的薪水、记录货物信息、记录客户情况等数据。这些数据越来越多之后文件会越来越大打开文件和查找速度就会变得很慢最后只好按照某些列进行Excel的拆分。
接下来我就为你讲解一下如何进行Excel的批量拆分。让我们来看一个工资条的案例。
例如我在一个Excel中存放了工资信息需要把第一行的表头和员工工资拆分成一个以员工名字命名的Excel文件。我来带你看下具体该怎么操作
<img src="https://static001.geekbang.org/resource/image/d6/0b/d66b559b7603f2698b4d4e8c81196c0b.png" alt="">
如果把拆分工作也画成时序图就会发现逐行读取可以使用循环功能批量操作对每一行的内容处理如果能使用Python进行自动化的话一个Excel拆分的工作就全部能使用Python自动化实现了。所以我打算设计一个for循环语句用于遍历所有的行在for循环语句当中实现对每一行具体内容的处理。
我把文件拆分的关键代码写了出来,你可以参考一下:
```
for line in range(1,employee_number):
content = table.row_values(rowx=line, start_colx=0, end_colx=None)
# 将表头和员工数量重新组成一个新的文件
new_content = []
# 增加表头到要写入的内容中
new_content.append(salary_header)
# 增加员工工资到要写入的内容中
new_content.append(content)
# 调用自定义函数write_to_file()写入新的文件
write_to_file(filename = content[1], cnt = new_content)
```
在这段代码的第一行我使用了一个range函数它会生成从1到员工总数的数字范围。你可能会问为什么没有直接写出Excel中总员工的数量而是使用employee_number这样一个变量呢
这是因为如果直接写出员工数量一旦遇到员工入职或员工离职等情况你就需要根据Excel中的行数重新编写Python代码而我现在使用的方式是每次打开Excel文件会自动统计员工的数量即行数这种编写代码的方式能够让你的程序有更好的扩展性一般这种方式用于处理文件内容经常变动的情况。
文件的批量拆分也是通过循环来实现逐行处理的功能的但是你需要注意拆分以后的要保存的文件名称不要重复不然很容易导致Excel中只有最后一次循环写入的内容。
## 小结
今天我为你讲解了如何使用Python代替手工进行Excel的批量合并和拆分操作。一般来说批量合并和拆分主要有几个步骤
1. 手动或借助时序图找到需要重复操作的部分。
1. 将重复的部分用Python实现自动化操作。
1. 对Python实现自动化的脚本,再用循环功能实现批量操作。
Python有着丰富的扩展库**当你掌握了熟练操作Excel的方法之后对于WPS等其他办公软件也可以通过相应的API进行类似的合并和拆分操作**。
除了基本操作呢,我还给你介绍了分析问题的方法——时序图。通过时序图,你可以找到那些运行逻辑中重复的部分,利用循环完成自动化操作。
不管是利用Python代替手工操作节约时间还是使用循环代替手工多次执行减少工作难度这些都是提升工作效率的有效办法。希望你能在处理日常工作时多思考有意识地把你的日常办公工作自动化。
## 思考题
欢迎你在课程后留言告诉我使用Python解决了你工作中的哪些重复性问题。
如果你觉得这节课有用,能解决你的办公效率问题,欢迎你点击“请朋友读”,分享给你的朋友或同事。

View File

@@ -0,0 +1,284 @@
<audio id="audio" title="02善用Python扩展库如何批量合并多个文档" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/85/24/85aa07467e9271376c43232bd6e52124.mp3"></audio>
你好,我是尹会生。
在日常工作中我们打交道最多的文件就要数Word和Excel了。我们经常面临这么一种场景需要将Excel的内容合并到Word中。你可以想一想完成这个需求需要手动进行几个步骤的操作呢很显然有4步。
- 首先要手动打开Excel、Word文件
- 接着复制一个单元格的文字到Word指定位置
- 然后,如果有多个单元格,就需要重复复制多次;
- 最后保存Word文件并关闭Excel和Word文件。
如果只有两个文件这几步手动操作一定不成问题不会耗费太多的时间。但是如果文件特别多哪怕只有十几个手动操作就相当耗费时间了而且一不小心还容易出错。幸运的是现在我们可以通过Python来实现批量文件合并功能你只需要执行一个Python程序就能搞定所有文件的合并操作。
所以今天这节课我们先从比较简单的内容讲起用Python自动合并两个Word文件。然后再进阶学习如何合并Word和其他类型的文件。一步一步来相信你会掌握得既牢固又扎实。
# 手工操作和用Python操作的区别
首先我们要知道为什么在合并文件的时候用Python更高效。我用一个例子来给你讲解手工操作和用Python操作的区别。比如下面这一段文字
>
简易流**程**——集团原则在每年**1<strong><strong>**</strong>5</strong>月、**7<strong><strong>**</strong>11</strong>月生产工作任务较重或**考核时间较紧**的情况下运用。
在Word文件中不但有文字内容还有加粗、红色等格式而且这些特殊的格式和文字内容是混合在一起的。
但如果用Python来读取Word文件这段文字会被分为纯文字、段落、字体、字号以及表格等更具体的部分而且每一个部分都对应着Python的变量和函数。
这样一来,你就可以根据自己的需求,只提取某一具体部分的内容。比如说,你看到哪一段文字的字体很好看,就可以直接读取之后套用到新的文字段落就行,非常便捷。
不过用Python读取文件时你需要记住很多个Python变量和函数。当然了针对这一点你也不用担心这些变量和函数在Python的Word扩展库[官方文档](https://python-docx.readthedocs.io/en/latest/)可以查看。所以如果你需要某个功能,但是不知道应该用什么变量和函数名称时,可以在官方文档中找到它的名字和描述信息。
总之用Python读取文件的方式是非常有助于提高工作效率的。所以接下来我们用Python处理Word文件时就需要通过刚才介绍的变量和函数来替代手动操作。
接下来我先带你学习怎样用Python合并多个Word文件然后再讲怎么把Word文件与纯文本、图片和Excel进行合并。
# 如何合并多个Word文件只保留文字内容
先从合并两个Word文件说起。假设你现在有两个Word文件需要进行文件中的文字合并操作。两个文件的内容分别是:
文件一(内容包含字体、字号、颜色等额外信息):
**1.简易流程**——集团原则在每年**1<strong><strong>**</strong>5</strong>月、**7<strong><strong>**</strong>11</strong>月生产工作任务较重或**考核时间较紧**的情况下运用。
文件二(内容文字出现在表格中):
<img src="https://static001.geekbang.org/resource/image/5e/fd/5e608e620e13e2c4f846b5d4766679fd.png" alt="">
我把两个文件的信息总结如下:
- 第一个文件中,字体使用了黑体和宋体字,此外还有红色字体和加粗等格式。现在我需要只提取其中的文字内容,不带任何格式。
- 第二个文件中文字被放在了一张表格里。现在我需要用Python把表格中的文字提取出来合并成一个新的文件。
我先把核心代码给你贴出来,然后再给你详细讲解具体的操作方法。
首先我们可以使用一段Python代码提取Word文件里的内容然后合并成一个文件。
```
import docx
def merge_without_format(docx_files: list):
'''
只获取内容进行合并
'''
# 遍历每个文件
for docx_file in sorted(docx_files):
another_doc = Document(docx_file)
# 获取每个文件的所有“段落”
paras = another_doc.paragraphs
# 获取所有段落的文字内容
# paras_content = [para.text for para in paras]
for para in paras:
# 为新的word文件创建一个新段落
newpar = doc.add_paragraph('')
# 将提取的内容写入新的文本段落中
newpar.add_run(para.text)
# 所有文件合并完成后在指定路径进行保存
doc.save(Path(word_files_path, 'new.docx'))
# 调用函数
merge_without_format(files)
```
在这段代码中你可以看到我使用了一个Python的扩展库它叫做python-docx这也是我想重点给你讲解的一个扩展库。
python-docx是Python专门用来编辑Word文档的库我在实现Word文档自动化操作的工作中经常会用到它。使用它的好处就是不必自己研究docx文件类型的底层实现细节你可以像操作.txt文本一样直接打开、修改和保存关闭文件。可以说python-docx扩展库降低了用户使用Python的复杂度。
我再举个例子展示一下具体的操作过程。例如python-docx库支持函数Document它实现了Word文件的打开功能底层也做了很多对Word格式的处理工作让你可以直接使用paragraphs变量就能读取一整段Word文件。
Document函数格式如下
```
Document(docx_file)
```
还有函数save也是python-docx扩展库提供的word文件保存函数。同样的save函数在底层也做了很多对docx格式兼容的操作。像下面的代码一样你就可以直接给这个函数传递一个文件路径然后进行保存。是不是降低了编写代码的难度呢?
```
doc.save(Path(word_files_path, 'new.docx'))
```
通过我举的例子你就能更直观地感受到Python扩展库的方便之处了。接下来我们再回到刚才那两个文件的合并合并之后的结果如下
```
1.简易流程——集团原则在每年1-5月、7-11月生产工作任务较重或考核时间较紧的情况下运用。
人力资源、生产、品管、财务等部门整理、提供绩效考核数据。
人力资源部门收集各部门提供的考核数据,依据员工绩效考核评分标准对集团所有员工进行绩效考核得分计算。
人力资源部门将核计的员工绩效考核结果提交部门经理确认后报集团主管领导核定。
```
现在你已经掌握了两个Word文件的合并方法了。但如果我想让这段程序适用于三个、四个甚至更多个Word文件的合并那该怎么操作呢?
一个好消息就是上面的代码我们不需要做任何修改就可以合并多个Word文件。因为我使用了一个叫做**函数**的功能。**函数有时候也被称作过程、方法****<strong>它的作用是将那些需要反复使用的代码组合在一起**</strong>
之前我们使用过函数这些函数是Python自带的或是扩展库提供的。这些函数我们可以直接拿来使用使用函数在计算机术语中被称作函数调用。通过函数我们可以实现程序的模块化多次使用可以多次调用从而减少代码的重复性。但如果你需要自己编写函数怎么办呢?
你可以将重复的代码功能写在自己定义的函数中在需要使用的地方调用就可以了。这种自己编写的函数就被称作自定义函数。自定义函数和Python自带的函数一样也可以实现减少代码重复性的作用。
关于自定义函数,你需要熟悉它们的相关语法,主要是函数名、函数定义和调用方法。通常编写一个函数要为函数并起一个名字,这个名字叫做函数名。当你需要使用函数的功能时,可以使用函数名加“()”的方式来使用它,而且使用一个函数一般被称作调用函数。
我把函数定义和函数调用的写法单独拿出来给你看下定义函数的格式是def后面跟着函数名称调用函数是函数名称后面跟着一个"()" ,这是它的语法格式:
```
# 定义一个函数
def 函数名(参数列表):
函数体
# 调用一个函数
函数名(参数)
```
知道了自定义函数的语法,接下来我们就可以在程序中使用自定义函数了。
举个简单的例子。像我在合并Word文件的程序中第2行的merge_without_format就是我定义的一个函数第24行merge_without_format(files) 就是对函数进行调用files叫做函数的参数。通过函数参数可以在调用函数的时候为函数指定要操作的对象。
相信你不难发现,使用函数以后,不但可以提高代码的重复利用率,还能提高代码的可读性。
那这段程序是怎样处理多个文件合并的呢? 我在调用函数merge_without_format时使用了files变量作为参数而files变量包含了大量的文件。因为是多个文件合并所以在函数中我使用了一个小技巧就是你熟悉的for循环语句for循环语句能够遍历files变量的值这样就可以将files指向的全部Word文件逐一进行文件内容的提取进行两两合并从而实现任意多个Word文件的合并操作。
通过对多个word合并我希望你能学会怎么提取Word中的文字内容如果你需要编写大量重复的代码可以将它们写成自己定义的函数。
# 怎样合并不同类型的文件?
通过上面的例子我们实现了Word文件之间的合并。在工作中我们经常需要处理Word和Txt文件、图片、Excel这些类型合并的情况又该如何操作呢接下来我就一个一个来讲一讲。
### 将纯文本和Word文件合并
如果是为了支持信息丰富我们Word和Txt合并之后保存到新的Word中会出现Txt里的字体字号和原有文件不统一的问题我们可以使用python-docx扩展库为Txt文件中的文字增加格式。
如果合并前Word文件是仿宋字体而且有下划线和红色字体我们将Txt合并之后如何进行字体、样式和颜色的统一呢我们可以使用下面这段代码。
```
def add_content_mode1(content):
'''
增加内容
'''
para = doc.add_paragraph().add_run(content)
# 设置字体格式
para.font.name = '仿宋'
# 设置下划线
para.font.underline = True
# 设置颜色
para.font.color.rgb = RGBColor(255,128,128)
```
首先我定义了一个叫做add_content_mode1的函数。考虑到Word合并Txt是否会有多个Txt进行合并操作所以我使用自定义函数功能。
当你需要对多个Txt进行合并就调用函数依次对它们进行处理这样你就不用编写重复的代码了这也是我在编写代码时进行提效的一个小技巧。
接下来我们将每个新合并的txt内容作为一个新的段落合并到原有的文字中这个功能使用python-docx的add_paragraph函数就可以增加了一个新的段落。
最后把这一段所有文字设置成和原有的Word统一的字体、下划线和颜色保证新的段落在格式上的统一。
在具体操作的时候我还要提醒你Word文件支持的格式丰富程度远远高于Txt文件所以当这两种格式丰富程度不一致的文件进行合并时要么向下兼容去掉Txt不支持的格式要么向上兼容对Txt进行格式再调整。否则容易出现合并之后仍需要手动调整格式的问题影响工作效率。
### 将图片和Word文件合并
我们再来看一下第2种情况怎么把图片和Word文件进行合并呢?
想一下,我们经常见到的图片格式就有.jpg、.png、.gif等由于这些格式应用范围广格式没有被商业软件加密所以python-docx库的add_picture函数就能实现把图片插入Word的功能。代码如下
```
from docx import Document
from docx import shared
doc = Document()
# 按英寸设置宽度,添加图片
doc.add_picture('test.jpg', width=shared.Inches(1))
```
那有没有被商业保护、不能直接支持的格式呢比如Pohotshop自带的.ps格式我们如果将.ps格式插入Word文档.ps格式不能被add_picture所支持就只能以附件的形式添加到Word文件中作为附件添加的文件无法直接展示图片的内容和add_picture相比不够直观。
所以如果不需要进行内容的加密等商业目的的时候,建议使用通用和公开格式,这些格式对编程语言的兼容性更好。
总的来说python-docx的功能非常强大除了将文本和图片合并到Word文件中还可以和第一节课我们学过的xlrd扩展库相配合将Excel和Word进行合并。
### 将Excel和Word文件合并
为了让你更好地理解如何进行Word和Excel文件的合并我用一个利用Excel和Word批量制作邀请函的例子来给你讲解。
我在Word中保存了邀请函的标准公文格式但是其中的被邀请人、性别先生、女士以及发出邀请的时间分别用“&lt;姓名&gt;”“&lt;性别&gt;”“&lt;时间&gt;”替代。邀请函格式如下:
**尊敬的 &lt;姓名&gt; &lt;性别&gt;**
**... 邀请函内容 ...**
**&lt;今天日期&gt;**
我在Excel的每一行中写了被邀请人的姓名、性别信息。格式如下
<img src="https://static001.geekbang.org/resource/image/32/ee/3268ea0a297fc76a38b711d7be73aaee.png" alt="">
现在我们需要将Excel和Word进行合并操作为每个被邀请人自动生成一个Word格式的邀请函。
虽然Word中自带的邮件功能可以批量制作邀请函但是在灵活性还是较差的。比如我要在邀请函制作完成的时候自动添加制作时间等功能就无法通过Word自带的邮件功能实现。接下来我就用Python来生成邀请函代码如下
```
def generat_invitation():
'''
生成邀请函文件
'''
doc = Document(invitation)
# 取出每一段
for para in doc.paragraphs:
for key, value in replace_content.items():
if key in para.text:
# 逐个关键字进行替换
para.text = para.text.replace(key, value)
file_name = PurePath(invitation_path).with_name(replace_content['&lt;姓名&gt;']).with_suffix('.docx')
doc.save(file_name)
```
对于这个问题,我是这样思考的。如果手动操作,我需要:
- 先将Excel中的每一行中的姓名、性别填入Word文件中
- 再将当前日期填入到Word文件中
- 最后再按照姓名另存为一个文件。
但如果使用Python来实现呢就会非常简单。首先在整个过程中Word文档是被反复使用到的所以对Word文档进行修改的这个动作我会将它写入到循环语句当中。
接着我需要一个循环语句来处理Excel里的每一行循环因为我们需要把Excel的每一行读取出来然后替换“&lt;姓名&gt;”“&lt;性别&gt;”。
最后我们要解决的就是替换问题了。python-docx功能非常强大它自带了替换函数--replace函数能够将"&lt;姓名&gt;""&lt;性别&gt;"替换成Excel真实的用户和性别。
我再用代码解释一下。对应上面的代码:
- 第7行的for循环实现了遍历每个段落功能para变量就是表示每个段落的变量。
- 第8行我们将excel提前处理为python的基础类型--字典(链接), for循环实现了姓名、性别的遍历。
- 第11行实现了内容的替换功能。
- 第12行我将姓名作为文件名称将.docx作为扩展名指定为新的文件名称通过第13行的save函数进行了邀请函的保存。
你看多次读取Word文件的循环、多次按行读取Excel文件的循环、替换的函数都有了那我们就可以实现自动化生成邀请函的功能了。
最终每张邀请函实现的效果如下图:
<img src="https://static001.geekbang.org/resource/image/40/82/407f6cf1b86e8142064f6e7939ce5682.png" alt="">
如你所见,我们在对不同类型文件进行合并时,要考虑不同的问题:
1. 对于支持格式丰富不同的文件时要考虑格式的兼容性;
1. 对于图片、音乐、视频和Word合并时要考虑是否是受到word支持的通用格式
1. 对于像Excel格式于Word合并时能实现更复杂的功能代码的复杂程度也会随之提高一般需要先分析功能再进行代码编写。
# 小结
通过上面对Word文件的批量处理我为你总结了Word和各种类型合并增效的几个通用法则。
- 首先尽量选择Word兼容的格式,这些格式往往也是python-docx库能直接支持的类型。
- 第二善于将手工操作转换为Python程序实现。如果无法直接转换为Python程序可以尝试将手工操作继续细化拆分。
- 第三反复在程序中出现的代码可以编写为函数功能函数可以让你的程序更健壮较短的代码数量也减少了出现Bug的机率。
# 思考题
在最后我也想留一个问题给你思考如果邀请函的格式从Word文件改为图片你将会如何去解决呢?
如果你觉得这节课有用,能解决你的办公效率问题,欢迎你点击“请朋友读”,分享给你的朋友或同事。

View File

@@ -0,0 +1,247 @@
<audio id="audio" title="03图片转文字如何提高识别准确率" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/9f/c5/9fd833067b40ebdb11dc518f329961c5.mp3"></audio>
你好,我是尹会生。
不知道你有没有遇见过这样的场景:在工作中,你遇见了一个紧急情况,对方给你发了一串儿聊天记录的长截图,当你处理完事情想要复盘的时候,必须要把这些记录处理成文字,然后就发现图片转文字过程中会出现很多问题,还需要自己二次手动校对。
经过不断尝试就发现用互联网上的AI产品可以非常准确地识别出图片中的印刷体文字。而且再通过Python还可以实现把识别到的文字进行格式处理、文件保存等自动化操作。
那么今天,我就给你介绍两种能够精准、快速地把图片转成文字的方式:在线识别和离线识别。我会给你讲解具体的操作方法,当你再遇见这样的需求的时候,就可以很轻松应对了。
## 图片转文字的两种处理方法
我先来对图片转文字的两种处理方法进行介绍。
目前能够达到较高文字识别正确率的一般分为两种识别方式:一种是文字识别工作都需要在网络侧完成的方式,我们称为在线识别;另一种是不需要互联网功能的,我们称作离线识别。
根据不同的工作场景,我会选择不同的方式实现文字识别。那么,接下来我就带你了解下这两种方式各自的特性。
先看第一种,在线识别的方式。
在线识别方式最大的优点就是它在初次进行文字识别的时候准确率非常高。比如对聊天截图中的识别准确率就高达99%。因为在线识别使用了人工智能领域的深度学习算法和文字识别相结合的技术,能够把图片转换成文字后,还能在语义上把相近的字进行二次纠正。
比如说被识别的内容包含英文单词“Hello”一旦它的字母“o”被识别成数字“0”在线识别软件就会根据上下文语境把这类错误纠正回来而这种二次纠正的功能在离线识别软件中是没有的。
不过在线识别软件也有它的缺点那就是识别文字的过程需要在公有云的服务器上完成。也就是说需要通过互联网把图片上传到服务器那么一旦图片过大或者图片数量比较多就会导致上传时间过长。我们知道一张高清图片至少有3MB大小根据个人的网络情况至少要达到秒级上传才行。这就意味着在大批量文字识别的场景中或对实时性要求很高的场景下在线识别是不能满足要求的。
另外,图片需要经过互联网传输,识别以后的图片该怎么保存,怎么销毁,是不是会被其他人得到,这些都是安全风险。总之,信息泄露的风险比较大。所以像公司的合同、财务资料等涉密程度比较高的扫描件,很少使用在线识别。
再看第二种,离线识别的方式。
这种方式在识别过程中不需要连接网络,节省了在线传输图片的时间,适合那些对实时性要求比较高或网络信号比较差的场景。
但是离线识别方式的问题就在于,初次识别文字的准确率比较低,识别完之后必须要经过人工二次纠正才行。所以在前期人工校对,花费的时间相对来说会比较长。
我把它们各自的优缺点做了一张表格,如下:
<img src="https://static001.geekbang.org/resource/image/89/2f/896412e86623262d3123f4bb0761e22f.png" alt="">
那接下来,我就带你学习一下这两种识别方式怎样具体实现。
## 怎么进行在线文字识别?
我们先来看怎么进行在线文字识别。
在线文字识别方式,识别的主要功能的需要放在公有云的服务器中才能实现,所以在代码实现中就要考虑用户验证和图片加密传输问题。
用户验证能确保识别的结果交还给你本人图片加密传输能确保图片上的信息不会被其他人窃取到。这些功能各个公有云的AI产品都考虑得非常周全一般会提供给用户一个扩展库。你要做的就是安装这些扩展库。
举个例子百度云的AI产品你可以在终端下执行这样一个命令来进行安装。
```
pip install baidu-aip
```
在这里我使用了百度云提供的在线文字识别产品它提供了一个baidu-aip的安装包安装之后提供了AipOcr函数实现用户验证、client.basicGeneral函数实现文字识别功能。代码如下
```
from aip import AipOcr
&quot;&quot;&quot; 你的 APPID AK SK &quot;&quot;&quot;
# APP_ID = '你的 App ID'
# API_KEY = '你的 Api Key'
# SECRET_KEY = '你的 Secret Key'
client = AipOcr(APP_ID, API_KEY, SECRET_KEY)
&quot;&quot;&quot; 读取图片 &quot;&quot;&quot;
def get_file_content(filePath):
with open(filePath, 'rb') as fp:
return fp.read()
image = get_file_content('example.png')
&quot;&quot;&quot; 调用通用文字识别, 图片参数为本地图片 &quot;&quot;&quot;
result = client.basicGeneral(image)
print(result)
```
在这段代码里实现了三个功能分别是用户验证、读取图片和识别图片。为了让你更直观地看到效果我来对一张电子发票的图片example.png进行识别。识别结果如下
<img src="https://static001.geekbang.org/resource/image/24/fa/2400754a55374cba0cd2df2979a9f6fa.png" alt="">
其实识别的准确率是非常高的,基本上没有错误。那接下来我具体给你介绍一下核心代码。
在代码的第一行我使用了一个AipOcr库。AipOcr是百度云提供给用户的OCR Python SDK客户端能够让你用Python语言和百度云进行交互。
一般情况下我们进行用户认证、图片上传至服务器功能都需要自己编写很多代码但是使用AipOcr库之后这些基础功能都被封装好了。你只需要填写三个变量就能正式进入文字识别的环节了
- 第一个变量是APP_ID它用来识别应用
- 第二个变量是API_KEY用于识别用户
- 第三个变量是SECRET_KEY ,用来加密密钥。
当把这三个变量传入AipOcr函数使用AipOcr函数通过互联网交互后就可以用来识别用户是不是被授权使用相应的产品之后就可以把图片加密发送到AI产品的服务器上了。
这些就是所有公有云识别用户的通用做法,我把这个做法整理为三个步骤。
第一步, 安装SDK。代码是
```
pip install baidu-aip
```
第二步,注册用户。
以百度云为例你需要登录https://ai.baidu.com/网址,以自己的手机为用户名注册一个新的用户。
第三步,申请应用。
成功登录网站之后你会进入服务控制台界面然后选择文字识别功能再新创建一个文字识别类型的应用。创建应用之后就可以在服务控制台中的应用列表中查看百度云提供的APP_ID、API_KEY、SECRET_KEY 三个变量。
在这三个变量中APP_ID在百度云的控制台中创建用户之后会自动创建。<br>
<img src="https://static001.geekbang.org/resource/image/f3/83/f34cd2e1ea9d2b97b6c130b181fb5783.png" alt="">
变量API_KEY与SECRET_KEY是在创建完毕应用后分配给用户的。
在你成功通过服务控制台得到这三个变量之后就可以把这三个变量回添到代码中的第3-5行然后去掉“#”开头的注释,这样就可以通过代码完成用户签名和验证功能。
你在掌握它们之后,也可以使用其他的公有云完成类似的用户签名和验证功能。
完成以上三个步骤之后我们需要把指定路径的图片上传到百度云通过第12行的get_file_content函数把图片的路径和名称作为参数传入这个函数之后再交给client.basicGeneral(image)函数处理这样就能够完成图片的上传功能了图片的识别和返回结果都会由AipOcr包自动处理以后放入result变量中。
总结来说,我在代码中实现了单个图片的在线识别文字功能,当你掌握了对单个图片实现文字识别的方法之后, 你就可以通过我们在第二讲学过的的for循环功能遍历多个文件夹里的多个图片你可以实现批量图片的识别功能了。
## 怎么进行离线文字识别?
我们再来看另一种文字识别形式——离线文字识别。被识别的图片同样是example.png这张发票的扫描件代码如下
```
import pytesseract
from PIL import Image
# 打开图片
image = Image.open('example.png')
# 转为灰度图片
imgry = image.convert('L')
# 二值化采用阈值分割算法threshold为分割点,根据图片质量调节
threshold = 150
table = []
for j in range(256):
if j &lt; threshold:
table.append(0)
else:
table.append(1)
temp = imgry.point(table, '1')
# OCR识别lang指定中文,--psm 6 表示按行识别,有助于提升识别准确率
text = pytesseract.image_to_string(temp, lang=&quot;chi_sim+eng&quot;, config='--psm 6')
# 打印识别后的文本
print(text)
```
在进行离线识别的时候有一个现成的文字识别库那就是pytesseract库这个库实现了对图片中的文字识别功能。使用pytesseract库可以自动实现文字的切分和识别功能识别效果如下
<img src="https://static001.geekbang.org/resource/image/55/ac/5586e245b35a18dded24877ac34881ac.png" alt="">
通过识别结果你可以发现英文和数字被准确识别出来的正确率是比较高的达到90%以上。如果是纯英文的图片识别之后的文字是可以直接使用的。但是在对中文、字母、数字混合的图片识别结果中中文会出现较多的错误正确率不足50%,这种情况下就需要人工去对每个字进行一遍纠正。
不过,如果我们想让初次识别的正确率就很高,那能不能实现呢?其实是可以的。不过在这个过程中我们需要花大量的时间把自己工作场景中经常出现的字手工进行纠错,形成新的识别模型。实现起来还是有一定难度的,为了让你更好地评估是不是值得自己优化离线识别软件的识别正确率,我先来带你看一下原理。
对图像进行文字识别的一般过程为5个步骤<br>
第一步,图像输入;<br>
第二步,前期处理,比如二值化,图像降噪,倾斜纠正;<br>
第三步,文字检测,比如版面分析,字符分割;<br>
第四步,文本识别,比如字符识别,后期矫正;<br>
第五步,也就是最后一步,输出文本。
在这5个步骤中影响识别准确率最大的就是文本识别部分这部分会受到对文字的标注和算法模型的大小这两个关键因素的影响。既然我们搞清楚了图像识别的步骤和原理那能不能在保留本地识别的优势前提下通过在线识别产品来优化离线识别产品的正确率呢我们来具体分析一下。
首先,我们可以在文字标注方面优化识别正确率。
像身份证识别、票据识别、聊天截图等大部分文字识别场景,这些待识别图片采用了印刷体,每个字的间隔都是固定的,所以我们要想提升准确率,通常的做法是对每个字都进行人工纠错。
当Tesseract再识别到同一个字的时候就会按照我们纠正过的来识别那么逐个汉字纠正是离线识别方式提升识别正确率的必要方式所以已经有高手为你写好了汉字纠正的工具了它就是 jTessBoxEditorFX软件。
具体来说,这个软件可以把图片中的每个字切割开,并提示哪个方框对应哪个识别的文字,更正为正确的文字之后,就可以把更正的结果进行保存。这个保存好的结果就被称作新训练的模型。
<img src="https://static001.geekbang.org/resource/image/a9/b6/a98b20d0789d4db3195488c768b0e7b6.png" alt="">
所以整个过程你可以理解成三个步骤:<br>
第一步,人工观察;<br>
第二步,对比原始图像;<br>
第三步,把错误的文字手工纠正为正确的汉字。
比如截图中的“某”字,多次识别错误,当我们人工把这些文字改为正确的“某”字之后,就可以生成一个训练文件,再把训练文件和文字识别的算法合并成新的识别模型。等下次再识别发票的时候,就可以采用新的模型进行识别。所以再次出现“某”字,就可以正确识别了。
这种文字标注对于单一场景非常有效,比如名片、火车票、飞机票、发票、车牌的识别场景,经过手工标注,不断增加样本数量之后,能够让正确率呈对数级别增加。
接下来我们再看一下在算法的模型上,能不能通过在线识别软件来优化离线识别软件。
在线识别软件的模型很大通常是上百GB的容量离线识别软件的模型往往只有几十MB它们之间存在着巨大的差异模型越大样本数量就越多类似离线识别软件手工标注的模型一样巨大的模型让在线识别软件的准确率在第一次识别就会非常高。
如果把在线模型直接放到你的笔记本当中,因为模型庞大,本地计算能力不足,识别的时间甚至超过在线识别的时间,那本地识别就不具有实时性的优势了。所以直接使用在线识别软件的模型方式不适合离线识别软件。
总的来说,由于手工标注和模型大小有限两个因素的影响,离线识别软件的应用场景大部分在企业内部票据识别上面。虽然离线识别中文的效果比较差,但是如果场景单一,在线识别的实时性达不到你的要求,那还是值得投入一部分时间人工标注汉字的。
识别出文字以后,我们就要提取文字内容并保存了。
## 识别成功后的文字处理工作
通过刚才案例中的识别结果我们知道识别结果准确率很高像单张图片已经可以把识别的结果直接保存使用了。不过一旦需要识别的图片比较多手工操作起来就非常繁琐了因为识别过程是使用Python编写的程序后续可以继续利用Python对识别结果进行处理和保存。比如说在线识别的结果
<img src="https://static001.geekbang.org/resource/image/24/fa/2400754a55374cba0cd2df2979a9f6fa.png" alt="">
为了能够提取文本内容,去掉记录分段信息的"word"和 “{ }”,需要对这段文字再加工处理。
如果你仔细观察的话就会发现这段文字是有一定规律的它的写法就是Python的基本数据类型“字典”字典类型会强调一对或多对数据之间的映射关系。为了将识别的文字结果进行保存我们还会使用另一个基本数据类型“列表”一般用来表示多段文字的并列关系为了提取文本内容我接下来把字典转换成列表就可以实现内容的提取了。
从字典到列表转换的功能怎么实现呢?首先来看一下两种数据类型的定义。
- 字典由花扩号中的KEY和VALUE两部分组成的它的格式是{"words":"1234567" } 。
- 列表由方括号的VALUE组成可以存放一个也可以存放多个VALUE格式是["1234567",] 或["123", "abc"]。
观察这两个数据类型不难发现把字典的VALUE存放到列表里就能实现内容的提取了如果字典里有多个值可以使用for循环进行遍历然后批量提取。对于识别的结果我们可以采用一段代码进行从字典到列表的转换。代码如下
```
info = []
for i in result['words_result']:
info.append(i['words'])
print(info)
```
经过数据类型的转换以后,我们可以得到处理好的文字内容:
<img src="https://static001.geekbang.org/resource/image/48/a5/48882e7ac50861fe7b1eca9bff872ba5.png" alt="">
最后我们把处理好的结果保存至文件就行了。因为类型转换和保存我们都使用Python完成所以我可以把这部分功能和文字识别功能放在同一个脚本中避免手工操作。
不过我还要提醒你一下,在实现用户认证、图片上传、文字识别、数据清洗、文件保存的过程中,我们要尽可能实现流程的全自动化,避免中间步骤手工介入,这样可以更高效地提升工作效率。
## 小结
这节课我给你提供了两种文字识别的方式在线识别和离线识别。不管哪一种识别方式它们都需要经过图片输入、图片预处理、文字检测、文字识别和输出这样5个部分来实现完整的文字的识别功能。
但因为实现方式不同两种方式各有优缺点。在线方式准确率更高、离线方式保密性更强。你需要根据自己的工作场景来选择不同的识别方式。不过不管哪种识别最后我都建议你用Python对识别后的结果实现数据清洗和保存工作毕竟减少手工操作才是实现办公效率提升最有效方式。
现在你可以开始动手将自己电脑里存储多年的图片,自动转成文字了。
## 思考题
在讲离线文字识别的时候,我只讲了单张图片转换文字的操作方法。如果是多张图片呢?该怎么实现呢?我建议你使用[第二节课](https://time.geekbang.org/column/article/341220)学过的for循环来进行操作。
欢迎把你对今天这节课的思考和想法分享在留言区,我们一起交流讨论。也欢迎你点击“请朋友读”,我们一起提高工作效率。