├── README.md ├── Task01-文件处理与邮件自动化 ├── Task01 文件处理与邮件自动化.ipynb ├── Task01 文件自动化与邮件处理.md └── png │ ├── 1.png │ ├── 2.png │ ├── 3.png │ ├── 4.png │ ├── 5.png │ ├── 6.png │ ├── 7.png │ ├── 8.png │ ├── 9.png │ └── os.png ├── Task02-Python与Excel ├── OpenPyXL_test │ ├── creat_sheet_test.xlsx │ ├── new_test.xlsx │ ├── test.xlsx │ ├── 业务经理信息表_pd.xlsx │ ├── 业务经理信息表_xl.xlsx │ ├── 业务联系表.xlsx │ ├── 业务联系表_pd.xlsx │ ├── 客户信息表_pd.xlsx │ ├── 客户信息表_xl.xlsx │ ├── 用户行为偏好.xlsx │ └── 用户行为偏好_1.xlsx ├── Python_Excel_OpenPyXL.ipynb ├── Python_Excel_OpenPyXL.md ├── Python_Excel_OpenPyXL.pdf ├── Python_Excel_XLWings.ipynb ├── Python_Excel_XLWings.md ├── Python_Excel_XLWings.pdf ├── XLWings_test │ └── xlwings_wb.xlsx └── imgs │ ├── 2.3.png │ ├── Python_Excel_XLWings │ ├── code-result.png │ ├── xlwings-border.png │ ├── xlwings-charts.png │ ├── xlwings-format.png │ ├── xlwings-line.png │ ├── xlwings-local.png │ ├── xlwings-matplotlib.png │ ├── xlwings-practice1.png │ ├── xlwings-practice2.png │ ├── xlwings-principle.png │ └── xlwings-write.png │ ├── logo.png │ ├── output_30_0.png │ └── output_36_0.png ├── Task03-Python与Word和PDF ├── excel到word.xlsx ├── python与pdf.ipynb ├── python与pdf.md ├── python与word.ipynb ├── python与word.md ├── watermark.pdf └── 易方达中小盘混合型证券投资基金2020年中期报告.pdf ├── Task04-简单的python爬虫 ├── 爬虫参考结果 │ ├── datawhale.png │ ├── wuhan_ziru.csv │ └── 鲁迅文章.txt ├── 简单的python爬虫.ipynb └── 简单的python爬虫.md ├── Task05-Python操作钉钉自动化 ├── Python操作钉钉自动化.md └── asset │ ├── 01.png │ ├── 02.png │ ├── 03.png │ ├── 04.png │ ├── 05.png │ ├── 06.png │ ├── 07.png │ ├── 08.png │ ├── 09.png │ └── 10.png ├── Task06-其它推荐软件和网页 ├── asset │ ├── 01.png │ ├── 02.png │ ├── 03.png │ ├── 04.png │ ├── 05.png │ ├── 06.png │ ├── Desktopcal.png │ ├── clippingmagic.png │ └── geek.png └── 其它优秀的小工具.md └── 自动化办公.pptx /README.md: -------------------------------------------------------------------------------- 1 | # office-automation 2 | 3 | 🎉本课程已上线至智海(国家级的AI科教平台):https://aiplusx.momodel.cn/classroom/class/664bf764599277c8d81d326d 4 | 5 | 课程基本信息 6 | 7 | - 学习周期:14天,每天平均花费时间1小时-3小时不等,根据个人学习接受能力强弱有所浮动。 8 | - 学习形式:理论学习 + 练习 9 | - 人群定位:有Python语言编程基础,对自动化办公有需求的学员。 10 | - 先修内容:Python编程语言 11 | - 相关课程:数据采集 12 | - 测试课程:Datawhale组队学习-办公自动化 13 | 14 | ### 课程大纲 15 | 16 | **Task01 文件处理与邮件自动化** 17 | - 文件路径识别、处理、文件夹的操作理论学习 18 | - 文件自动化处理实践 19 | - 邮件自动发送理论学习 20 | 21 | **Task02 Python与excel** 22 | - Excel读取与写入 23 | - Excel样式调整 24 | - 综合练习 25 | 26 | **Task03 Python与word和PDF** 27 | - python与word相关的理论知识学习 28 | - python与PDF相关的理论知识学习 29 | 30 | **Task04 简单的Python爬虫** 31 | - requests库的理论与实践 32 | - HTML页面解析与提取方法 33 | - 自如公寓数据抓取 34 | - 36kr信息抓取与邮件发送 35 | 36 | **Task05 Python操作钉钉自动化** 37 | - Python操作钉钉的相关知识学习 38 | 39 | **Task06 其它推荐软件和网页** 40 | - 一些好用的小工具和网页 41 | 42 | ### 致谢 43 | 44 | 感谢以下成员对项目推进作出的贡献 45 | 46 | 47 | 48 | 49 | 54 | 59 | 64 | 69 | 74 | 75 | 76 |
50 | pic
51 | 牧小熊 52 |

Task04&Task06

53 |
55 | pic
56 | 老表 57 |

Task02

58 |
60 | pic
61 | 小一 62 |

Task05&Task03

63 |
65 | pic
66 | 赵信达 67 |

Task01

68 |
70 | pic
71 | 于鸿飞 72 |

Task03

73 |
77 | 78 | 79 | 关于Datawhale: Datawhale是一个专注于数据科学与AI领域的开源组织,汇集了众多领域院校和知名企业的优秀学习者,聚合了一群有开源精神和探索精神的团队成员。Datawhale 以“for the learner,和学习者一起成长”为愿景,鼓励真实地展现自我、开放包容、互信互助、敢于试错和勇于担当。同时 Datawhale 用开源的理念去探索开源内容、开源学习和开源方案,赋能人才培养,助力人才成长,建立起人与人,人与知识,人与企业和人与未来的联结。 80 | 81 | ![logo.png](https://camo.githubusercontent.com/8578ee173c78b587d5058439bbd0b98fa39c173def229a8c3d957e62aac0b649/68747470733a2f2f696d672d626c6f672e6373646e696d672e636e2f323032303039313330313032323639382e706e67237069635f63656e746572) 82 | -------------------------------------------------------------------------------- /Task01-文件处理与邮件自动化/Task01 文件自动化与邮件处理.md: -------------------------------------------------------------------------------- 1 | --- 2 | typora-copy-images-to: png 3 | --- 4 | 5 | # Task 01 文件自动化处理 6 | 7 | - [Task 01 文件自动化处理](#task-01-文件自动化处理) 8 | - [1.1文件处理](#11文件处理) 9 | - [1.1.1 文件与文件路径](#111--文件与文件路径) 10 | - [1.1.2 当前工作目录](#112--当前工作目录) 11 | - [1.1.3 路径操作](#113--路径操作) 12 | - [1.1.3.1 绝对路径和相对路径](#1131-绝对路径和相对路径) 13 | - [1.1.3.2 路径操作](#1132-路径操作) 14 | - [1.1.3.3 路径有效性检查](#1133-路径有效性检查) 15 | - [1.1.4 文件及文件夹操作](#114--文件及文件夹操作) 16 | - [1.1.4.1 用os.makedirs()创建新文件夹](#1141-用osmakedirs创建新文件夹) 17 | - [1.1.4.2 查看文件大小和文件夹内容](#1142-查看文件大小和文件夹内容) 18 | - [1.1.5 文件读写过程](#115-文件读写过程) 19 | - [1.1.5.1 用open()函数打开文件](#1151-用open函数打开文件) 20 | - [1.1.5.2 读取文件内容](#1152-读取文件内容) 21 | - [1.1.5.3 写入文件](#1153-写入文件) 22 | - [1.1.5.4 保存变量](#1154-保存变量) 23 | - [1.1.6 练习](#116-练习) 24 | - [1.1.7 组织文件](#117--组织文件) 25 | - [1.1.1.7.1 shutil模块](#11171-shutil模块) 26 | - [1.1.1.7.2 复制文件和文件夹](#11172-复制文件和文件夹) 27 | - [1.1.7.3 文件和文件夹的移动与改名](#1173-文件和文件夹的移动与改名) 28 | - [1.1.7.4 永久删除文件和文件夹](#1174-永久删除文件和文件夹) 29 | - [1.1.7.5 用send2trash模块安全地删除](#1175-用send2trash模块安全地删除) 30 | - [1.1.8 遍历目录树](#118-遍历目录树) 31 | - [1.1.9 用zipfile模块压缩文件](#119-用zipfile模块压缩文件) 32 | - [1.1.9.1 创建和添加到zip文件](#1191-创建和添加到zip文件) 33 | - [1.1.9.2 读取zip文件](#1192-读取zip文件) 34 | - [1.1.9.3 从zip文件中解压缩](#1193-从zip文件中解压缩) 35 | - [1.1.10 练习](#1110-练习) 36 | - [1.2 自动发送电子邮件](#12-自动发送电子邮件) 37 | 38 | 39 | 40 | **Task01 文件自动化处理&邮件批量处理 (3天)** 41 | 42 | - 文件路径识别、处理、文件夹的操作理论学习 43 | - 文件自动化处理实践 44 | - 邮件自动发送理论学习,使用python发送邮件附带excel附件 45 | 46 | 我们知道,程序运行时,可以用变量来保存运算结果,但如果希望程序运行关闭后,依然可以查看运行后的结果,就需要将数据保存到文件中。简单点,你可以将文件内容理解为一个字符串值,大小可能有几个GB。本节将学习,如何使用python在硬盘上创建、读取和保存文件。 47 | ## 1.1文件处理 48 | ### 1.1.1 文件与文件路径 49 | 50 | 文件的两个属性:“路径”和“文件名”,路径指明文件在计算机上的位置,文件名是指该位置的文件的名称。比如,我的电脑上,有个名字为Datawhale - 开源发展理论研究.pdf的文件,它的路径在D:\Datawhale。在windows中,路径中的D:\部分是“根文件夹”,Datawhale是文件夹名。注:Windows中文件夹名和文件名不区分大小写的。 51 | 52 | 在windows上,路径书写是使用倒斜杠'\'作为文件夹之间的分隔符,而在OS X和Linux上,是使用正斜杠'/'作为它们的路径分隔符。通常我们用`os.path.join()`函数来创建文件名称字符串。 53 | 54 | os常用的操作函数如下图 55 | 56 | ![os](.\png\os.png) 57 | 58 | ```python 59 | import os 60 | os.path.join('Datawhale','docu') 61 | ``` 62 | 63 | 我们可以看到返回的是('Datawhale\\\docu'),有两个斜杠,这是因为有一个斜杠是用来转义的,在OS X或Linux上调用这个函数,这个字符串就会是'Datawhale/docu'。 64 | 65 | ### 1.1.2 当前工作目录 66 | 67 | 每个运行在计算机上的程序,都有一个“当前工作目录”。利用`os.getcwd()`函数,可以取得当前工作路径的 68 | 字符串,并可以利用`os.chdir()`改变它。 69 | 70 | ![3](.\png\3.png) 71 | 72 | ### 1.1.3 路径操作 73 | 74 | #### 1.1.3.1 绝对路径和相对路径 75 | 76 | “绝对路径”,总是从根文件夹开始。 77 | “相对路径”,相对于程序的当前工作目录。 78 | 相对路径中,单个句点“.”表示当前目录的缩写,两个句点“..”表示父文件夹。 79 | ![1](.\png\1.png) 80 | 81 | “绝对路径”,总是从根文件夹开始。 82 | 83 | “相对路径”,相对于程序的当前工作目录。 84 | 85 | 相对路径中,单个句点“.”表示当前目录的缩写,两个句点“..”表示父文件夹。 86 | 87 | 几个常用的绝对路径和相对路径处理函数: 88 | 89 | - os.path.abspath(path):将相对路径转换为绝对路径,将返回参数的绝对路径的字符串。 90 | - os.path.isabs(path):判断是否是绝对路径,是返回True,不是则返回False 91 | 92 | ![4](.\png\4.png) 93 | 94 | 95 | 96 | #### 1.1.3.2 路径操作 97 | 98 | `os.path.relpath(path,start)`:返回从start路径到path的相对路径的字符串。如果没提供start,就使用当前工作目录作为开始路径。 99 | `os.path.dirname(path)`: 返回当前路径的目录名称。 100 | `os.path.basename(path)`:返回当前路径的文件名称。 101 | 102 | ![5](.\png\5.png) 103 | 104 | 如果同时需要一个路径的目录名称和基本名称,可以调用`os.path.split()`,获得者两个字符串的元组。 105 | 106 | ```python 107 | caFilePath = 'D:\\Datawhale\\python办公自动化\\python课程画图.pptx' 108 | os.path.split(caFilePath) #('D:\\Datawhale\\python办公自动化', 'python课程画图.pptx') 109 | ``` 110 | 111 | 我们也可以调用os.path.dirname()和os.path.basename(),将它们的返回值放在一个元组中,从而得到同样的元组。 112 | 113 | ```python 114 | (os.path.dirname(caFilePath),os.path.basename(caFilePath)) #('D:\\Datawhale\\python办公自动化', 'python课程画图.pptx') 115 | ``` 116 | 117 | 如果我们想返回每个文件夹的字符串的列表。用`os.path.split()`无法得到,我们可以用`split()`字符串方法,并根据`os.path.sep` 中的字符串进行分割。`os.path.sep` 变量设置为正确的文件夹分割斜杠。 118 | 119 | ```python 120 | caFilePath.split(os.path.sep) #['D:', 'Datawhale', 'python办公自动化', 'python课程画图.pptx'] 121 | ``` 122 | 123 | #### 1.1.3.3 路径有效性检查 124 | 125 | 如果提供的路径不存在,很多Python函数就会崩溃并报错。`os.path`模块提供了一些函数,用于检测给定的路径是否存在,以及判定是文件还是文件夹。 126 | 127 | `os.path.exists(path)`:如果path参数所指的文件或文件夹存在,则返回True,否则返回False。 128 | 129 | `os.path.isfile(path)`:如果path参数存在,并且是一个文件,则返回True,否则返回False。 130 | 131 | `os.path.isdir(path)`:如果path参数存在,并且是一个文件夹,则返回True,否则返回False。 132 | 133 | ```python 134 | os.path.exists('C:\\Windows') 135 | ``` 136 | 137 | ```python 138 | os.path.exists('C:\\else') 139 | ``` 140 | 141 | ```python 142 | os.path.isfile('D:\\Datawhale\\python办公自动化\\python课程画图.pptx') 143 | ``` 144 | 145 | ```python 146 | os.path.isdir('D:\\Datawhale\\python办公自动化\\python课程画图.pptx') 147 | ``` 148 | 149 | ### 1.1.4 文件及文件夹操作 150 | 151 | #### 1.1.4.1 用os.makedirs()创建新文件夹 152 | 153 | 注:`os.makedirs()`可以创建所有必要的中间文件夹。 154 | 155 | ```python 156 | import os 157 | os.makedirs('D:\\Datawhale\\practice') #查看目录,已创建,若文件夹已存在,不会覆盖,会报错 158 | ``` 159 | 160 | #### 1.1.4.2 查看文件大小和文件夹内容 161 | 162 | 我们已经可以处理文件路径,这是操作文件及文件夹的基础。接下来,我们可以搜集特定文件和文件夹的信息。`os.path`模块提供了一些函数,用于查看文件的字节数以及给定文件夹中的文件和子文件夹。 163 | `os.path.getsize(path)`:返回path参数中文件的字节数。 164 | `os.listdir(path)`:返回文件名字符串的列表,包含path参数中的每个文件。 165 | 166 | ```python 167 | """ 168 | 注意这里你可以自己按照这个路径新建文件夹,并任意放入一个pptx文件, 169 | 并重命名为python课程画图.pptx。否则若不存在该文件将会报错,而非0字节 170 | """ 171 | os.path.getsize('D:\\Datawhale\\python办公自动化\\python课程画图.pptx') 172 | ``` 173 | 174 | ```python 175 | os.listdir('D:\\Datawhale\\python办公自动化') 176 | ``` 177 | 178 | 如果想知道目录下所有文件的总字节数,可以同时使用`os.path.getsize()`和`os.listdir()` 179 | 180 | ```python 181 | totalSize = 0 182 | for filename in os.listdir('D:\\Datawhale\\python办公自动化'): 183 | totalSize = totalSize + os.path.getsize(os.path.join('D:\\Datawhale\\python办公自动化',filename)) 184 | print(totalSize) 185 | ``` 186 | 187 | ### 1.1.5 文件读写过程 188 | 189 | 读写文件3个步骤: 190 | 191 | 1.调用`open()`函数,返回一个File对象。 192 | 193 | 2.调用File对象的`read()`或`write()`方法。 194 | 195 | 3.调用File对象的`close()`方法,关闭该文件。 196 | 197 | open函数中 常见的对象方法及其作用说明: 198 | 199 | ![6](.\png\6.png) 200 | 201 | #### 1.1.5.1 用open()函数打开文件 202 | 203 | 要用`open()`函数打开一个文件,就要向它传递一个字符串路径,表明希望打开的文件。这既可以是绝对路径,也可以是相对路径。`open()`函数返回一个File对象。 204 | 先用TextEdit创建一个文本文件,名为hello.txt。输入Hello World!作为该文本文件的内容,将它保存在你的用户文件夹中。 205 | 206 | ![7](.\png\7.png) 207 | 208 | 文件对象可以通过Python内置的open函数得到,完整的语法如下。 209 | 210 | open(file,mode=r',buffering=-1,encoding=None,errors=None,newline=None,closefd=True,opener=None) 211 | 212 | open函数有8个参数,常用前4个,除了file参数外,其他参数都有默认值。file指定了要打开的文件名称,应包含文件路径,不写路径则表示文件和当前py脚本在同一个文件夹。buffering用于指定打开文件所用的缓冲方式,默认值-1表示使用系统默认的缓冲机制。文件读写要与硬盘交互,设置缓冲区的目的是减少CPU操作磁盘的次数,延长硬盘使用寿命。encoding用于指定文件的编码方式,如GBK、UTF-8等,默认采用UTF-8,有时候打开一个文件全是乱码,这是因为编码参数和创建文件时采用的编码方式不一样。 213 | 214 | mode指定了文件的打开模式。打开文件的基本模式包括r、w、a,对应读、写、追加写入。附加模式包括b、t、+,表示二进制模式、文本模式、读写模式,附加模式需要和基本模式组合才能使用,如“rb”表示以二进制只读模式打开文件,“rb+”表示以二进制读写模式打开文件。 215 | 216 | 要注意的是,凡是带w的模式,操作时都要非常谨慎,它首先会清空原文件,但不会有提示。凡是带r的文件必须先存在,否则会因找不到文件而报错。 217 | 218 | 219 | 220 | ```python 221 | helloFile = open('D:\\Datawhale\\python办公自动化\\hello.txt') 222 | print(helloFile) 223 | ``` 224 | 225 | 可以看到,调用`open()`函数将会返回一个File对象。当你需要读取或写入该文件,就可以调用helloFile变量中的File对象的方法。 226 | 227 | #### 1.1.5.2 读取文件内容 228 | 229 | 有了File对象,我们就可以开始从它读取内容。 230 | 231 | `read()`:读取文件内容。 232 | 233 | `readlines()`:按行读取文件中的内容,取得一个字符串列表,列表中每个字符串是文本中的一行且以\n结束。 234 | 235 | ```python 236 | helloContent = helloFile.read() 237 | helloContent 238 | ``` 239 | 240 | ```python 241 | sonnetFile = open('D:\\Datawhale\\python办公自动化\\hello.txt') 242 | sonnetFile.readlines() 243 | ``` 244 | 245 | #### 1.1.5.3 写入文件 246 | 247 | 需要用“写模式”‘w’和“添加模式”'a'打开一个文件,而不能用读模式打开文件。 248 | “写模式”将覆写原有的文件,从头开始。“添加模式”将在已有文件的末尾添加文本。 249 | 250 | ```python 251 | baconFile = open('bacon.txt','w') 252 | baconFile.write('Hello world!\n') 253 | ``` 254 | 255 | ```python 256 | baconFile.close() #注意,关闭后,才能完成写入,从txt文件中看到写入的内容。 257 | ``` 258 | 259 | ```python 260 | baconFile = open('bacon.txt','a') 261 | baconFile.write('Bacon is not a vegetable.') 262 | ``` 263 | 264 | ```python 265 | baconFile.close() 266 | ``` 267 | 268 | ```python 269 | baconFile = open('bacon.txt') 270 | content = baconFile.read() 271 | baconFile.close() 272 | print(content) 273 | ``` 274 | 275 | 注意,`write()`方法不会像print()函数那样,在字符串的末尾自动添加换行字符。必须自己添加该字符。 276 | 277 | - 案例:统计字母出现的频率 278 | 279 | 文件对象有iter、next方法,所以它是一个可迭代对象,可以用for循环遍历。我们可以遍历文件获得每一行字符,再遍历每一行,获得每个字符,将字符放入列表,然后统计每个字符出现的频率。 280 | 281 | ```python 282 | from collections import Counter 283 | my_list = [] 284 | punctuation=',.!?\,。!?、()【】<>《》=:+-*“”...\n' 285 | with open('bacon.txt','r') as f: 286 | for line in f: 287 | for word in line: 288 | if word not in punctuation: 289 | my_list.append(word) 290 | 291 | counter = Counter(my_list) 292 | counter 293 | ``` 294 | 295 | 296 | 297 | #### 1.1.5.4 保存变量 298 | 299 | 1)、shelve模块 300 | 301 | 用`shelve`模块,可以将Python中的变量保存到二进制的`shelf`文件中。这样,程序就可以从硬盘中恢复变量的数据。 302 | 303 | ```python 304 | import shelve 305 | shelfFile = shelve.open('mydata') 306 | cats = ['Zonphie','Pooka','Simon'] 307 | shelfFile['cats'] = cats 308 | shelfFile.close() 309 | ``` 310 | 311 | 在Windows上运行前面的代码,我们会看到当前工作目录下有3个新文件:mydata.bak、mydata.dat和mydata.dir。在OS X上,只会创建一个mydata.db文件。 312 | 313 | 重新打开这些文件,取出数据。注意:`shelf`值不必用读模式或写模式打开,因为打开后,既能读又能写。 314 | 315 | ![8](.\png\8.png) 316 | 317 | ```python 318 | shelfFile = shelve.open('mydata') 319 | type(shelfFile) 320 | ``` 321 | 322 | ```python 323 | shelve.DbfilenameShelf 324 | ``` 325 | 326 | ```python 327 | shelfFile['cats'] 328 | ``` 329 | 330 | ```python 331 | shelfFile.close() 332 | ``` 333 | 334 | 就像字典一样,`shelf`值有`keys()`和`values()`方法,返回shelf中键和值的类似列表的值。但是这些方法返回类似列表的值,却不是真正的列表,所以应该将它们传递给`list()`函数,取得列表的形式。 335 | 336 | ```python 337 | shelfFile = shelve.open('mydata') 338 | list(shelfFile.keys()) 339 | ``` 340 | 341 | ```python 342 | list(shelfFile.values()) 343 | ``` 344 | 345 | ```python 346 | shelfFile.close() 347 | ``` 348 | 349 | 2)、用`pprint.pformat()`函数保存变量 350 | 351 | `pprint.pformat()`函数返回要打印的内容的文本字符串,这个字符串既易于阅读,也是语法上正确的Python代码。 352 | 353 | 假如,有一个字典,保存在一个变量中,希望保存这个变量和它的内容,以便将来使用。`pprint.pformat()`函数将提供一个字符串,我们可以将它写入.py文件。这个文件可以成为我们自己的模块,如果需要使用存储其中的变量,就可以导入它。 354 | 355 | ```python 356 | import pprint 357 | cats = [{'name':'Zophie','desc':'chubby'},{'name':'Pooka','desc':'fluffy'}] 358 | pprint.pformat(cats) 359 | ``` 360 | 361 | ```python 362 | fileObj = open('myCats.py','w') 363 | fileObj.write('cats = '+pprint.pformat(cats)+'\n') 364 | ``` 365 | 366 | ```python 367 | fileObj.close() 368 | ``` 369 | 370 | import语句导入的模块本身就是Python脚本。如果来自pprint.pformat()的字符串保存为一个.py文件,该文件就是一个可以导入的模块。 371 | 372 | ```python 373 | import myCats 374 | myCats.cats 375 | ``` 376 | 377 | ```python 378 | myCats.cats[0] 379 | ``` 380 | 381 | ```python 382 | myCats.cats[0]['name'] 383 | ``` 384 | 385 | ### 1.1.6 练习 386 | 387 | 1、如果已有的文件以写模式打开,会发生什么? 388 | 389 | 提示: 390 | 391 | ``` 392 | 以写模式打开 393 | 394 | r : 只读模式,文件不存在泽报错,默认模式(文件指针位于文件末尾) 395 | 396 | w : 写入模式,文件不存在则自动报错,每次打开会覆盖原文件内容,文件不关闭则可以进行多次写入(只会在打开文件时清空文件内容) 397 | ``` 398 | 399 | 2、`read()`和`readlines()`方法之间的区别是什么? 400 | 401 | 提示: 402 | 403 | read():以原格式返回全部文本 404 | 405 | readline(): 只返回第一行文本 406 | 407 | readlines(): 以列表的格式返回全部文本,文本的第几行对应列表的第几个元素 408 | 综合练习: 409 | 一、生成随机的测验试卷文件 410 | 假如你是一位地理老师, 班上有 35 名学生, 你希望进行美国各州首府的一个 411 | 小测验。不妙的是,班里有几个坏蛋, 你无法确信学生不会作弊。你希望随机调整 412 | 问题的次序, 这样每份试卷都是独一无二的, 这让任何人都不能从其他人那里抄袭答案。当然,手工完成这件事又费时又无聊。 好在, 你懂一些 Python。 413 | 414 | 下面是程序所做的事: 415 | 416 | • 创建 35 份不同的测验试卷。 417 | 418 | • 为每份试卷创建 50 个多重选择题,次序随机。 419 | 420 | • 为每个问题提供一个正确答案和 3 个随机的错误答案,次序随机。 421 | 422 | • 将测验试卷写到 35 个文本文件中。 423 | 424 | • 将答案写到 35 个文本文件中。 425 | 426 | 这意味着代码需要做下面的事: 427 | 428 | • 将州和它们的首府保存在一个字典中。 429 | 430 | • 针对测验文本文件和答案文本文件,调用 open()、 write()和 close()。 431 | 432 | • 利用 random.shuffle()随机调整问题和多重选项的次序。 433 | 434 | 435 | 436 | 提示: 437 | https://blog.csdn.net/liying_tt/article/details/117968373 438 | 439 | ### 1.1.7 组织文件 440 | 441 | 在上一节中,已经学习了如何使用Python创建并写入新文件。本节将介绍如何用程序组织硬盘上已经存在的文件。不知你是否经历过查找一个文件夹,里面有几十个、几百个、甚至上千个文件,需要手工进行复制、改名、移动或压缩。比如下列这样的任务: 442 | 443 | • 在一个文件夹及其所有子文件夹中,复制所有的 pdf 文件(且只复制 pdf 文件) 444 | 445 | • 针对一个文件夹中的所有文件,删除文件名中前导的零,该文件夹中有数百个文件,名为 spam001.txt、 spam002.txt、 spam003.txt 等。 446 | 447 | • 将几个文件夹的内容压缩到一个 ZIP 文件中(这可能是一个简单的备份系统) 448 | 449 | 所有这种无聊的任务,正是在请求用 Python 实现自动化。通过对电脑编程来完成这些任务,你就把它变成了一个快速工作的文件职员,而且从不犯错。 450 | 451 | #### 1.1.1.7.1 shutil模块 452 | 453 | `shutil`(或称为shell工具)模块中包含一些函数,可以在Python程序中复制、移动、改名和删除文件。要使用`shutil`的函数,首先需要`import shutil` 454 | 455 | ![9](.\png\9.png) 456 | 457 | #### 1.1.1.7.2 复制文件和文件夹 458 | 459 | `shutil.copy(source, destination)`:将路径source处的文件复制到路径 destination处的文件夹(source 和 destination 都是字符串),并返回新复制文件绝对路径字符串。 460 | 461 | 其中destination可以是: 462 | 463 | 1)、一个文件的名称,则将source文件复制为新名称的destination 464 | 465 | 2)、一个文件夹,则将source文件复制到destination中 466 | 467 | 3)、若这个文件夹不存在,则将source目标文件内的内容复制到destination中,若destination文件夹不存在,则自动生成该文件。(慎用,因为会将source文件复制为一个没有扩展名的名字为destination的文件,这往往不是我们希望的) 468 | 469 | ```python 470 | """ 471 | 这里如果路径下没有bacon.txt,可以从当前代码文件路径下找到bacon.txt, 472 | 将其移至指定路径学习使用 473 | """ 474 | 475 | import shutil 476 | import os 477 | shutil.copy('D:\\Datawhale\\python办公自动化\\bacon.txt', 'D:\\Datawhale\\practice') 478 | ``` 479 | 480 | - shutil.copytree(source, destination):将路径source处的文件夹,包括其包含的文件夹和文件,复制到路径destination处的文件夹,并返回新复制文件夹绝对路径字符串。 481 | 482 | 注:destination处的文件夹为新创建的文件夹,如已存在,则会报错 483 | 484 | ```python 485 | import shutil 486 | shutil.copytree('D:\\Datawhale\\python办公自动化','D:\\Datawhale\\practice') 487 | ``` 488 | 489 | ```python 490 | import shutil 491 | shutil.copytree('D:\\Datawhale\\python办公自动化','D:\\Datawhale\\practice_unexist') 492 | ``` 493 | 494 | 495 | 496 | #### 1.1.7.3 文件和文件夹的移动与改名 497 | 498 | `shutil.move(source, destination)`:将路径 source 处的文件/文件夹移动到路径destination,并返回新位置的绝对路径的字符串。 499 | 500 | 1)、如果source和destination是文件夹,且destination已存在,则会将source文件夹下所有内容复制到destination文件夹中。移动。 501 | 502 | 2)、如果source是文件夹,destination不存在,则会将source文件夹下所有内容复制到destination文件夹中,source原文件夹名称将被替换为destination文件夹名。 移动+重命名 503 | 504 | 3)、如果source和destination是文件,source处的文件将被移动到destination处的位置,并以destination处的文件名进行命名,移动+重命名。 505 | 506 | 注意:如果destination中有原来已经存在同名文件,移动后,会被覆写,所以应当特别注意。 507 | 508 | ```python 509 | import shutil 510 | shutil.move('D:\\Datawhale\\practice','D:\\Datawhale\\docu') 511 | ``` 512 | 513 | #### 1.1.7.4 永久删除文件和文件夹 514 | 515 | `os.unlink(path)`: 删除path处的文件。 516 | 517 | `os.rmdir(path)`: 删除path处的文件夹。该文件夹必须为空,其中没有任何文件和文件夹。 518 | 519 | `shutil.rmtree(path)`:删除 path 处的文件夹,它包含的所有文件和文件夹都会被删除。 520 | 521 | 注意:使用时,需要非常小心,避免删错文件,一般在第一次运行时,注释掉这些程序,并加上`print()`函数来帮助查看是否是想要删除的文件。 522 | 523 | ```python 524 | #建议先指定操作的文件夹,并查看 525 | os.chdir('D:\\Datawhale\\docue') 526 | os.getcwd() 527 | ``` 528 | 529 | ```python 530 | import os 531 | for filename in os.listdir(): 532 | print(filename) 533 | os.unlink(filename) 534 | 535 | # 可以看到bacon.txt已经被删除 536 | 537 | for filename in os.listdir(): 538 | print(filename) 539 | ``` 540 | 541 | #### 1.1.7.5 用send2trash模块安全地删除 542 | 543 | `shutil.rmtree(path)`会不可恢复的删除文件和文件夹,用起来会有危险。因此使用第三方的`send2trash`模块,可以将文件或文件夹发送到计算机的垃圾箱或回收站,而不是永久删除。因程序缺陷而用send2trash 删除的某些你不想删除的东西,稍后可以从垃圾箱恢复。 544 | 545 | 注意:使用时,需要非常小心,避免删错文件,一般在第一次运行时,注释掉这些程序,并加上`print()`函数来帮助查看是否是想要删除的文件。 546 | 547 | ```python 548 | !pip install send2trash #安装send2trash模块 549 | ``` 550 | 551 | ```python 552 | import send2trash 553 | send2trash.send2trash('bacon.txt') 554 | ``` 555 | 556 | ### 1.1.8 遍历目录树 557 | 558 | `os.walk(path)`:传入一个文件夹的路径,在for循环语句中使用`os.walk()`函数,遍历目录树,和range()函数遍历一个范围的数字类似。不同的是,`os.walk()`在循环的每次迭代中,返回三个值: 559 | 560 | 1)、当前文件夹称的字符串。 561 | 562 | 2)、当前文件夹中子文件夹的字符串的列表。 563 | 564 | 3)、当前文件夹中文件的字符串的列表。 565 | 566 | 注:当前文件夹,是指for循环当前迭代的文件夹。程序的当前工作目录,不会因为`os.walk()`而改变。 567 | 568 | ![2](.\png\2.png) 569 | 570 | 571 | 572 | 按照下图目录树,创建相应的文件。 573 | 574 | 575 | ```python 576 | import os 577 | for folderName, subFolders,fileNames in os.walk('D:\\animals'): 578 | print('The current folder is ' + folderName) 579 | for subFolder in subFolders: 580 | print('Subfolder of ' + folderName+':'+subFolder) 581 | for filename in fileNames: 582 | print('File Inside ' + folderName+':'+filename) 583 | print('') 584 | ``` 585 | 586 | ### 1.1.9 用zipfile模块压缩文件 587 | 588 | 为方便传输,常常将文件打包成.zip格式文件。利用zipfile模块中的函数,Python程序可以创建和打开(或解压)zip文件。 589 | 590 | #### 1.1.9.1 创建和添加到zip文件 591 | 592 | 将上述章节中animals文件夹进行压缩。创建一个example.zip的zip文件,并向其中添加文件。 593 | 594 | `zipfile.ZipFile('filename.zip', 'w')` :以写模式创建一个压缩文件 595 | 596 | `ZipFile` 对象的 `write('filename','compress_type=zipfile.ZIP_DEFLATED')`方法:如果向`write()`方法中传入一个路径,Python 就会压缩该路径所指的文件, 将它加到 ZIP 文件中。 如果向`write()`方法中传入一个字符串,代表要添加的文件名。第二个参数是“压缩类型”参数,告诉计算机用怎样的算法来压缩文件。可以总是将这个值设置为 `zipfile.ZIP_DEFLATED`(这指定了 deflate 压缩算法,它对各种类型的数据都很有效)。 597 | 598 | 注意:写模式会擦除zip文件中所有原有的内容。如果只希望将文件添加到原有的zip文件中,就要向`zipfile.ZipFile()`传入'a'作为第二个参数,以添加模式打开 ZIP 文件。 599 | 600 | ```python 601 | ## 1 创建一个new.zip压缩文件,并向其中添加文件 602 | import zipfile 603 | newZip = zipfile.ZipFile('new.zip','w') 604 | newZip.write('Miki.txt',compress_type=zipfile.ZIP_DEFLATED) 605 | newZip.close() 606 | ``` 607 | 608 | ```python 609 | newZip = zipfile.ZipFile('new.zip','w') 610 | newZip.write('D:\\animals\\dogs\\Taidi.txt',compress_type=zipfile.ZIP_DEFLATED) 611 | newZip.close() 612 | ``` 613 | 614 | ```python 615 | ## 2 创建一个example.zip的压缩文件,将animals文件夹下所有文件进行压缩。 616 | import zipfile 617 | import os 618 | newZip = zipfile.ZipFile('example.zip','w') 619 | for folderName, subFolders,fileNames in os.walk('D:\\animals'): 620 | for filename in fileNames: 621 | newZip.write(os.path.join(folderName,filename),compress_type=zipfile.ZIP_DEFLATED) 622 | newZip.close() 623 | ``` 624 | 625 | #### 1.1.9.2 读取zip文件 626 | 627 | 调用`zipfile.ZipFile(filename)`函数创建一个`ZipFile`对象(注意大写字母Z和F),filename是要读取zip文件的文件名。 628 | 629 | `ZipFile`对象中的两个常用方法: 630 | 631 | `namelis()`方法,返回zip文件中包含的所有文件和文件夹的字符串列表。 632 | 633 | `getinfo()`方法,返回一个关于特定文件的`ZipInfo`对象。 634 | 635 | `ZipInfo`对象的两个属性:`file_size`和`compress_size`,分别表示原来文件大小和压缩后文件大小。1.2.3.2 读取zip文件 636 | 637 | ``` 638 | import zipfile,os 639 | exampleZip = zipfile.ZipFile('example.zip') 640 | exampleZip.namelist() 641 | ``` 642 | 643 | ``` 644 | catInfo = exampleZip.getinfo('animals/Miki.txt') 645 | ``` 646 | 647 | ``` 648 | catInfo.file_size 649 | ``` 650 | 651 | ``` 652 | catInfo.compress_size 653 | ``` 654 | 655 | ``` 656 | print('Compressed file is %s x smaller!' %(round(catInfo.file_size/catInfo.compress_size,2))) 657 | ``` 658 | 659 | ``` 660 | exampleZip.close() 661 | ``` 662 | 663 | #### 1.1.9.3 从zip文件中解压缩 664 | 665 | `ZipFile` 对象的 `extractall()`方法:从zip文件中解压缩所有文件和文件夹,放到当前工作目录中。也可以向`extractall()`传递的一个文件夹名称,它将文件解压缩到那个文件夹, 而不是当前工作目录。如果传递的文件夹名称不存在,就会被创建。 666 | 667 | `ZipFile` 对象的 `extract()`方法:从zip文件中解压单个文件。也可以向 extract()传递第二个参数, 将文件解压缩到指定的文件夹, 而不是当前工作目录。如果第二个参数指定的文件夹不存在, Python 就会创建它。extract()的返回值是被压缩后文件的绝对路径。 668 | 669 | ```python 670 | import zipfile, os 671 | exampleZip = zipfile.ZipFile('example.zip') 672 | exampleZip.extractall('.\zip') 673 | exampleZip.close() 674 | ``` 675 | 676 | ```python 677 | exampleZip = zipfile.ZipFile('example.zip') 678 | exampleZip.extract('animals/Miki.txt') 679 | exampleZip.extract('animals/Miki.txt', 'D:\\animals\\folders') 680 | exampleZip.close() 681 | ``` 682 | 683 | ### 1.1.10 文件查找 684 | 685 | 对于文件操作,最需要熟练掌握的就是查找文件。前面介绍了使用os.listdir、os.walk方法可以批量列出当前工作目录的全部文件,下面介绍常用于查找特定文件的模块。 686 | 687 | #### 1.1.10.1 glob 688 | 689 | glob是Python自带的一个文件操作相关模块,用它可以查找符合条件的文件。例如,我们要找到当前目录下全部的.txt文档,可以用下面的代码。 690 | 691 | ```python 692 | import glob 693 | glob.glob('*.txt') 694 | ``` 695 | 696 | 这里主要是写匹配条件,“*”匹配任意个字符,“?”匹配单个字符,也可以用“[]”匹配指定范围内的字符,如[0-9]匹配数字。 697 | 698 | 699 | - glob.glob('*[0-9]*.*')可以匹配当前目录下文件名中带有数字的文件。 700 | - glob.glob(r'G:\*')可以获取G盘下的所有文件和文件夹,但是它不会进一步列明文件夹下的文件。也就是说,其返回的文件名只包括当前目录里的文件名,不包括子文件夹里的文件 701 | 702 | #### 1.1.10.2 fnmatch模块 703 | 704 | fnmatch也是Python自带的库,是专门用来进行文件名匹配的模块,使用它可以完成更为复杂的文件名匹配。它有4个函数,分别是fnmatch、fnmatchcase、filter和translate,其中最常用的是fnmatch函数,其语法如下。 705 | 706 | - fnmatch.fnmatch(filename,pattern) 707 | 708 | pattern表示匹配条件,测试文件名filename是否符合匹配条件。 709 | 710 | 下面找出目标文件夹里所有结尾带数字的文件 711 | 712 | ```python 713 | import os,fnmatch 714 | path = os.getcwd() # 获取当前代码文件所在目录 715 | for foldname, subfolders,filenames in os.walk(path): 716 | for filename in filenames: 717 | if fnmatch.fnmatch(filename,'*[0-9].*'): 718 | print(filename) 719 | ``` 720 | 721 | fnmatchcase和fnmatch函数类似,只是fnmatchcase函数强制区分字母大小写。 722 | 723 | 以上两个函数都返回True或者False,filter函数则返回匹配的文件名列表,其语法如下: 724 | 725 | - fnmatch.filter(filelist,pattern) 726 | 727 | #### 1.1.10.3 hashlib模块 728 | 729 | 随着计算机中文件越来越多,我们需要找出重复文件。重复文件可能有不同的文件名,不能简单用文件名和文件大小来判断。从科学角度,最简单的办法就是通过MD5来确定两个文件是不是一样的。 730 | 731 | Python自带的hashlib库里提供了获取文件MD5值的方法。 732 | 733 | ```python 734 | import hashlib 735 | m = hashlib.md5() 736 | f = open('bacon.txt','rb') 737 | m.update(f.read()) 738 | f.close() 739 | md5_value = m.hexdigest() 740 | print(md5_value) 741 | ``` 742 | 743 | 电子文件容易被篡改或者伪造,在出现纠纷时,怎么提供有力的证据来证明文件的真实性?一个可行的办法就是制作文件后对整个文件生成MD5值。一旦MD5值生成之后,文件发生过任何修改,MD5值都将改变,通过此方法可以确定文件是否被篡改过。 744 | 745 | ### 1.1.11 练习 746 | 747 | 1)、编写一个程序,遍历一个目录树,查找特定扩展名的文件(诸如.pdf 或.jpg)。不论这些文件的位置在哪里, 将它们拷贝到一个新的文件夹中。 748 | 749 | 2) 、一些不需要的、 巨大的文件或文件夹占据了硬盘的空间, 这并不少见。如果你试图释放计算机上的空间, 那么删除不想要的巨大文件效果最好。但首先你必须找到它们。编写一个程序, 遍历一个目录树, 查找特别大的文件或文件夹, 比方说, 超过100MB 的文件(回忆一下,要获得文件的大小,可以使用 os 模块的 `os.path.getsize()`)。将这些文件的绝对路径打印到屏幕上。 750 | 751 | 3)、编写一个程序, 在一个文件夹中, 找到所有带指定前缀的文件, 诸如 spam001.txt,spam002.txt 等,并定位缺失的编号(例如存在 spam001.txt 和 spam003.txt, 但不存在 spam002.txt)。让该程序对所有后面的文件改名, 消除缺失的编号。作为附加的挑战,编写另一个程序,在一些连续编号的文件中,空出一些编号,以便加入新的文件。 752 | 753 | ## 1.3 自动发送电子邮件 754 | 755 | 使用Python实现自动化邮件发送,可以让你摆脱繁琐的重复性业务,节省非常多的时间。 756 | 757 | Python有两个内置库:`smtplib`和`email`,能够实现邮件功能,`smtplib`库负责发送邮件,`email`库负责构造邮件格式和内容。 758 | 759 | 邮件发送需要遵守**SMTP**协议,Python内置对SMTP的支持,可以发送纯文本邮件、HTML邮件以及带附件的邮件。 760 | 761 | ```python 762 | #1 先导入相关的库和方法 763 | import smtplib #导入库 764 | from smtplib import SMTP_SSL #加密邮件内容,防止中途被截获 765 | from email.mime.text import MIMEText #构造邮件的正文 766 | from email.mime.image import MIMEImage #构造邮件的图片 767 | from email.mime.multipart import MIMEMultipart #把邮件的各个部分装在一起,邮件的主体 768 | from email.header import Header #邮件的文件头,标题,收件人 769 | ``` 770 | 771 | ```python 772 | #2 设置邮箱域名、发件人邮箱、邮箱授权码、收件人邮箱 773 | host_server = 'smtp.163.com' #sina 邮箱smtp服务器 #smtp 服务器的地址 774 | sender_163 = 'pythonauto_emai@163.com' #sender_163为发件人的邮箱 775 | pwd = 'DYEPOGLZDZYLOMRI' #pwd为邮箱的授权码'DYEPOGLZDZYLOMRI' 776 | #也可以自己注册个邮箱,邮箱授权码'DYEPOGLZDZYLOMRI' 获取方式可参考#http://help.163.com/14/0923/22/A6S1FMJD00754KNP.html 777 | 778 | # 设置接受邮箱,换成自己的邮箱即可 779 | receiver = '1121091694@qq.com' 780 | ``` 781 | 782 | ```python 783 | #3 构建MIMEMultipart对象代表邮件本身,可以往里面添加文本、图片、附件等 784 | msg = MIMEMultipart() #邮件主体 785 | ``` 786 | 787 | ```python 788 | #4 设置邮件头部内容 789 | mail_title = 'python办公自动化邮件' # 邮件标题 790 | msg["Subject"] = Header(mail_title,'utf-8') #装入主体 791 | msg["From"] = sender_163 #寄件人 792 | msg["To"] = Header("测试邮箱",'utf-8') #标题 793 | ``` 794 | 795 | ```python 796 | #5 添加正文文本 797 | mail_content = "您好,这是使用python登录163邮箱发送邮件的测试" #邮件的正文内容 798 | message_text = MIMEText(mail_content,'plain','utf-8') #构造文本,参数1:正文内容,参数2:文本格式,参数3:编码方式 799 | msg.attach(message_text) # 向MIMEMultipart对象中添加文本对象 800 | ``` 801 | 802 | ```python 803 | #6 添加图片 804 | image_data = open('D:\\animals\\cats\\zophie.jpg','rb') # 二进制读取图片 805 | message_image = MIMEImage(image_data.read()) # 设置读取获取的二进制数据 806 | image_data.close() # 关闭刚才打开的文件 807 | msg.attach(message_image) # 添加图片文件到邮件信息当中去 808 | ``` 809 | 810 | ```python 811 | # 7 添加附件(excel表格) 812 | atta = MIMEText(open('D:\\animals\\cats\\cat.xlsx', 'rb').read(), 'base64', 'utf-8') # 构造附件 813 | atta["Content-Disposition"] = 'attachment; filename="cat.xlsx"' # 设置附件信息 814 | msg.attach(atta) ## 添加附件到邮件信息当中去 815 | ``` 816 | 817 | ```python 818 | #8 发送邮件 819 | smtp = SMTP_SSL(host_server) #SSL登录 创建SMTP对象 820 | smtp.login(sender_163,pwd) ## 登录邮箱,传递参数1:邮箱地址,参数2:邮箱授权码 821 | smtp.sendmail(sender_163,receiver,msg.as_string()) # 发送邮件,传递参数1:发件人邮箱地址,参数2:收件人邮箱地址,参数3:把邮件内容格式改为str 822 | print("邮件发送成功") 823 | smtp.quit # 关闭SMTP对象 824 | ``` 825 | 826 | **参考!!!**: 827 | 828 | https://github.com/datawhalechina/team-learning-program/blob/master/OfficeAutomation/Task01%20%E6%96%87%E4%BB%B6%E8%87%AA%E5%8A%A8%E5%8C%96%E4%B8%8E%E9%82%AE%E4%BB%B6%E5%A4%84%E7%90%86.md 829 | 830 | 和 831 | 832 | 《学Python,不加班——轻松实现办公自动化》-何华平 一书的第三章高效办公文件管理 -------------------------------------------------------------------------------- /Task01-文件处理与邮件自动化/png/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task01-文件处理与邮件自动化/png/1.png -------------------------------------------------------------------------------- /Task01-文件处理与邮件自动化/png/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task01-文件处理与邮件自动化/png/2.png -------------------------------------------------------------------------------- /Task01-文件处理与邮件自动化/png/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task01-文件处理与邮件自动化/png/3.png -------------------------------------------------------------------------------- /Task01-文件处理与邮件自动化/png/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task01-文件处理与邮件自动化/png/4.png -------------------------------------------------------------------------------- /Task01-文件处理与邮件自动化/png/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task01-文件处理与邮件自动化/png/5.png -------------------------------------------------------------------------------- /Task01-文件处理与邮件自动化/png/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task01-文件处理与邮件自动化/png/6.png -------------------------------------------------------------------------------- /Task01-文件处理与邮件自动化/png/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task01-文件处理与邮件自动化/png/7.png -------------------------------------------------------------------------------- /Task01-文件处理与邮件自动化/png/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task01-文件处理与邮件自动化/png/8.png -------------------------------------------------------------------------------- /Task01-文件处理与邮件自动化/png/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task01-文件处理与邮件自动化/png/9.png -------------------------------------------------------------------------------- /Task01-文件处理与邮件自动化/png/os.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task01-文件处理与邮件自动化/png/os.png -------------------------------------------------------------------------------- /Task02-Python与Excel/OpenPyXL_test/creat_sheet_test.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/OpenPyXL_test/creat_sheet_test.xlsx -------------------------------------------------------------------------------- /Task02-Python与Excel/OpenPyXL_test/new_test.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/OpenPyXL_test/new_test.xlsx -------------------------------------------------------------------------------- /Task02-Python与Excel/OpenPyXL_test/test.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/OpenPyXL_test/test.xlsx -------------------------------------------------------------------------------- /Task02-Python与Excel/OpenPyXL_test/业务经理信息表_pd.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/OpenPyXL_test/业务经理信息表_pd.xlsx -------------------------------------------------------------------------------- /Task02-Python与Excel/OpenPyXL_test/业务经理信息表_xl.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/OpenPyXL_test/业务经理信息表_xl.xlsx -------------------------------------------------------------------------------- /Task02-Python与Excel/OpenPyXL_test/业务联系表.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/OpenPyXL_test/业务联系表.xlsx -------------------------------------------------------------------------------- /Task02-Python与Excel/OpenPyXL_test/业务联系表_pd.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/OpenPyXL_test/业务联系表_pd.xlsx -------------------------------------------------------------------------------- /Task02-Python与Excel/OpenPyXL_test/客户信息表_pd.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/OpenPyXL_test/客户信息表_pd.xlsx -------------------------------------------------------------------------------- /Task02-Python与Excel/OpenPyXL_test/客户信息表_xl.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/OpenPyXL_test/客户信息表_xl.xlsx -------------------------------------------------------------------------------- /Task02-Python与Excel/OpenPyXL_test/用户行为偏好.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/OpenPyXL_test/用户行为偏好.xlsx -------------------------------------------------------------------------------- /Task02-Python与Excel/OpenPyXL_test/用户行为偏好_1.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/OpenPyXL_test/用户行为偏好_1.xlsx -------------------------------------------------------------------------------- /Task02-Python与Excel/Python_Excel_OpenPyXL.md: -------------------------------------------------------------------------------- 1 | # Task 02 Python Excel 自动化之 OpenPyXL 2 | 3 | - [Task 02 Python Excel 自动化之 OpenPyXL](#task-02-python-excel-自动化之-openpyxl) 4 | - [2.0 包的安装](#20-包的安装) 5 | - [2.1 Excel读取](#21-excel读取) 6 | - [2.1.1 读取Excel中的工作表](#211-读取excel中的工作表) 7 | - [1. 读取Excel文件 `用户行为偏好.xlsx ` ,查看返回值属性](#1-读取excel文件-用户行为偏好xlsx--查看返回值属性) 8 | - [2. 查看对应工作簿包含的 sheet(工作表) 的名称,读取活动表](#2-查看对应工作簿包含的-sheet工作表-的名称读取活动表) 9 | - [3. 查看指定sheet信息](#3-查看指定sheet信息) 10 | - [2.1.2 读取工作表中的单元格](#212-读取工作表中的单元格) 11 | - [2.1.3 读取多个单元格的值](#213-读取多个单元格的值) 12 | - [2.1.4 练习题](#214-练习题) 13 | - [2.2 Excel写入](#22-excel写入) 14 | - [2.2.1 写入数据并保存](#221-写入数据并保存) 15 | - [1. 原有工作簿中修改数据并保存](#1-原有工作簿中修改数据并保存) 16 | - [2. 创建新的表格写入数据并保存](#2-创建新的表格写入数据并保存) 17 | - [2.2.2 将公式写入单元格保存](#222-将公式写入单元格保存) 18 | - [2.2.3 插入空列/行](#223-插入空列行) 19 | - [2.2.4 删除](#224-删除) 20 | - [2.2.5 移动](#225-移动) 21 | - [2.3 Excel 样式](#23-excel-样式) 22 | - [2.3.1设置字体样式](#231设置字体样式) 23 | - [1. 设置单个 cell(单元格) 字体样式](#1-设置单个-cell单元格-字体样式) 24 | - [2. 设置多个 cell 的字体样式](#2-设置多个-cell-的字体样式) 25 | - [2.3.2 设置边框样式](#232-设置边框样式) 26 | - [1. 设置单元格边框样式](#1-设置单元格边框样式) 27 | - [2.3.3 设置单元格其他样式](#233-设置单元格其他样式) 28 | - [1. 设置单元格背景色](#1-设置单元格背景色) 29 | - [2.设置水平居中](#2设置水平居中) 30 | - [3. 设置行高与列宽](#3-设置行高与列宽) 31 | - [2.3.3 合并、取消合并单元格](#233-合并取消合并单元格) 32 | - [2.3.5 练习题](#235-练习题) 33 | - [2.4 综合练习](#24-综合练习) 34 | - [2.4.1 将 业务联系表.xlsx 拆分成以下两个 excel:](#241-将-业务联系表xlsx-拆分成以下两个-excel) 35 | - [2.4.2 将 客户信息表.xlsx 和 客户关系表.xlsx 合并成一个excel](#242-将-客户信息表xlsx-和-客户关系表xlsx-合并成一个excel) 36 | - [2.5 后记](#25-后记) 37 | 38 | ## 2.0 包的安装 39 | 40 | 操作难度:⭐ 41 | 42 | 打开 CMD/Terminal 进入到自己环境后,执行下面语句安装`openpyxl`模块。 43 | ```bash 44 | pip3 install openpyxl 45 | ``` 46 | 47 | 注:openpyxl可以读/写 .xlsx /.xlsm /.xltx /.xltm 的格式文件,但是不支持去读 /.xls 格式;读取 xls 格式,可以安装 **xlrd** 模块,`pip3 install xlrd`,本章节以 /.xlsx 格式为主。 48 | 49 | ## 2.1 Excel读取 50 | 51 | 项目难度:⭐ 52 | 53 | - Excel 全称为 Microsoft Office Excel,2003年版本的是 xls 格式,2007和2007年之后的版本是 xlsx 格式。 54 | - xlsx 格式通过 `openpyxl` 模块打开; xls 格式通过 `xlwt` 模块写,`xlrd` 模块读取。 55 | - 本文以 xlsx 模式为例 56 | 57 | ### 2.1.1 读取Excel中的工作表 58 | 59 | **关于路径:** 60 | 61 | 文件应在当前工作目录才可直接用相对路径引用,可导入`os`,使用函数`os.getcwd()`弄清楚当前工作目录是什么,可使用`os.chdir()`改变当前工作目录,具体可参考第一章节。(此处显现为相对路径) 62 | 63 | 64 | ```python 65 | # 获取当前工作目录 66 | import os 67 | print(os.getcwd()) 68 | 69 | import warnings 70 | warnings.filterwarnings('ignore') 71 | root_path = './OpenPyXL_test/' 72 | ``` 73 | 74 | #### 1. 读取Excel文件 `用户行为偏好.xlsx ` ,查看返回值属性 75 | 76 | 77 | ```python 78 | # 导入模块,查看属性 79 | import openpyxl 80 | 81 | wb = openpyxl.load_workbook(root_path+'用户行为偏好.xlsx') 82 | type(wb) 83 | ``` 84 | 85 | 86 | 87 | 88 | openpyxl.workbook.workbook.Workbook 89 | 90 | 91 | 92 | 【代码解释】 93 | 94 | 这里我们使用 openpyxl 中的 load_workbook 函数来加载指定的 xlsx 文件,。 95 | - openpyxl.load_workbook( 96 | filename, 97 | read_only=False, 98 | keep_vba=False, 99 | data_only=False, 100 | keep_links=True, 101 | ) 102 | 103 | load_workbook 函数有五个参数,除 filename 外,其他参数都有默认值,各参数含义如下: 104 | 105 | - `filename`: str 类型,表示要打开的文件的相对/绝对路径; 106 | - `read_only`: bool 类型,是否以只读模式打开文件,默认值为 False,可读写; 107 | - `keep_vba`: bool 类型,是否保留文件中的 vba 内容(即使保留了也不一定在代码中能使用),默认值为 False,不保留; 108 | - `data_only`: bool 类型,如果单元格中是 excel 公式,是以公式计算后的值的形式显示还是以公式内容形式显示,默认值为 False,以公式内容形式展示; 109 | - `keep_links`: bool 类型,是否保留单元格中的外链,默认值为 True,保留外链; 110 | 111 | - 返回值类型: `openpyxl.workbook.Workbook` 112 | 113 | 如无特殊要求,我们只需要指定`filename`参数即可。 114 | 115 | 116 | 【小知识】 117 | 118 | **import * 和from...import...** 119 | 120 | `import *`和`from...import...`的区别 121 | 122 | - `import`导入一个模块,相当于导入的是一个文件夹,相对路径。 123 | - `from...import...`导入了一个模块中的一个函数,相当于文件夹中的文件,绝对路径。 124 | 125 | #### 2. 查看对应工作簿包含的 sheet(工作表) 的名称,读取活动表 126 | 127 | 128 | ```python 129 | # 导入模块中的函数,查询对应表的名称 130 | print(wb.sheetnames) 131 | ``` 132 | 133 | ['订单时长分布', 'Sheet3'] 134 | 135 | 136 | 【代码解释】 137 | 138 | 这里我们使用 `openpyxl.workbook.Workbook` 类对象的 `sheetnames` 属性来获取读取的工作簿中包含的 sheet(工作表) 的名称。 139 | 140 | 通过上述代码输出内容,我们可以知道 `用户行为偏好.xlsx` 中包含两个 sheet(工作表),分别是:订单时长分布、 Sheet3。 141 | 142 | 143 | ```python 144 | # 读取工作簿的活动表 145 | # 活动表是工作簿在 Excel 中打开时出现的工作表,在取得 Worksheet 对象后,可通过 title 属性取得它的名称。 146 | active_sheet = wb.active 147 | print(f'active_sheet对象: {active_sheet}') 148 | print(f'active_sheet 名称: {active_sheet.title}') 149 | ``` 150 | 151 | active_sheet对象: 152 | active_sheet 名称: 订单时长分布 153 | 154 | 155 | 【小知识】 156 | 157 | 活动表是可以修改的,在我们正常打开excel,完成修改后,保存excel,在关闭 excel 前显示的 sheet 就是活动表。 158 | 159 | #### 3. 查看指定sheet信息 160 | 161 | 162 | ```python 163 | # 通过传递表名字符串读取表、类型和名称、内容占据的大小 164 | sheet = wb.get_sheet_by_name('Sheet3') 165 | print(f'sheet: {sheet}') 166 | print(f'type(sheet): {type(sheet)}') 167 | print(f'sheet.title: {sheet.title}') 168 | print(f'sheet.dimensions: {sheet.dimensions}') 169 | ``` 170 | 171 | sheet: 172 | type(sheet): 173 | sheet.title: Sheet3 174 | sheet.dimensions: A1:I17 175 | 176 | 177 | 【代码解释】 178 | 179 | 这里我们使用 `openpyxl.workbook.Workbook` 类对象的 `get_sheet_by_name` 方法,通过指定 sheetname 的方式来获取读取的工作簿中指定的 sheet(工作表) 对象。 180 | 181 | 并使用 `openpyxl.worksheet.worksheet.Worksheet` 类对象的一些属性来获取 sheet 的基本信息,比如 `Worksheet.title`获取 sheet 名称,`Worksheet.dimensions` 获取 sheet 中值的范围。 182 | 183 | 184 | Workbook.get_sheet_by_name(name) 函数只有一个参数,就是:sheetname(工作表名称),功能是:通过 sheetname 获取到 Worksheet 对象,除了通过函数的方式获取到 Worksheet 对象,你还可以提过索引的方式,如: 185 | ```python 186 | wb['Sheet3'] 187 | ``` 188 | 189 | ### 2.1.2 读取工作表中的单元格 190 | 191 | ![image-20211110131533928](./imgs/2.3.png) 192 | 193 | **Cell(Excel单元格)** 194 | 195 | - Cell 对象有一个 value 属性,包含这个单元格中保存的值。 196 | - Cell 对象也有 row 、column 和 coordinate 属性,提供该单元格的位置信息。 197 | - Excel 用字母指定列,在Z列之后,列开始使用两个字母:AA、AB等,所以在调用的 cell() 方法时,可传入整数作为 row 和 column 关键字参数,也可以得到一个单元格。 198 | - 注:第一行或第一列的整数取1,而不是0. 199 | 200 | 201 | ```python 202 | # 从表中取得单元格 在 2.1.1 中我们已经读取过工作簿了 返回结果存储变量为 wb 203 | ## 获取表格名称 204 | print(f'sheetnames: {wb.sheetnames}') 205 | ``` 206 | 207 | sheetnames: ['订单时长分布', 'Sheet3'] 208 | 209 | 210 | 211 | ```python 212 | # 获取指定sheet 213 | sheet = wb.get_sheet_by_name('订单时长分布') 214 | 215 | # 通过单元格位置获取单元格对象,如:B1 216 | a = sheet['B1'] 217 | print(f"sheet[B1']: {a}") 218 | 219 | # 获取并打印 B1 单元格的文本内容 220 | print(f"sheet[B1'].value: {a.value}") 221 | 222 | # 获取并打印 B1 单元格所在行、列和数值 223 | print(f'Row: {a.row}, Column: {a.column}') 224 | 225 | # 获取并打印 B1 单元格坐标 和 值 226 | print(f'Cell {a.coordinate} is {a.value}') 227 | ``` 228 | 229 | sheet[B1']: 230 | sheet[B1'].value: 日期 231 | Row: 1, Column: 2 232 | Cell B1 is 日期 233 | 234 | 235 | 236 | ```python 237 | # 获取并打印出 B列 前8行的奇数行单元格的值 238 | for i in range(1,8,2): 239 | print(i, sheet.cell(row=i,column=2).value) 240 | ``` 241 | 242 | 1 日期 243 | 3 2020-07-24 00:00:00 244 | 5 2020-07-24 00:00:00 245 | 7 2020-07-24 00:00:00 246 | 247 | 248 | 249 | ```python 250 | # 确定表格的最大行数和最大列数,即表的大小 251 | print(f'sheet.max_row: {sheet.max_row}') 252 | print(f'sheet.max_column: {sheet.max_column}') 253 | ``` 254 | 255 | sheet.max_row: 14 256 | sheet.max_column: 4 257 | 258 | 259 | ### 2.1.3 读取多个单元格的值 260 | 261 | 262 | ```python 263 | # 方法一:直接通过sheet索引,A1到C8区域的值 264 | cells = sheet['A1:C8'] 265 | print(f'type(cells): {type(cells)} \n') 266 | 267 | # 遍历元组 print每一个cell值 268 | for rows in cells: 269 | for cell in rows: 270 | print(cell.value, end=" |") 271 | print("\n") 272 | ``` 273 | 274 | type(cells): 275 | 276 | 编号 |日期 |行为时长 | 277 | 278 | 71401.30952380953 |2020-07-24 00:00:00 |a | 279 | 280 | 71401.30952380953 |2020-07-24 00:00:00 |b | 281 | 282 | 71401.30952380953 |2020-07-24 00:00:00 |c | 283 | 284 | 71401.30952380953 |2020-07-24 00:00:00 |d | 285 | 286 | 71401.30952380953 |2020-07-24 00:00:00 |e | 287 | 288 | 71401.30952380953 |2020-07-24 00:00:00 |f | 289 | 290 | 71401.30952380953 |2020-07-24 00:00:00 |g | 291 | 292 | 293 | 294 | 295 | ```python 296 | # 方法二:sheet.iter_rows函数 按行获取数据 297 | rows = sheet.iter_rows(min_row=1, max_row=8, min_col=1, max_col=3) 298 | # 遍历元组 print每一个cell值 299 | for row in rows: 300 | for cell in row: 301 | print(cell.value, end=" |") 302 | print("\n") 303 | ``` 304 | 305 | 编号 |日期 |行为时长 | 306 | 307 | 71401.30952380953 |2020-07-24 00:00:00 |a | 308 | 309 | 71401.30952380953 |2020-07-24 00:00:00 |b | 310 | 311 | 71401.30952380953 |2020-07-24 00:00:00 |c | 312 | 313 | 71401.30952380953 |2020-07-24 00:00:00 |d | 314 | 315 | 71401.30952380953 |2020-07-24 00:00:00 |e | 316 | 317 | 71401.30952380953 |2020-07-24 00:00:00 |f | 318 | 319 | 71401.30952380953 |2020-07-24 00:00:00 |g | 320 | 321 | 322 | 323 | 324 | ```python 325 | # 方法三:sheet.iter_cols函数 按列获取数据 326 | cols = sheet.iter_cols(min_row=1, max_row=4, min_col=1, max_col=3) 327 | # 遍历元组 print每一个cell值 328 | for col in cols: 329 | for cell in col: 330 | print(cell.value, end=" |") 331 | print("\n") 332 | ``` 333 | 334 | 编号 |71401.30952380953 |71401.30952380953 |71401.30952380953 | 335 | 336 | 日期 |2020-07-24 00:00:00 |2020-07-24 00:00:00 |2020-07-24 00:00:00 | 337 | 338 | 行为时长 |a |b |c | 339 | 340 | 341 | 342 | ### 2.1.4 练习题 343 | 344 | 找出`用户行为偏好.xlsx`中 Sheet3 表中空着的格子,并输出这些格子的坐标 345 | 346 | 347 | ```python 348 | from openpyxl import load_workbook 349 | 350 | exl = load_workbook(root_path+'用户行为偏好.xlsx') 351 | sheet3 = exl.get_sheet_by_name('Sheet3') 352 | ``` 353 | 354 | 355 | ```python 356 | sheet3.dimensions 357 | ``` 358 | 359 | 360 | 361 | 362 | 'A1:I17' 363 | 364 | 365 | 366 | 367 | ```python 368 | # 直接通过sheet索引,sheet3.dimensions获取sheet数据区域 369 | cells = sheet3[sheet3.dimensions] 370 | 371 | # 遍历元组 判断每一个cell值是否为空 372 | for rows in cells: 373 | for cell in rows: 374 | if not cell.value: 375 | print(f'{cell.coordinate} is None \n') 376 | ``` 377 | 378 | D3 is None 379 | 380 | D8 is None 381 | 382 | G10 is None 383 | 384 | 385 | 386 | ## 2.2 Excel写入 387 | 388 | 项目难度:⭐ 389 | 390 | ### 2.2.1 写入数据并保存 391 | 392 | #### 1. 原有工作簿中修改数据并保存 393 | 394 | 395 | ```python 396 | # 1) 导入 openpyxl 中的 load_workbook 函数 397 | from openpyxl import load_workbook 398 | 399 | # 2) 获取指定 excel文件对象 Workbook 400 | exl = load_workbook(filename=root_path+'用户行为偏好.xlsx') 401 | # 3) 通过指定 sheetname 从 Workbook 中获取 sheet 对象 Worksheet 402 | sheet = exl.get_sheet_by_name('Sheet3') 403 | # 4) 通过索引方式获取指定 cell 值,并重新赋值 404 | print(f"修改前 sheet['A1']: {sheet['A1'].value}") 405 | sheet['A1'].value = 'hello world' 406 | print(f"修改后 sheet['A1']: {sheet['A1'].value}") 407 | # 5) 保存修改后的内容 408 | # 如果 filename 和原文件同名,则是直接在原文件中修改; 409 | # 否则会新建一个 excel 文件,并保存内容 410 | exl.save(filename=root_path+'用户行为偏好_1.xlsx') # 保存到一个新文件中 新文件名称为:用户行为偏好_1.xlsx 411 | ``` 412 | 413 | 修改前 sheet['A1']: 1 414 | 修改后 sheet['A1']: hello world 415 | 416 | 417 | 418 | ```python 419 | # 验证保存修改内容是否成功 420 | exl_1 = load_workbook(filename=root_path+'用户行为偏好_1.xlsx') 421 | # 我们将原表中 Sheet3 中的 A1 值改为了 'hello world' 422 | # 所以读取保存文件,查看对应值是否为 'hello world' 即可 423 | a1 = exl_1['Sheet3']['A1'].value 424 | if a1 == 'hello world': 425 | print(f"修改保存成功啦~,exl_1['Sheet3']['A1'].value = {a1}") 426 | else: 427 | print(f"修改保存有问题,现在exl_1['Sheet3']['A1'].value = {a1}") 428 | ``` 429 | 430 | 修改保存成功啦~,exl_1['Sheet3']['A1'].value = hello world 431 | 432 | 433 | 【代码解释】 434 | 435 | 从这里我们可以看到,我们只需要获取到 sheet 中的 cell 对象后,就可以通过改变 cell.value 的值来改变 对应单元格中的值,然后使用 Workbook 对象的 save 函数可以将修改后的工作簿内容保存起来。 436 | 437 | #### 2. 创建新的表格写入数据并保存 438 | 439 | 440 | ```python 441 | # 1) 导入 openpyxl 中的 Workbook 类 442 | from openpyxl import Workbook 443 | 444 | # 2) 初始化一个 Workbook 对象 445 | wb = Workbook() 446 | print(f'默认sheet:{wb.sheetnames}') 447 | 448 | # 3) 通过 Workbook 对象的 create_sheet 函数创建一个 sheet 449 | # title sheet 名称 450 | # index sheet 位置,默认从0开始 451 | sheet = wb.create_sheet(title='mysheet', index=0) 452 | print(f'添加后sheet:{wb.sheetnames}') 453 | 454 | # 4) 在新建的 sheet 中写入数据 455 | # 比如 在 A1 单元格中写入 'this is test' 456 | sheet['A1'].value = 'this is test' 457 | 458 | print(f"sheet['A1'].value = {sheet['A1'].value}") 459 | 460 | # 保存 461 | wb.save(root_path+'creat_sheet_test.xlsx') 462 | ``` 463 | 464 | 默认sheet:['Sheet'] 465 | 添加后sheet:['mysheet', 'Sheet'] 466 | sheet['A1'].value = this is test 467 | 468 | 469 | ### 2.2.2 将公式写入单元格保存 470 | 471 | 472 | ```python 473 | # 1) 导入 openpyxl 中的 load_workbook 函数 474 | from openpyxl import load_workbook 475 | 476 | # 2) 获取指定 excel文件对象 Workbook 477 | exl_1 = load_workbook(filename=root_path+'用户行为偏好_1.xlsx') 478 | # 3) 通过指定 sheetname 从 Workbook 中获取 sheet 对象 Worksheet 479 | sheet = exl_1['订单时长分布'] 480 | 481 | print(f'订单时长分布 值范围: {sheet.dimensions}') #先查看原有表格的单元格范围,防止替代原有数据 482 | ``` 483 | 484 | 订单时长分布 值范围: A1:D14 485 | 486 | 487 | 488 | ```python 489 | # 单元格 A15 中写入 合计 490 | sheet['A15'].value = '合计' 491 | ``` 492 | 493 | 494 | ```python 495 | # 单元格 D15 中写入求和公式:SUM(D2:D14) 496 | sheet['D15'] = '=SUM(D2:D14)' 497 | exl_1.save(filename='用户行为偏好_1.xlsx') 498 | ``` 499 | 500 | 501 | ```python 502 | # 使用 xlwings 打开 excel 文件然后保存 使写入的 公式生效 503 | import xlwings as xw 504 | # 打开工作簿 505 | app = xw.App(visible=False, add_book=False) 506 | wb = app.books.open('用户行为偏好_1.xlsx') 507 | wb.save() 508 | # 关闭工作簿 509 | wb.close() 510 | app.quit() 511 | ``` 512 | 513 | 514 | ```python 515 | # 验证写入是否成功 516 | # 1) 获取指定 excel文件对象 Workbook, 517 | # 并设置 data_only=True,表示读取的时候如果单元格内是公式的话,以公式计算后的值的形式显示 518 | exl_2 = load_workbook(filename = '用户行为偏好_1.xlsx', data_only=True) 519 | # 2) 打印相关信息 520 | sheet = exl_2['订单时长分布'] 521 | print(f"sheet['A15']={sheet['A15'].value},sheet['D15']={sheet['D15'].value}") 522 | print(f"{sheet['D1'].value} 求和值为SUM(D2:D14)={sheet['D15'].value}") 523 | ``` 524 | 525 | sheet['A15']=合计,sheet['D15']=4004.7261561561563 526 | 次数 求和值为SUM(D2:D14)=4004.7261561561563 527 | 528 | 529 | 【注意】 530 | 531 | 即使设置了 data_only=True,也不能立即获取到刚刚添加的公式计算后的结果,需要自己 手动/添加代码 打开下 对应excel表格,然后 ctrl s保存下,再运行上面代码才能获取到对应公式计算后的值。 532 | 533 | 你可以使用下面代码自动打开指定 excel 文件然后保存使写入的公式生效,使用前你需要安装 xlwings,输入`pip3 install xlwings`即可,再后面我们也会学习这个模块。 534 | 535 | ```python 536 | # 使用 xlwings 打开 excel 文件然后保存 使写入的 公式生效 537 | import xlwings as xw 538 | # 打开工作簿 539 | app = xw.App(visible=False, add_book=False) 540 | wb = app.books.open('用户行为偏好_1.xlsx') 541 | wb.save() 542 | # 关闭工作簿 543 | wb.close() 544 | app.quit() 545 | ``` 546 | 547 | ### 2.2.3 插入空列/行 548 | 549 | 550 | ```python 551 | # 获取指定 sheet 552 | sheet = exl_1['Sheet3'] 553 | 554 | # 插入列数据 insert_cols(idx,amount=1) 555 | # idx是插入位置,amount是插入列数,默认是1 556 | # idx=2第2列,第2列前插入一列 557 | sheet.insert_cols(idx=2) 558 | # 第2列前插入5 559 | # sheet.insert_cols(idx=2, amount=5) 560 | 561 | # 插入行数据 insert_rows(idx,amount=1) 562 | # idx是插入位置,amount是插入行数,默认是1 563 | # 在第二行前插入一行 564 | sheet.insert_rows(idx=2) 565 | # 第2行前插入5行 566 | # sheet.insert_rows(idx=2, amount=5) 567 | 568 | exl_1.save(filename=root_path+'用户行为偏好_1.xlsx') 569 | ``` 570 | 571 | ### 2.2.4 删除 572 | 573 | 574 | ```python 575 | # 删除多列 576 | sheet.delete_cols(idx=5, amount=2) 577 | # 删除多行 578 | sheet.delete_rows(idx=2, amount=5) 579 | 580 | exl_1.save(filename=root_path+'用户行为偏好_1.xlsx') 581 | ``` 582 | 583 | ### 2.2.5 移动 584 | 585 | 当数字为正即向下或向右,为负即为向上或向左 586 | 587 | 588 | ```python 589 | # 移动 590 | # 当数字为正即向下或向右,为负即为向上或向左 591 | sheet.move_range('B3:E16',rows=1,cols=-1) 592 | exl_1.save(filename=root_path+'用户行为偏好_1.xlsx') 593 | ``` 594 | 595 | ## 2.3 Excel 样式 596 | 597 | 项目难度:⭐⭐ 598 | 599 | ### 2.3.1设置字体样式 600 | 601 | #### 1. 设置单个 cell(单元格) 字体样式 602 | 603 | `Font(name字体名称,size大小,bold粗体,italic斜体,color颜色)` 604 | 605 | 606 | ```python 607 | # 1) 导入 openpyxl 中的 load_workbook 函数 608 | # 导入 openpyxl 中的 styles 模块中的 Font 类 609 | from openpyxl import load_workbook 610 | from openpyxl.styles import Font 611 | 612 | # 2) 获取指定 excel文件对象 Workbook 613 | exl_1 = load_workbook(filename=root_path+'用户行为偏好_1.xlsx') 614 | # 3) 通过指定 sheetname 从 Workbook 中获取 sheet 对象 Worksheet 615 | sheet = exl_1['订单时长分布'] 616 | ``` 617 | 618 | 619 | ```python 620 | # 4) 获取到指定 cell 后,查看cell字体属性 621 | cell = sheet['A1'] 622 | cell.font 623 | ``` 624 | 625 | 626 | 627 | 628 | 629 | Parameters: 630 | name='宋体', charset=134, family=3.0, b=True, i=False, strike=None, outline=None, shadow=None, condense=None, color= 631 | Parameters: 632 | rgb=None, indexed=None, auto=None, theme=1, tint=0.0, type='theme', extend=None, sz=11.0, u=None, vertAlign=None, scheme='minor' 633 | 634 | 635 | 636 | 637 | ```python 638 | # 5) 实例化一个 Font 对象,设置字体样式 639 | # 字体改为:黑体 大小改为:20 设置为:加粗 斜体 红色 640 | font = Font(name='黑体', size=20, bold=True, italic=True, color='FF0000') 641 | cell.font = font 642 | # 6) 保存修改 643 | exl_1.save(filename=root_path+'用户行为偏好_1.xlsx') 644 | ``` 645 | 646 | #### 2. 设置多个 cell 的字体样式 647 | 648 | 649 | ```python 650 | # 上面我们已经获取到了 '用户行为偏好_1.xlsx' 中的 订单时长分布 工作表 651 | # 我们处理了 单元格 A1 的字体样式,我们也可以通过遍历的形式,批量设置单元格字体样式 652 | 653 | # 1) 获取要处理的单元格 654 | # 通过 sheet 索引获取第二行 cell 655 | # 获取列可以用 字母索引,如 sheet['A'] 获取第一列 cell 656 | cells = sheet[2] 657 | # 2) 实例化一个 Font 对象,设置字体样式 658 | # 字体改为:黑体 大小改为:10 设置为:加粗 斜体 红色 659 | font = Font(name='黑体', size=10, bold=True, italic=True, color='FF0000') 660 | # 3) 遍历给每一个 cell 都设置上对应字体样式 661 | for cell in cells: 662 | cell.font = font 663 | # 4) 保存修改 664 | exl_1.save(filename=root_path+'用户行为偏好_1.xlsx') 665 | ``` 666 | 667 | ### 2.3.2 设置边框样式 668 | 669 | #### 1. 设置单元格边框样式 670 | 671 | `Side`:边线样式设置类,边线颜色等 672 | 673 | Side(style=None, color=None, border_style=None) 674 | 675 | - style:边线的样式,有以下值可选:double, mediumDashDotDot, slantDashDot, dashDotDot, dotted, hair, mediumDashed, dashed, dashDot, thin, mediumDashDot, medium, thick 676 | - color:边线颜色 677 | - border_style:style 的别名,必须设置,一般直接设置 border_style 就行,不用设置 style 678 | 679 | `Border`:边框定位类,左右上下边线 680 | 681 | Border常用参数解释: 682 | 683 | - top bottom left right diagonal:上下左右和对角线的边线样式,为 Side 对象 684 | - diagonalDown:对角线从左上角向右下角方向,默认为 False 685 | - diagonalUp:对角线从右上角向左下角方向,默认为 False 686 | 687 | 688 | ```python 689 | # 上面我们已经获取到了 '用户行为偏好_1.xlsx' 中的 订单时长分布 工作表 sheet 690 | # 1) 导入 openpyxl 中的 styles 模块中的 Side, Border 类 691 | from openpyxl.styles import Side, Border 692 | # 2) 首先初始化一个边线对象(也可以设置多个) 693 | side = Side(border_style='double', color='FF000000') 694 | # 3) 通过 Border 去设置 整个单元格边框样式 695 | border = Border(left=side, right=side, top=side, bottom=side, diagonal=side, diagonalDown=True, diagonalUp=True) 696 | ``` 697 | 698 | 699 | ```python 700 | # 4) 查看目前单元格边框样式 701 | # 获取第一行 cells 702 | cells = sheet[1] 703 | # 取出一个 cell 看边框样式 704 | cells[0].border 705 | ``` 706 | 707 | 708 | 709 | 710 | 711 | Parameters: 712 | outline=True, diagonalUp=False, diagonalDown=False, start=None, end=None, left= 713 | Parameters: 714 | style=None, color=None, right= 715 | Parameters: 716 | style=None, color=None, top= 717 | Parameters: 718 | style=None, color=None, bottom= 719 | Parameters: 720 | style=None, color=None, diagonal= 721 | Parameters: 722 | style=None, color=None, vertical=None, horizontal=None 723 | 724 | 725 | 726 | 727 | ```python 728 | # 5) 修改边框样式,并保存修改 729 | for cell in cells: 730 | cell.border = border 731 | exl_1.save(filename=root_path+'用户行为偏好_1.xlsx') 732 | ``` 733 | 734 | ### 2.3.3 设置单元格其他样式 735 | 736 | #### 1. 设置单元格背景色 737 | 738 | 739 | ```python 740 | # 上面我们已经获取到了 '用户行为偏好_1.xlsx' 中的 订单时长分布 工作表 sheet 741 | # 1) 从 openpyxl.styles 中导入 背景颜色设置类 PatternFill, GradientFill 742 | from openpyxl.styles import PatternFill, GradientFill 743 | 744 | # 2) 实例化 PatternFill 对象,fill_type 参数必须指定 745 | pattern_fill = PatternFill(fill_type='solid',fgColor="DDDDDD") 746 | # 3) 实例化 GradientFill 对象,填充类型 type 默认为 linear 747 | gradient_fill = GradientFill(stop=('FFFFFF', '99ccff','000000')) 748 | ``` 749 | 750 | 751 | ```python 752 | # 4) 获取指定 cells 遍历填充 753 | # 对第三行 PatternFill 模式设置背景色 754 | cells = sheet[3] 755 | for cell in cells: 756 | cell.fill = pattern_fill 757 | 758 | # 对第四行 GradientFill 模式设置背景色 759 | cells = sheet[4] 760 | for cell in cells: 761 | cell.fill = gradient_fill 762 | 763 | # 5) 保存修改 764 | exl_1.save(filename=root_path+'用户行为偏好_1.xlsx') 765 | ``` 766 | 767 | #### 2.设置水平居中 768 | 769 | openpyxl.styles 中的 Alignment 类常用参数介绍: 770 | 771 | - horizontal:水平对齐,常见值 `distributed, justify, center, left, fill, centerContinuous, right, general` 772 | - vertical:垂直对齐,常见值 `bottom, distributed, justify, center, top` 773 | - textRotation:文字旋转角度,数值:0-180 774 | - wrapText:是否自动换行,bool值,默认 False 775 | 776 | 777 | ```python 778 | # 上面我们已经获取到了 '用户行为偏好_1.xlsx' 中的 订单时长分布 工作表 sheet 779 | # 1) 从 openpyxl.styles 中导入 对齐方式设置类 Alignment 780 | from openpyxl.styles import Alignment 781 | 782 | # 2) 实例化一个 Alignment 对象,设置水平、垂直居中 783 | alignment = Alignment(horizontal='center', vertical='center') 784 | 785 | # 3) 获取指定 cells 遍历填充 786 | # 对第五行数据设置上面的对齐方式 787 | cells = sheet[5] 788 | for cell in cells: 789 | cell.alignment = alignment 790 | # 4) 保存修改 791 | exl_1.save(filename=root_path+'用户行为偏好_1.xlsx') 792 | ``` 793 | 794 | #### 3. 设置行高与列宽 795 | 796 | 797 | ```python 798 | # 1) 设置行高,通过 row_dimensions 和 column_dimensions 来获取行和列对象 799 | # 2) 设置第1行行高为 30 800 | sheet.row_dimensions[1].height = 30 801 | # 3) 设置第3列列款为 24 802 | sheet.column_dimensions['C'].width = 24 803 | # 4) 保存修改 804 | exl_1.save(filename=root_path+'用户行为偏好_1.xlsx') 805 | ``` 806 | 807 | ### 2.3.3 合并、取消合并单元格 808 | 809 | 810 | ```python 811 | # 注意:合并后的单元格只会显示合并区域中最右上角的单元格的值,会导致其他单元格内容丢失 812 | # 上面我们已经获取到了 '用户行为偏好_1.xlsx' 对象 exl_1,我们可以通过 exl_1 来索引获取自己想要的 sheet 813 | # 1) 获取 Sheet3 这个工作表 814 | sheet = exl_1['Sheet3'] 815 | 816 | # 合并指定区域单元格 817 | sheet.merge_cells('A1:B2') 818 | 819 | # sheet.merge_cells(start_row=1, start_column=3, 820 | # end_row=2, end_column=4) 821 | 822 | # 保存修改 823 | exl_1.save(filename=root_path+'用户行为偏好_1.xlsx') 824 | ``` 825 | 826 | 827 | ```python 828 | # 解除合并 829 | sheet.unmerge_cells('A1:B2') 830 | 831 | # sheet.unmerge_cells(start_row=1, start_column=3, 832 | # end_row=2, end_column=4) 833 | 834 | # 保存修改 835 | exl_1.save(filename=root_path+'用户行为偏好_1.xlsx') 836 | ``` 837 | 838 | ### 2.3.5 练习题 839 | 840 | 打开 test.xlsx 文件,找出文件中购买数量 `buy_mount` 超过5的单元格,并对其标红、加粗、加上红色边框。 841 | 842 | 843 | ```python 844 | # 1) 导入 openpyxl 相关函数和类 845 | from openpyxl import load_workbook 846 | from openpyxl.styles import Font, Side, Border 847 | 848 | # 2) 读取 test.xlsx 文件,并筛选出 buy_mount 这一列 849 | workbook = load_workbook(root_path+'test.xlsx') 850 | sheet = workbook.active 851 | buy_mount = sheet['B'] 852 | ``` 853 | 854 | 855 | ```python 856 | # 3) 设置边框 文字样式 857 | side = Side(style='thin', color='FF0000') 858 | border = Border(left=side, right=side, top=side, bottom=side) 859 | font = Font(bold=True, color='FF0000') 860 | ``` 861 | 862 | 863 | ```python 864 | # 4) 遍历判断 cell 值是否满足筛选条件 865 | for cell in buy_mount: 866 | if isinstance(cell.value, float) and cell.value > 5: 867 | cell.font = font 868 | cell.border = border 869 | # 5) 修改内容另存为 new_test.xlsx 870 | workbook.save(root_path+'new_test.xlsx') 871 | ``` 872 | 873 | ## 2.4 综合练习 874 | 875 | ### 2.4.1 将 业务联系表.xlsx 拆分成以下两个 excel: 876 | - 客户信息表:客户名称 客户地址 客户方负责人 性别 联系电话 对接业务经理编号 877 | - 业务经理信息表:业务经理编号 所在分区 所在区域 业务经理姓名 878 | 879 | 880 | ```python 881 | # 1) 导入 openpyxl 相关函数和类 882 | from openpyxl import load_workbook, Workbook 883 | 884 | # 2) 读取原表数据 885 | wb = load_workbook(root_path+'业务联系表.xlsx') 886 | # 3) 获取工作表 887 | sheet = wb.active 888 | ``` 889 | 890 | 891 | ```python 892 | # 草稿纸 893 | # 我们知道我们表格的实际列名在第二行 894 | # 获取每列第二行的坐标和值 895 | for i in sheet[2]: 896 | print(i.coordinate, i.value) 897 | ``` 898 | 899 | A2 业务经理编号 900 | B2 分区 901 | C2 区域 902 | D2 业务经理 903 | E2 客户名称 904 | F2 客户地址 905 | G2 客户方负责人 906 | H2 性别 907 | I2 联系电话 908 | J2 备注 909 | 910 | 911 | 912 | ```python 913 | sheet.max_column, sheet.max_row 914 | ``` 915 | 916 | 917 | 918 | 919 | (10, 57) 920 | 921 | 922 | 923 | 924 | ```python 925 | # 4) 筛选出需要的列 926 | # 4.1) 客户信息表:客户名称 客户地址 客户方负责人 性别 联系电话 备注 对接业务经理编号 927 | cust_info = {'业务经理编号': 'A', '客户名称': 'B', '客户地址': 'C', '客户方负责人': 'D', '性别': 'E', '联系电话': 'F', '备注': 'G'} 928 | 929 | # 4.2) 新建一个工作簿,并将默认sheet名称改成 客户信息 930 | cust_info_excel = Workbook() 931 | cust_info_sh = cust_info_excel.active 932 | cust_info_sh.title = '客户信息' 933 | ``` 934 | 935 | 936 | ```python 937 | # 4.3) 遍历筛选,如果是需要的表头,就将该列的值复制到新的工作簿中的 客户信息 工作表中 938 | for i in sheet[2]: 939 | if i.value in cust_info: 940 | # 遍历将这一列中除了第一个cell外的所有cell值复制到新表 941 | for cell in sheet[i.coordinate[0]]: 942 | if cell.row == 1: 943 | continue 944 | cust_info_sh[f'{cust_info[i.value]}{cell.row-1}'].value = cell.value 945 | ``` 946 | 947 | 948 | ```python 949 | # 5) 筛选出需要的列 950 | # 5.1) 业务经理信息表:业务经理编号 所在分区 所在区域 业务经理姓名 951 | manager_info = {'业务经理编号': 'A', '分区': 'B', '区域': 'C', '业务经理': 'D'} 952 | 953 | # 5.2) 新建一个工作簿,并将默认sheet名称改成 客户信息 954 | manager_info_excel = Workbook() 955 | manager_info_sh = manager_info_excel.active 956 | manager_info_sh.title = '业务经理信息' 957 | ``` 958 | 959 | 960 | ```python 961 | # 5.3) 遍历筛选,如果是需要的表头,就将该列的值复制到新的工作簿中的 业务经理信息 工作表中 962 | for i in sheet[2]: 963 | if i.value in manager_info: 964 | # 遍历将这一列中除了第一个cell外的所有cell值复制到新表 965 | for cell in sheet[i.coordinate[0]]: 966 | if cell.row == 1: 967 | continue 968 | manager_info_sh[f'{manager_info[i.value]}{cell.row-1}'].value = cell.value 969 | ``` 970 | 971 | 972 | ```python 973 | # 6.1 ) 保存 客户信息表 工作簿内容 974 | cust_info_excel.save(root_path+'客户信息表_xl.xlsx') 975 | # 6.2) 保存 业务经理信息表 工作簿内容 976 | manager_info_excel.save(root_path+'业务经理信息表_xl.xlsx') 977 | ``` 978 | 979 | 以上,虽然完成了数据拆分,但是对于进一步数据处理,继续使用 openpyxl 并不是很便捷,比如数据去重,筛选等,接下来我将给大家介绍如何使用 pandas 更便捷的处理 excel 数据。 980 | 981 | 982 | ```python 983 | import pandas as pd 984 | 985 | # 1) 读取数据 986 | data = pd.read_excel(root_path+'业务联系表.xlsx', header=1) 987 | ``` 988 | 989 | 990 | ```python 991 | # 2) 数据筛选处理 992 | # 2.1) 客户信息表 993 | # 筛选出 客户信息表 需要的列 994 | cust_info_pd = data[['业务经理编号', '客户名称', '客户地址', '客户方负责人', '性别', '联系电话', '备注']] 995 | # 去除重复行 996 | cust_info_pd.drop_duplicates(inplace=True) 997 | # 打印出前三行 998 | cust_info_pd.head(3) 999 | ``` 1000 | 1001 | 1002 | 1003 | 1004 |
1005 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | 1040 | 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | 1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | 1061 | 1062 | 1063 |
业务经理编号客户名称客户地址客户方负责人性别联系电话备注
01尹承望*****-*****-****孙康适***-****-***NaN
11何茂材*****-*****-****孙康适***-****-***NaN
21徐新霁*****-*****-****孙康适***-****-***NaN
1064 |
1065 | 1066 | 1067 | 1068 | 1069 | ```python 1070 | # 2.2) 业务经理信息表 1071 | # 筛选出 业务经理信息表 需要的列,并打印出前三行 1072 | manager_info_pd = data[['业务经理编号', '分区', '区域', '业务经理']] 1073 | # 去除重复行 1074 | manager_info_pd.drop_duplicates(inplace=True) 1075 | # 打印出前三行 1076 | manager_info_pd.head(3) 1077 | ``` 1078 | 1079 | 1080 | 1081 | 1082 |
1083 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | 1103 | 1104 | 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 |
业务经理编号分区区域业务经理
01南区贵州占亮
52南区贵州李朝华
113北区河北王一磊
1130 |
1131 | 1132 | 1133 | 1134 | 1135 | ```python 1136 | # 3) 数据保存 1137 | cust_info_pd.to_excel(root_path+'客户信息表_pd.xlsx', index=None) 1138 | manager_info_pd.to_excel(root_path+'业务经理信息表_pd.xlsx', index=None) 1139 | ``` 1140 | 1141 | ### 2.4.2 将 客户信息表.xlsx 和 客户关系表.xlsx 合并成一个excel 1142 | 1143 | 1144 | 1145 | ```python 1146 | # 接上面的,将 客户信息表.xlsx 和 客户关系表.xlsx 合并成一个excel 1147 | # 这里我们依然用 pandas 来处理 1148 | business_contact = pd.merge(manager_info_pd, cust_info_pd, on='业务经理编号') 1149 | # 查看合并后数据基本信息 1150 | business_contact.info() 1151 | ``` 1152 | 1153 | 1154 | Int64Index: 55 entries, 0 to 54 1155 | Data columns (total 10 columns): 1156 | # Column Non-Null Count Dtype 1157 | --- ------ -------------- ----- 1158 | 0 业务经理编号 55 non-null int64 1159 | 1 分区 55 non-null object 1160 | 2 区域 55 non-null object 1161 | 3 业务经理 55 non-null object 1162 | 4 客户名称 55 non-null object 1163 | 5 客户地址 55 non-null object 1164 | 6 客户方负责人 55 non-null object 1165 | 7 性别 55 non-null object 1166 | 8 联系电话 55 non-null object 1167 | 9 备注 0 non-null float64 1168 | dtypes: float64(1), int64(1), object(8) 1169 | memory usage: 4.7+ KB 1170 | 1171 | 1172 | 1173 | ```python 1174 | # 查看前10条数据 1175 | business_contact.head(10) 1176 | ``` 1177 | 1178 | 1179 | 1180 | 1181 |
1182 | 1195 | 1196 | 1197 | 1198 | 1199 | 1200 | 1201 | 1202 | 1203 | 1204 | 1205 | 1206 | 1207 | 1208 | 1209 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 | 1216 | 1217 | 1218 | 1219 | 1220 | 1221 | 1222 | 1223 | 1224 | 1225 | 1226 | 1227 | 1228 | 1229 | 1230 | 1231 | 1232 | 1233 | 1234 | 1235 | 1236 | 1237 | 1238 | 1239 | 1240 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 | 1247 | 1248 | 1249 | 1250 | 1251 | 1252 | 1253 | 1254 | 1255 | 1256 | 1257 | 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | 1264 | 1265 | 1266 | 1267 | 1268 | 1269 | 1270 | 1271 | 1272 | 1273 | 1274 | 1275 | 1276 | 1277 | 1278 | 1279 | 1280 | 1281 | 1282 | 1283 | 1284 | 1285 | 1286 | 1287 | 1288 | 1289 | 1290 | 1291 | 1292 | 1293 | 1294 | 1295 | 1296 | 1297 | 1298 | 1299 | 1300 | 1301 | 1302 | 1303 | 1304 | 1305 | 1306 | 1307 | 1308 | 1309 | 1310 | 1311 | 1312 | 1313 | 1314 | 1315 | 1316 | 1317 | 1318 | 1319 | 1320 | 1321 | 1322 | 1323 | 1324 | 1325 | 1326 | 1327 | 1328 | 1329 | 1330 | 1331 | 1332 | 1333 | 1334 | 1335 | 1336 | 1337 | 1338 | 1339 | 1340 | 1341 | 1342 | 1343 |
业务经理编号分区区域业务经理客户名称客户地址客户方负责人性别联系电话备注
01南区贵州占亮尹承望*****-*****-****孙康适***-****-***NaN
11南区贵州占亮何茂材*****-*****-****孙康适***-****-***NaN
21南区贵州占亮徐新霁*****-*****-****孙康适***-****-***NaN
31南区贵州占亮郭承悦*****-*****-****邓翰翮***-****-***NaN
41南区贵州占亮梁浩思*****-*****-****邓翰翮***-****-***NaN
52南区贵州李朝华毛英朗*****-*****-****邓翰翮***-****-***NaN
62南区贵州李朝华侯俊美*****-*****-****任敏智***-****-***NaN
72南区贵州李朝华许高轩*****-*****-****任敏智***-****-***NaN
82南区贵州李朝华段英豪*****-*****-****任敏智***-****-***NaN
92南区贵州李朝华汤承福*****-*****-****任敏智***-****-***NaN
1344 |
1345 | 1346 | 1347 | 1348 | 1349 | ```python 1350 | # 数据保存 1351 | manager_info_pd.to_excel(root_path+'业务联系表_pd.xlsx', index=None) 1352 | ``` 1353 | 1354 | ## 2.5 后记 1355 | 1356 | - Python与Excel的自动化内容较多,此篇重在介绍基础,起到抛砖引玉的学习效果。 1357 | - 后面还给大家介绍了 pandas 处理excel的案例,比较简单,大家实际工作、学习中可以按自己需要使用不同框架 1358 | 1359 | 1360 | ```python 1361 | 1362 | ``` 1363 | -------------------------------------------------------------------------------- /Task02-Python与Excel/Python_Excel_OpenPyXL.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/Python_Excel_OpenPyXL.pdf -------------------------------------------------------------------------------- /Task02-Python与Excel/Python_Excel_XLWings.md: -------------------------------------------------------------------------------- 1 | # Task 02 Python Excel 自动化之 XLWings 2 | 3 | 4 | - [Task 02 Python Excel 自动化之 XLWings](#task-02-python-excel-自动化之-xlwings) 5 | - [2.0 模块基本介绍与使用](#20-模块基本介绍与使用) 6 | - [2.1 xlwings模块实战](#21-xlwings模块实战) 7 | - [2.1.1 基础语法一览](#211-基础语法一览) 8 | - [2.1.2 单元格样式设置](#212-单元格样式设置) 9 | - [2.1.3 Excel中生成统计图或者插入图片](#213-excel中生成统计图或者插入图片) 10 | - [2.2 实战练习](#22-实战练习) 11 | - [2.2.1 将消费数据可视化生成带平均线的趋势图,存入excel](#221-将消费数据可视化生成带平均线的趋势图存入excel) 12 | - [2.2.2 将股票数据以指定的格式存储到excel并生成股票走势图](#222-将股票数据以指定的格式存储到excel并生成股票走势图) 13 | 14 | ## 2.0 模块基本介绍与使用 15 | 16 | **xlwings** 17 | 18 | 基本介绍:用于Python与Excel之间的交互,可以轻松地从 Excel 调用 Python,也可以利用Python自动化操作Excel,调用VBA,非常方便。 19 | 20 | 项目地址:https://github.com/xlwings/xlwings 21 | 22 | ![xlwings-principle](./imgs/Python_Excel_XLWings/xlwings-principle.png) 23 | 基本使用方法:新建一个excel文件,取名为xlwings_wb.xlsx,并新建一个sheet,取名为first_sht,在其A1单元格内插入字符串`Datawhale`。 24 | 25 | 打开 CMD/Terminal 进入到自己环境后,执行下面语句安装 xlwings 模块。 26 | ```python 27 | pip3 install xlwings 28 | ``` 29 | 30 | 31 | ```python 32 | root_path = './XLWings_test/' 33 | ``` 34 | 35 | 36 | ```python 37 | # 导入xlwings,并起一个别名 xw,方便操作 38 | import xlwings as xw 39 | 40 | # 1、创建一个app应用,打开Excel程序 41 | # visible=True 表示打开操作Excel过程可见 初次接触可以设置为True,了解其过程 42 | # add_book=False 表示启动app后不用新建个工作簿 43 | app = xw.App(visible=True, add_book=False) 44 | 45 | # 2、新建一个工作簿 46 | wb = app.books.add() 47 | 48 | # 3、新建一个sheet,并操作 49 | # 3.1 新建sheet 起名为first_sht 50 | sht = wb.sheets.add('first_sht') 51 | # 3.2 在新建的sheet表中A1位置插入一个值:Datawhale 52 | sht.range('A1').value = 'Datawhale' 53 | # 3.3 保存新建的工作簿,并起一个名字 54 | wb.save(root_path+'xlwings_wb.xlsx') 55 | 56 | # 4、关闭工作簿 57 | wb.close() 58 | 59 | # 5、程序运行结束,退出Excel程序 60 | app.quit() 61 | ``` 62 | 63 | 通过简单五步,我们就可以完成新建一个excel,并向其中指定sheet中的指定位置输入值了。 64 | ![code-result](./imgs/Python_Excel_XLWings/code-result.png) 65 | 66 | ## 2.1 xlwings模块实战 67 | 68 | ### 2.1.1 基础语法一览 69 | 70 | - 导包 71 | 72 | 73 | ```python 74 | # 基础导入包 75 | import xlwings as xw # 程序第一步 76 | ``` 77 | 78 | - 打开关闭Excel程序(理解成excel软件打开、关闭) 79 | 80 | 81 | ```python 82 | # visible=True 表示打开操作Excel过程可见 初次接触可以设置为True,了解其过程 83 | # add_book=False 表示启动app后不用新建个工作簿 84 | app = xw.App(visible=True, add_book=False) # 程序第二步 85 | 86 | # 关闭excel程序 87 | # app.quit() # 程序最后一步 88 | ``` 89 | 90 | - 工作簿相关操作(理解成excel文件) 91 | 92 | 93 | ```python 94 | # 1、新建一个工作簿 95 | # wb = app.books.add() # 程序第三步 96 | 97 | # 2、保存新建的工作簿,并起一个名字 98 | # 程序倒数第三步,非常关键,保存操作数据结果 99 | # wb.save(root_path+'xlwings_wb.xlsx') 100 | 101 | 102 | # 3、打开一个已经存在的工作簿 103 | wb = app.books.open(root_path+'xlwings_wb.xlsx') # 程序第三步 104 | 105 | # 4、关闭工作簿 106 | # wb.close() # 程序倒数第二步 107 | ``` 108 | 109 | - sheet相关操作(理解成工作表) 110 | 111 | 112 | ```python 113 | # 在工作簿中新建一个sheet,起名为 second_sht 114 | sht1 = wb.sheets.add('second_sht') 115 | print('sht1:', sht1) 116 | 117 | # 选中已经存在的sheet 118 | sht2 = wb.sheets('first_sht') 119 | print('sht2:', sht2) 120 | 121 | # 也可以通过索引选择已存在的sheet 122 | sht3 = wb.sheets[0] # 选中工作簿中的第一个sheet 123 | print('sht3:', sht3) 124 | 125 | # 获取工作簿中工作表的个数 126 | sht_nums = wb.sheets.count 127 | print('工作簿中的sheet个数为:%d'% sht_nums) 128 | 129 | # 当前工作表名字 130 | print('sht1.name:', sht1.name) 131 | 132 | # 获取指定sheet中数据的行数 133 | print('sht1.used_range.last_cell.row:', sht1.used_range.last_cell.row) 134 | 135 | # 获取指定sheet中数据的列数 136 | print('sht1.used_range.last_cell.column:', sht1.used_range.last_cell.column) 137 | 138 | # 删除指定的sheet 比如删除:first_sht 139 | wb.sheets('first_sht').delete() 140 | ``` 141 | 142 | sht1: 143 | sht2: 144 | sht3: 145 | 工作簿中的sheet个数为:3 146 | sht1.name: second_sht 147 | sht1.used_range.last_cell.row: 1 148 | sht1.used_range.last_cell.column: 1 149 | 150 | 151 | - 单元格相关操作(就是excel单元格子) 152 | 153 | 154 | ```python 155 | ''' 156 | 写入 157 | ''' 158 | # 在工作表中指定位置插入数据 159 | sht1.range('B1').value = 'Datawhale' 160 | 161 | # 在工作表指定位置插入多个数据 默认是横向插入 162 | sht1.range('B2').value = ['DATAWHALE', 'FOR', 'THE', 'LEARNER'] 163 | 164 | # 在工作表指定位置竖向插入多个数据 165 | # 设置 options(transpose=True),transpose=True 表示转置的意思 166 | sht1.range('B3').options(transpose=True).value = [1, 2, 3, 4] 167 | 168 | # 在工作表指定位置开始插入多行数据 169 | sht1.range('B7').value = [['a', 'b'], ['c', 'd']] 170 | 171 | # 在工作表指定位置开始插入多列数据 172 | sht1.range('B9').options(transpose=True).value = [['a', 'b'], ['c', 'd']] 173 | 174 | # 向单元格写入公式 175 | sht1.range('F2').formula = '=sum(B2:E2)' 176 | ``` 177 | 178 | 运行结果: 179 | ![xlwings-write](./imgs/Python_Excel_XLWings/xlwings-write.png) 180 | 181 | 182 | ```python 183 | ''' 184 | 读取 185 | ''' 186 | # 在工作表中读取指定位置数据 187 | print('单元格B1=', sht1.range('B1').value) 188 | 189 | # 在工作表中读取指定区域数据 一行 190 | print('单元格B2:F2=', sht1.range('B2:F2').value) 191 | 192 | # 在工作表中读取指定区域数据 一列 193 | print('单元格B3:B6=', sht1.range('B3:B6').value) 194 | 195 | # 在工作表中读取指定区域数据 一个区域 196 | # 设置options(transpose=True)就可以按列读 不设置就是按行读 197 | print('单元格B7:C10=', sht1.range('B7:C10').options(transpose=True).value) 198 | ``` 199 | 200 | 单元格B1= Datawhale 201 | 单元格B2:F2= ['DATAWHALE', 'FOR', 'THE', 'LEARNER', 0.0] 202 | 单元格B3:B6= [1.0, 2.0, 3.0, 4.0] 203 | 单元格B7:C10= [['a', 'c', 'a', 'b'], ['b', 'd', 'c', 'd']] 204 | 205 | 206 | 207 | ```python 208 | ''' 209 | 删除 210 | ''' 211 | # 删除指定单元格中的数据 212 | sht1.range('B10').clear() 213 | 214 | # 删除指定范围内单元格数据 215 | sht1.range('B7:B9').clear() 216 | ``` 217 | 218 | ### 2.1.2 单元格样式设置 219 | 220 | 221 | ```python 222 | ''' 223 | 格式修改 224 | ''' 225 | # 选中已经存在的sheet 226 | sht1 = wb.sheets('second_sht') 227 | # 返回单元格绝对路径 228 | sht1.range('B3').get_address() 229 | # sht1.range('B3').address 230 | 231 | # 合并单元格B3 C3 232 | sht1.range('B3:C3').api.merge() 233 | 234 | # 解除合并单元格B3 C3 235 | # sht1.range('B3:C3').api.unmerge() 236 | 237 | # 向指定单元格添加带超链接文本 238 | # address- 超连接地址 239 | # text_to_display- 超链接文本内容 240 | # screen_tip- 鼠标放到超链接上后显示提示内容 241 | sht1.range('C2').add_hyperlink(address='https://datawhale.club', 242 | text_to_display='DATAWHALE 官网', 243 | screen_tip='点击查看 DATAWHALE 官网 ') 244 | 245 | # 获取指定单元格的超链接地址 246 | sht1.range('C2').hyperlink 247 | 248 | # 自动调试指定单元格高度和宽度 249 | sht1.range('B1').autofit() 250 | 251 | # 设置指定单元格背景颜色 252 | sht1.range('B1').color = (93,199,221) 253 | 254 | # 返回指定范围内的中第一列的编号 数字,如:A-1 B-2 255 | sht1.range('A2:B2').column 256 | 257 | # 获取或者设置行高/列宽 258 | # row_height/column_width会返回行高/列宽 ,范围内行高/列宽不一致会返回None 259 | # 也可以设置一个新的行高/列宽 260 | sht1.range('A2').row_height = 25 261 | sht1.range('B2').column_width = 20 262 | ``` 263 | 264 | 运行结果: 265 | ![xlwings-format](./imgs/Python_Excel_XLWings/xlwings-format.png) 266 | 267 | - 在windows上可以使用以下方法设置单元格文字颜色等格式,如下: 268 | 269 | ```python 270 | # windows系统下字体设置在 sheet.range().api.Font下 271 | # 颜色 272 | sht1.range('A1').api.Font.Color = (255,0,124) 273 | # 字体名字 274 | sht1.range('A1').api.Font.Name = '宋体' 275 | # 字体大小 276 | sht1.range('A1').api.Font.Size = 28 277 | # 是否加粗 278 | sht1.range('A1').api.Font.Bold = True 279 | # 数字格式 280 | sht1.range('A1').api.NumberFormat = '0.0' 281 | # -4108 水平居中 282 | # -4131 靠左 283 | # -4152 靠右 284 | sht1.range('A1').api.HorizontalAlignment = -4108 285 | # -4108 垂直居中(默认) 286 | # -4160 靠上 287 | # -4107 靠下 288 | # -4130 自动换行对齐。 289 | sht1.range('A1').api.VerticalAlignment = -4130 290 | # 设置上边框线风格和粗细 291 | sht1.range('A1').api.Borders(8).LineStyle = 5 292 | sht1.range('A1').api.Borders(8).Weight = 3 293 | ``` 294 | 295 | - 在mac下可以通过以下方法设置字体格式 296 | 297 | ```python 298 | # 在mac下可以通过以下方法设置字体格式 299 | # 设置单元格的字体颜色 300 | rgb_tuple = (0, 10, 200) 301 | sht1.range('B1').api.font_object.color.set(rgb_tuple) 302 | 303 | # 获取指定单元格字体颜色 304 | sht1.range('B1').api.font_object.color.get() 305 | 306 | # 获取指定单元格字体名字 可以使用set方法修改字体 set('宋体') 307 | sht1.range('B1').api.font_object.name.get() 308 | 309 | # 设置指定单元格字体格式 可以用get方法查看单元格字体格式 310 | sht1.range('B3').api.font_object.font_style.set('加粗') 311 | 312 | # 设置指定单元格字体大小 313 | sht1.range('B3').api.font_object.font_size.set(20) 314 | 315 | # 设置边框线粗细 316 | sht1.range('B2').api.get_border(which_border=9).weight.set(4) 317 | 318 | # 设置边框线风格 319 | sht1.range('B2').api.get_border(which_border=9).line_style.set(8) 320 | ``` 321 | 322 | 样式值含义基本说明: 323 | 324 | ![xlwings-border](./imgs/Python_Excel_XLWings/xlwings-border.png) 325 | 326 | ![xlwings-line](./imgs/Python_Excel_XLWings/xlwings-line.png) 327 | 328 | 再次提醒,进行完所有操作后一定要记得执行以下三句: 329 | 330 | 331 | ```python 332 | # 保存新建的工作簿,并起一个名字(如果已存在有名字的excel文件,就直接save即可) 333 | wb.save() 334 | # 关闭工作簿(关闭Excel文件) 335 | wb.close() 336 | # 程序运行结束,退出Excel程序 337 | app.quit() 338 | ``` 339 | 340 | ### 2.1.3 Excel中生成统计图或者插入图片 341 | 342 | - 自动生成统计图 343 | 344 | 345 | ```python 346 | import xlwings as xw 347 | 348 | # 新建一个sheet 349 | app = xw.App(visible=True, add_book=False) 350 | wb = app.books.open(root_path+'xlwings_wb.xlsx') 351 | sht3 = wb.sheets.add('third_sht') 352 | 353 | import pandas as pd 354 | import numpy as np 355 | 356 | # 生成模拟数据 357 | df = pd.DataFrame({ 358 | 'money':np.random.randint(45, 50, size = [1, 20])[0], 359 | }, 360 | index=pd.date_range('2021-02-01', '2021-02-20'), # 行索引和时间相关 361 | ) 362 | df.index.name = '消费日期' # 设置索引名字 363 | 364 | sht3.range('A1').value = df 365 | 366 | # 生成图表 367 | chart1 = sht3.charts.add() # 创建一个图表对象 368 | chart1.set_source_data(sht3.range('A1').expand()) # 加载数据 369 | chart1.chart_type = 'line' # 设置图标类型 370 | chart1.top = sht3.range('D2').top 371 | chart1.left = sht3.range('D2').left # 设置图标开始位置 372 | ``` 373 | 374 | 运行结果: 375 | ![xlwings-charts](./imgs/Python_Excel_XLWings/xlwings-charts.png) 376 | 377 | 除了绘制折线图,我们还可以绘制其他类型的图,修改`chart_type`值即可。 378 | ```python 379 | # 查看其他chart_types值 380 | xw.constants.chart_types 381 | ``` 382 | 383 | 返回结果很长,这里选几个常见的图形列出来: 384 | ``` 385 | '3d_line', # 3D折线图 386 | '3d_pie', # 3D饼图 387 | 'area', # 面积图 388 | 'bar_clustered', # 柱状图相关 389 | 'bubble', # 气泡图 390 | 'column_clustered', # 条形图相关 391 | 'line', # 折线图 392 | 'stock_hlc', # 有意思 股票K线图 393 | ``` 394 | 395 | - 将本地图片或者matplotlib图片保存到excel 396 | 397 | 398 | ```python 399 | ''' 400 | matplotlib 生成的图片存入excel 401 | ''' 402 | import matplotlib.pyplot as plt 403 | # 随便绘制一个饼图 404 | fig1 = plt.figure() # 先创建一个图像对象 405 | plt.pie([0.5, 0.3, 0.2], # 值 406 | labels=['a', 'b', 'c'], # 标签 407 | explode=(0, 0.2, 0), # (爆裂)距离 408 | autopct='%1.1f%%', # 显示百分数格式 409 | shadow=True) # 是否显示阴影 410 | plt.show() 411 | 412 | # 将饼图添加到excel指定位置 J17为图片开始位置 413 | sht3.pictures.add(fig1, name='matplotlib', 414 | left=sht3.range('J17').left, 415 | top=sht3.range('J17').top, update=True) 416 | ``` 417 | 418 | 419 | ![png](./imgs/output_30_0.png) 420 | 421 | > 422 | 423 | 424 | 425 | 运行结果: 426 | ![xlwings-matplotlib](./imgs/Python_Excel_XLWings/xlwings-matplotlib.png) 427 | 428 | 429 | ```python 430 | ''' 431 | 本地图片存入excel 432 | ''' 433 | # 将本地图片添加到excel指定位置 J1为图片开始位置 434 | pic_path = './imgs/logo.png' 435 | sht3.pictures.add(pic_path, name='local', 436 | left=sht3.range('J1').left, 437 | top=sht3.range('J1').top, update=True) 438 | ``` 439 | 440 | 441 | 442 | 443 | > 444 | 445 | 446 | 447 | 运行结果: 448 | ![xlwings-local](./imgs/Python_Excel_XLWings/xlwings-local.png) 449 | 450 | ## 2.2 实战练习 451 | 452 | ### 2.2.1 将消费数据可视化生成带平均线的趋势图,存入excel 453 | 454 | 455 | ```python 456 | ''' 457 | 实战练习一:将消费数据可视化生成带平均线的趋势图,存入excel 458 | 数据就是之前生成的模拟数据 459 | ''' 460 | fig, ax = plt.subplots() # 创建一个画板 461 | # 生成一条新线- 平均消费数据 462 | money_m = [df['money'].mean(axis=0) for i in range(len(df['money']))] 463 | # 建一条线:消费趋势折线图 464 | ax.plot(df.index, df['money'], color='#f16a0b', label='每日消费') 465 | # 再建一条线:平均消费直线图 466 | ax.plot(df.index, money_m, linestyle='--', color='#301502', label='平均消费') 467 | # 设置横纵坐标基础说明 468 | ax.set_xlabel(u'日期') 469 | ax.set_ylabel(u'消费金额/元') 470 | ax.set_title(u'消费折线图') 471 | # 设置x轴值显示方向 472 | plt.setp(ax.get_xticklabels(), rotation=70, horizontalalignment='right') 473 | # 添加图例(label说明到图片上) loc设置显示位置 474 | ax.legend(loc=2) 475 | plt.show() 476 | sht3.pictures.add(fig, name='消费情况', 477 | left=sht3.range('D17').left, 478 | top=sht3.range('D17').top, update=True) 479 | ``` 480 | 481 | 482 | ![png](./imgs/output_36_0.png) 483 | 484 | > 485 | 486 | 487 | 488 | 运行结果: 489 | ![xlwings-practice1](./imgs/Python_Excel_XLWings/xlwings-practice1.png) 490 | 491 | [matplotlib 中文显示参考教程](https://juejin.cn/post/7011473852198977550) 492 | 493 | 494 | ```python 495 | # 保存新建的工作簿,并起一个名字(如果已存在有名字的excel文件,就直接save即可) 496 | wb.save() 497 | # 关闭工作簿(关闭Excel文件) 498 | wb.close() 499 | # 程序运行结束,退出Excel程序 500 | app.quit() 501 | ``` 502 | 503 | ### 2.2.2 将股票数据以指定的格式存储到excel并生成股票走势图 504 | 505 | - 新建一个sheet,然后获取数据 506 | 507 | 508 | ```python 509 | ''' 510 | 实战练习二:将股票数据以指定的格式存储到excel并生成股票走势图 511 | 利用akshare爬取上证指数数据 代号:sh000001 512 | ''' 513 | # 需要提前 pip install akshare 安装 akshare 514 | import akshare as ak 515 | import xlwings as xw 516 | import datetime 517 | 518 | # 新建一个sheet 519 | app = xw.App(visible=True, add_book=False) 520 | wb = app.books.open(root_path+'xlwings_wb.xlsx') 521 | sz_sht = wb.sheets.add('sz_sht') # 第一次新建 522 | # sz_sht = wb.sheets['sz_sht'] # 已经存在,直接打开 523 | 524 | # 获取数据 open high low close volume index-date 525 | # volume 是成交量 多少股 526 | # 0、获取数据 527 | sz_index = ak.stock_zh_index_daily(symbol="sh000001") 528 | sz_index 529 | ``` 530 | 531 | 532 | 533 | 534 |
535 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 |
dateopenhighlowclosevolume
01990-12-1996.05099.98095.79099.980126000
11990-12-20104.300104.39099.980104.39019700
21990-12-21109.070109.130103.730109.1302800
31990-12-24113.570114.550109.130114.5503200
41990-12-25120.090120.250114.550120.2501500
.....................
76862022-06-083245.0173266.6303216.0153263.79343418327000
76872022-06-093259.4903270.5573223.4753238.95442272837200
76882022-06-103214.1853286.6203210.8083284.83443986573000
76892022-06-133256.2753272.9913229.3093255.55143857831200
76902022-06-143224.2143289.1343195.8193288.90745038818700
662 |

7691 rows × 6 columns

663 |
664 | 665 | 666 | 667 | - 处理并存储数据到excel 668 | 669 | 670 | ```python 671 | # 1、处理下数据 672 | sz_index.set_index('date', inplace=True) 673 | sz_data = sz_index[datetime.date(2022,1,1):datetime.date(2022,6,14)].iloc[:,0:4] # 只取今年数据 ohlc 674 | sz_data.index = sz_data.index.to_series().apply(lambda x : x.strftime('%m-%d')) # 将索引日期格式改为 月-日 675 | 676 | # 2、存储数据 677 | sz_sht.range('A1').value = sz_data 678 | ``` 679 | 680 | - 处理表格中数据格式 681 | 682 | 683 | ```python 684 | # 3、处理数据格式 685 | # - 表头字体 格式加粗 背景颜色 686 | sz_sht.range('A1:E1').api.font_object.name.set('Calibri') 687 | sz_sht.range('A1:E1').api.font_object.font_style.set('加粗') 688 | sz_sht.range('A1:E1').api.font_object.color.set((255, 255, 255)) 689 | # 背景颜色 690 | sz_sht.range('A1:E1').color = (16, 156, 245) 691 | # mac上居中未实现,有了解的小伙伴可以留言指出下,感谢 692 | # sz_sht.range('B1').api.font_object.horizontalalignment = xw.constants.Constants.xlCenter 693 | 694 | # - 添加边框 695 | # 1 左+内部竖线 696 | sz_sht.range('A1:E177').api.get_border(which_border=1).weight.set(2) 697 | # 10 最右侧竖线 698 | sz_sht.range('A1:E177').api.get_border(which_border=10).weight.set(2) 699 | # 3 上+内部横线 700 | sz_sht.range('A1:E177').api.get_border(which_border=3).weight.set(2) 701 | # 9 最下面横线 702 | sz_sht.range('A1:E177').api.get_border(which_border=9).weight.set(2) 703 | ``` 704 | 705 | - 生成ohlc k线图并存储到excel指定位置 706 | 707 | 708 | ```python 709 | # 4、生成图表 710 | chart_ohlc = sz_sht.charts.add() # 创建一个图表对象 711 | chart_ohlc.set_source_data(sz_sht.range('A1').expand()) # 加载数据 712 | chart_ohlc.chart_type = 'stock_ohlc' # 设置图标类型 713 | chart_ohlc.top = sz_sht.range('G2').top 714 | chart_ohlc.left = sz_sht.range('G2').left # 设置图标开始位置 715 | ``` 716 | 717 | 运行结果: 718 | ![xlwings-practice2](./imgs/Python_Excel_XLWings/xlwings-practice2.png) 719 | 720 | 721 | ```python 722 | # 保存新建的工作簿,并起一个名字(如果已存在有名字的excel文件,就直接save即可) 723 | wb.save() 724 | # 关闭工作簿(关闭Excel文件) 725 | wb.close() 726 | # 程序运行结束,退出Excel程序 727 | app.quit() 728 | ``` 729 | -------------------------------------------------------------------------------- /Task02-Python与Excel/Python_Excel_XLWings.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/Python_Excel_XLWings.pdf -------------------------------------------------------------------------------- /Task02-Python与Excel/XLWings_test/xlwings_wb.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/XLWings_test/xlwings_wb.xlsx -------------------------------------------------------------------------------- /Task02-Python与Excel/imgs/2.3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/imgs/2.3.png -------------------------------------------------------------------------------- /Task02-Python与Excel/imgs/Python_Excel_XLWings/code-result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/imgs/Python_Excel_XLWings/code-result.png -------------------------------------------------------------------------------- /Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-border.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-border.png -------------------------------------------------------------------------------- /Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-charts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-charts.png -------------------------------------------------------------------------------- /Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-format.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-format.png -------------------------------------------------------------------------------- /Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-line.png -------------------------------------------------------------------------------- /Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-local.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-local.png -------------------------------------------------------------------------------- /Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-matplotlib.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-matplotlib.png -------------------------------------------------------------------------------- /Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-practice1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-practice1.png -------------------------------------------------------------------------------- /Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-practice2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-practice2.png -------------------------------------------------------------------------------- /Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-principle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-principle.png -------------------------------------------------------------------------------- /Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-write.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/imgs/Python_Excel_XLWings/xlwings-write.png -------------------------------------------------------------------------------- /Task02-Python与Excel/imgs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/imgs/logo.png -------------------------------------------------------------------------------- /Task02-Python与Excel/imgs/output_30_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/imgs/output_30_0.png -------------------------------------------------------------------------------- /Task02-Python与Excel/imgs/output_36_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task02-Python与Excel/imgs/output_36_0.png -------------------------------------------------------------------------------- /Task03-Python与Word和PDF/excel到word.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task03-Python与Word和PDF/excel到word.xlsx -------------------------------------------------------------------------------- /Task03-Python与Word和PDF/python与pdf.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "### **批量拆分**" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": {}, 14 | "outputs": [ 15 | { 16 | "name": "stdout", 17 | "output_type": "stream", 18 | "text": [ 19 | "C:\\Users\\yuhongfei\\word-pdf\\易方达中小盘混合型证券投资基金2020年中期报告【拆分】\\易方达中小盘混合型证券投资基金2020年中期报告.pdf1.pdf\n", 20 | "C:\\Users\\yuhongfei\\word-pdf\\易方达中小盘混合型证券投资基金2020年中期报告【拆分】\\易方达中小盘混合型证券投资基金2020年中期报告.pdf2.pdf\n", 21 | "C:\\Users\\yuhongfei\\word-pdf\\易方达中小盘混合型证券投资基金2020年中期报告【拆分】\\易方达中小盘混合型证券投资基金2020年中期报告.pdf3.pdf\n", 22 | "C:\\Users\\yuhongfei\\word-pdf\\易方达中小盘混合型证券投资基金2020年中期报告【拆分】\\易方达中小盘混合型证券投资基金2020年中期报告.pdf4.pdf\n", 23 | "C:\\Users\\yuhongfei\\word-pdf\\易方达中小盘混合型证券投资基金2020年中期报告【拆分】\\易方达中小盘混合型证券投资基金2020年中期报告.pdf5.pdf\n", 24 | "C:\\Users\\yuhongfei\\word-pdf\\易方达中小盘混合型证券投资基金2020年中期报告【拆分】\\易方达中小盘混合型证券投资基金2020年中期报告.pdf6.pdf\n", 25 | "C:\\Users\\yuhongfei\\word-pdf\\易方达中小盘混合型证券投资基金2020年中期报告【拆分】\\易方达中小盘混合型证券投资基金2020年中期报告.pdf7.pdf\n", 26 | "C:\\Users\\yuhongfei\\word-pdf\\易方达中小盘混合型证券投资基金2020年中期报告【拆分】\\易方达中小盘混合型证券投资基金2020年中期报告.pdf8.pdf\n", 27 | "C:\\Users\\yuhongfei\\word-pdf\\易方达中小盘混合型证券投资基金2020年中期报告【拆分】\\易方达中小盘混合型证券投资基金2020年中期报告.pdf9.pdf\n", 28 | "C:\\Users\\yuhongfei\\word-pdf\\易方达中小盘混合型证券投资基金2020年中期报告【拆分】\\易方达中小盘混合型证券投资基金2020年中期报告.pdf10.pdf\n", 29 | "文件已成功拆分,保存路径为:C:\\Users\\yuhongfei\\word-pdf\\易方达中小盘混合型证券投资基金2020年中期报告【拆分】\n" 30 | ] 31 | } 32 | ], 33 | "source": [ 34 | "import os\n", 35 | "from PyPDF2 import PdfFileWriter, PdfFileReader\n", 36 | "\n", 37 | "def split_pdf(filename, filepath, save_dirpath, step=5):\n", 38 | " \"\"\"\n", 39 | " 拆分PDF为多个小的PDF文件,\n", 40 | " @param filename:文件名\n", 41 | " @param filepath:文件路径\n", 42 | " @param save_dirpath:保存小的PDF的文件路径\n", 43 | " @param step: 每step间隔的页面生成一个文件,例如step=5,表示0-4页、5-9页...为一个文件\n", 44 | " @return:\n", 45 | " \"\"\"\n", 46 | " if not os.path.exists(save_dirpath):\n", 47 | " os.mkdir(save_dirpath)\n", 48 | " pdf_reader = PdfFileReader(filepath)\n", 49 | " # 读取每一页的数据\n", 50 | " pages = pdf_reader.getNumPages()\n", 51 | " for page in range(0, pages, step):\n", 52 | " pdf_writer = PdfFileWriter()\n", 53 | " # 拆分pdf,每 step 页的拆分为一个文件\n", 54 | " for index in range(page, page+step):\n", 55 | " if index < pages:\n", 56 | " pdf_writer.addPage(pdf_reader.getPage(index))\n", 57 | " # 保存拆分后的小文件\n", 58 | " save_path = os.path.join(save_dirpath, filename+str(int(page/step)+1)+'.pdf')\n", 59 | " print(save_path)\n", 60 | " with open(save_path, \"wb\") as out:\n", 61 | " pdf_writer.write(out)\n", 62 | "\n", 63 | " print(\"文件已成功拆分,保存路径为:\"+save_dirpath)\n", 64 | " \n", 65 | "filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf'\n", 66 | "filepath = os.path.join(os.getcwd(), filename)\n", 67 | "save_dirpath = os.path.join(os.getcwd(), '易方达中小盘混合型证券投资基金2020年中期报告【拆分】')\n", 68 | "split_pdf(filename, filepath, save_dirpath, step=5)" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "### **批量合并**" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": null, 81 | "metadata": {}, 82 | "outputs": [], 83 | "source": [ 84 | "import os\n", 85 | "from PyPDF2 import PdfFileReader, PdfFileWriter\n", 86 | "\n", 87 | "def concat_pdf(filename, read_dirpath, save_filepath):\n", 88 | " \"\"\"\n", 89 | " 合并多个PDF文件\n", 90 | " @param filename:文件名\n", 91 | " @param read_dirpath:要合并的PDF目录\n", 92 | " @param save_filepath:合并后的PDF文件路径\n", 93 | " @return:\n", 94 | " \"\"\"\n", 95 | " pdf_writer = PdfFileWriter()\n", 96 | " # 对文件名进行排序\n", 97 | " list_filename = os.listdir(read_dirpath)\n", 98 | " list_filename.sort(key=lambda x: int(x[:-4].replace(filename, \"\")))\n", 99 | " for filename in list_filename:\n", 100 | " print(filename)\n", 101 | " filepath = os.path.join(read_dirpath, filename)\n", 102 | " # 读取文件并获取文件的页数\n", 103 | " pdf_reader = PdfFileReader(filepath)\n", 104 | " pages = pdf_reader.getNumPages()\n", 105 | " # 逐页添加\n", 106 | " for page in range(pages):\n", 107 | " pdf_writer.addPage(pdf_reader.getPage(page))\n", 108 | " # 保存合并后的文件\n", 109 | " with open(save_filepath, \"wb\") as out:\n", 110 | " pdf_writer.write(out)\n", 111 | " print(\"文件已成功合并,保存路径为:\"+save_filepath)\n", 112 | "\n", 113 | "filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf'\n", 114 | "read_dirpath = os.path.join(os.getcwd(), '易方达中小盘混合型证券投资基金2020年中期报告【拆分】')\n", 115 | "save_filepath = os.path.join(os.getcwd(), '易方达中小盘混合型证券投资基金2020年中期报告-合并后.pdf')\n", 116 | "concat_pdf(filename, read_dirpath, save_filepath)" 117 | ] 118 | }, 119 | { 120 | "cell_type": "markdown", 121 | "metadata": {}, 122 | "source": [ 123 | "### **提取文字内容**" 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": null, 129 | "metadata": {}, 130 | "outputs": [], 131 | "source": [ 132 | "import os\n", 133 | "import pdfplumber\n", 134 | "\n", 135 | "def extract_text_info(filepath):\n", 136 | " \"\"\"\n", 137 | " 提取PDF中的文字\n", 138 | " @param filepath:文件路径\n", 139 | " @return:\n", 140 | " \"\"\"\n", 141 | " with pdfplumber.open(filepath) as pdf:\n", 142 | " # 获取第2页数据\n", 143 | " page = pdf.pages[1]\n", 144 | " print(page.extract_text())\n", 145 | " \n", 146 | "filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf'\n", 147 | "filepath = os.path.join(os.getcwd(), filename)\n", 148 | "# 提取文字内容\n", 149 | "extract_text_info(filepath)" 150 | ] 151 | }, 152 | { 153 | "cell_type": "markdown", 154 | "metadata": {}, 155 | "source": [ 156 | "### **提取表格内容**" 157 | ] 158 | }, 159 | { 160 | "cell_type": "code", 161 | "execution_count": null, 162 | "metadata": {}, 163 | "outputs": [], 164 | "source": [ 165 | "import os\n", 166 | "import pandas as pd\n", 167 | "import pdfplumber\n", 168 | "\n", 169 | "def extract_table_info(filepath):\n", 170 | " \"\"\"\n", 171 | " 提取PDF中的图表数据\n", 172 | " @param filepath:\n", 173 | " @return:\n", 174 | " \"\"\"\n", 175 | " with pdfplumber.open(filepath) as pdf:\n", 176 | " # 获取第18页数据\n", 177 | " page = pdf.pages[17]\n", 178 | " # 如果一页有一个表格,设置表格的第一行为表头,其余为数据\n", 179 | " table_info = page.extract_table()\n", 180 | " df_table = pd.DataFrame(table_info[1:], columns=table_info[0])\n", 181 | " df_table.to_csv('dmeo.csv', index=False, encoding='gbk')\n", 182 | " \n", 183 | "filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf'\n", 184 | "filepath = os.path.join(os.getcwd(), filename)\n", 185 | "# 提取表格内容\n", 186 | "extract_table_info(filepath)" 187 | ] 188 | }, 189 | { 190 | "cell_type": "code", 191 | "execution_count": null, 192 | "metadata": {}, 193 | "outputs": [], 194 | "source": [ 195 | "import os\n", 196 | "import pandas as pd\n", 197 | "import pdfplumber\n", 198 | "\n", 199 | "def extract_table_info(filepath):\n", 200 | " \"\"\"\n", 201 | " 提取PDF中的图表数据\n", 202 | " @param filepath:\n", 203 | " @return:\n", 204 | " \"\"\"\n", 205 | " with pdfplumber.open(filepath) as pdf:\n", 206 | " # 获取第7页数据\n", 207 | " page = pdf.pages[6]\n", 208 | " # 如果一页有多个表格,对应的数据是一个三维数组\n", 209 | " tables_info = page.extract_tables()\n", 210 | " for index in range(len(tables_info)):\n", 211 | " # 设置表格的第一行为表头,其余为数据\n", 212 | " df_table = pd.DataFrame(tables_info[index][1:], columns=tables_info[index][0])\n", 213 | " df_table.to_csv('dmeo.csv', index=False, encoding='gbk')\n", 214 | " \n", 215 | "filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf'\n", 216 | "filepath = os.path.join(os.getcwd(), filename)\n", 217 | "# 提取表格内容\n", 218 | "extract_table_info(filepath)" 219 | ] 220 | }, 221 | { 222 | "cell_type": "markdown", 223 | "metadata": {}, 224 | "source": [ 225 | "### **提取pdf中的图片**" 226 | ] 227 | }, 228 | { 229 | "cell_type": "code", 230 | "execution_count": 2, 231 | "metadata": {}, 232 | "outputs": [ 233 | { 234 | "name": "stdout", 235 | "output_type": "stream", 236 | "text": [ 237 | "文件名:C:\\Users\\yuhongfei\\word-pdf\\易方达中小盘混合型证券投资基金2020年中期报告.pdf, 页数: 46, 对象: 15284\n" 238 | ] 239 | }, 240 | { 241 | "name": "stderr", 242 | "output_type": "stream", 243 | "text": [ 244 | "Deprecation: 'writePNG' removed from class 'Pixmap' after v1.19 - use 'save'.\n" 245 | ] 246 | } 247 | ], 248 | "source": [ 249 | "import os\n", 250 | "import re\n", 251 | "import fitz\n", 252 | "\n", 253 | "def extract_pic_info(filepath, pic_dirpath):\n", 254 | " \"\"\"\n", 255 | " 提取PDF中的图片\n", 256 | " @param filepath:pdf文件路径\n", 257 | " @param pic_dirpath:要保存的图片目录路径\n", 258 | " @return:\n", 259 | " \"\"\"\n", 260 | " if not os.path.exists(pic_dirpath):\n", 261 | " os.makedirs(pic_dirpath)\n", 262 | " # 使用正则表达式来查找图片\n", 263 | " check_XObject = r\"/Type(?= */XObject)\"\n", 264 | " check_Image = r\"/Subtype(?= */Image)\"\n", 265 | " img_count = 0\n", 266 | "\n", 267 | " \"\"\"1. 打开pdf,打印相关信息\"\"\"\n", 268 | " pdf_info = fitz.open(filepath)\n", 269 | " # 1.16.8版本用法 xref_len = doc._getXrefLength()\n", 270 | " # 最新版本\n", 271 | " xref_len = pdf_info.xref_length()\n", 272 | " # 打印PDF的信息\n", 273 | " print(\"文件名:{}, 页数: {}, 对象: {}\".format(filepath, len(pdf_info), xref_len-1))\n", 274 | "\n", 275 | " \"\"\"2. 遍历PDF中的对象,遇到是图像才进行下一步,不然就continue\"\"\"\n", 276 | " for index in range(1, xref_len):\n", 277 | " # 1.16.8版本用法 text = doc._getXrefString(index)\n", 278 | " # 最新版本\n", 279 | " text = pdf_info.xref_object(index)\n", 280 | "\n", 281 | " is_XObject = re.search(check_XObject, text)\n", 282 | " is_Image = re.search(check_Image, text)\n", 283 | " # 如果不是对象也不是图片,则不操作\n", 284 | " if is_XObject or is_Image:\n", 285 | " img_count += 1\n", 286 | " # 根据索引生成图像\n", 287 | " pix = fitz.Pixmap(pdf_info, index)\n", 288 | " pic_filepath = os.path.join(pic_dirpath, 'img_' + str(img_count) + '.png')\n", 289 | " \"\"\"pix.size 可以反映像素多少,简单的色素块该值较低,可以通过设置一个阈值过滤。以阈值 10000 为例过滤\"\"\"\n", 290 | " # if pix.size < 10000:\n", 291 | " # continue\n", 292 | "\n", 293 | " \"\"\"三、 将图像存为png格式\"\"\"\n", 294 | " if pix.n >= 5:\n", 295 | " # 先转换CMYK\n", 296 | " pix = fitz.Pixmap(fitz.csRGB, pix)\n", 297 | " # 存为PNG\n", 298 | " pix.writePNG(pic_filepath)\n", 299 | " \n", 300 | "filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf'\n", 301 | "filepath = os.path.join(os.getcwd(), filename)\n", 302 | "pic_dirpath = os.path.join(os.getcwd(), '易方达中小盘混合型证券投资基金2020年中期报告【文中图片】')\n", 303 | "# 提取图片内容\n", 304 | "extract_pic_info(filepath, pic_dirpath)" 305 | ] 306 | }, 307 | { 308 | "cell_type": "markdown", 309 | "metadata": {}, 310 | "source": [ 311 | "### **转换为图片**" 312 | ] 313 | }, 314 | { 315 | "cell_type": "code", 316 | "execution_count": null, 317 | "metadata": {}, 318 | "outputs": [], 319 | "source": [ 320 | "import os\n", 321 | "from pdf2image import convert_from_path, convert_from_bytes\n", 322 | "\n", 323 | "def convert_to_pic(filepath, pic_dirpath):\n", 324 | " \"\"\"\n", 325 | " 每一页的PDF转换成图片\n", 326 | " @param filepath:pdf文件路径\n", 327 | " @param pic_dirpath:图片目录路径\n", 328 | " @return:\n", 329 | " \"\"\"\n", 330 | " print(filepath)\n", 331 | " if not os.path.exists(pic_dirpath):\n", 332 | " os.makedirs(pic_dirpath)\n", 333 | "\n", 334 | " images = convert_from_bytes(open(filepath, 'rb').read())\n", 335 | " # images = convert_from_path(filepath, dpi=200)\n", 336 | " for image in images:\n", 337 | " # 保存图片\n", 338 | " pic_filepath = os.path.join(pic_dirpath, 'img_'+str(images.index(image))+'.png')\n", 339 | " image.save(pic_filepath, 'PNG')\n", 340 | " \n", 341 | "# PDF转换为图片\n", 342 | "convert_to_pic(filepath, pic_dirpath)\n", 343 | "\n", 344 | "\n", 345 | "filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf'\n", 346 | "filepath = os.path.join(os.getcwd(), filename)\n", 347 | "pic_dirpath = os.path.join(os.getcwd(), '易方达中小盘混合型证券投资基金2020年中期报告【转换为图片】')\n", 348 | "# PDF转换为图片\n", 349 | "convert_to_pic(filepath, pic_dirpath)" 350 | ] 351 | }, 352 | { 353 | "cell_type": "markdown", 354 | "metadata": {}, 355 | "source": [ 356 | "### **添加水印**" 357 | ] 358 | }, 359 | { 360 | "cell_type": "code", 361 | "execution_count": null, 362 | "metadata": {}, 363 | "outputs": [], 364 | "source": [ 365 | "import os\n", 366 | "from copy import copy\n", 367 | "from PyPDF2 import PdfFileReader, PdfFileWriter\n", 368 | "\n", 369 | "def add_watermark(filepath, save_filepath, watermark_filepath):\n", 370 | " \"\"\"\n", 371 | " 添加水印\n", 372 | " @param filepath:PDF文件路径\n", 373 | " @param save_filepath:最终的文件保存路径\n", 374 | " @param watermark_filepath:水印PDF文件路径\n", 375 | " @return:\n", 376 | " \"\"\"\n", 377 | " \"\"\"读取PDF水印文件\"\"\"\n", 378 | " # 可以先生成一个空白A4大小的png图片,通过 https://mp.weixin.qq.com/s/_oJA6lbsdMlRRsBf6DPxsg 教程的方式给图片加水印,将图片插入到word中并最终生成一个水印PDF文档\n", 379 | " watermark = PdfFileReader(watermark_filepath)\n", 380 | " watermark_page = watermark.getPage(0)\n", 381 | "\n", 382 | " pdf_reader = PdfFileReader(filepath)\n", 383 | " pdf_writer = PdfFileWriter()\n", 384 | "\n", 385 | " for page_index in range(pdf_reader.getNumPages()):\n", 386 | " current_page = pdf_reader.getPage(page_index)\n", 387 | " # 封面页不添加水印\n", 388 | " if page_index == 0:\n", 389 | " new_page = current_page\n", 390 | " else:\n", 391 | " new_page = copy(watermark_page)\n", 392 | " new_page.mergePage(current_page)\n", 393 | " pdf_writer.addPage(new_page)\n", 394 | " # 保存水印后的文件\n", 395 | " with open(save_filepath, \"wb\") as out:\n", 396 | " pdf_writer.write(out)\n", 397 | "\n", 398 | "filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf'\n", 399 | "filepath = os.path.join(os.getcwd(), filename)\n", 400 | "save_filepath = os.path.join(os.getcwd(), '易方达中小盘混合型证券投资基金2020年中期报告-水印.pdf')\n", 401 | "watermark_filepath = os.path.join(os.getcwd(), 'watermark.pdf')\n", 402 | "# 添加水印\n", 403 | "add_watermark(filepath, save_filepath, watermark_filepath)" 404 | ] 405 | }, 406 | { 407 | "cell_type": "markdown", 408 | "metadata": {}, 409 | "source": [ 410 | "### **文档加密与解密**" 411 | ] 412 | }, 413 | { 414 | "cell_type": "code", 415 | "execution_count": null, 416 | "metadata": {}, 417 | "outputs": [], 418 | "source": [ 419 | "import os\n", 420 | "from PyPDF2 import PdfFileReader, PdfFileWriter\n", 421 | "\n", 422 | "def encrypt_pdf(filepath, save_filepath, passwd='xiaoyi'):\n", 423 | " \"\"\"\n", 424 | " PDF文档加密\n", 425 | " @param filepath:PDF文件路径\n", 426 | " @param save_filepath:加密后的文件保存路径\n", 427 | " @param passwd:密码\n", 428 | " @return:\n", 429 | " \"\"\"\n", 430 | " pdf_reader = PdfFileReader(filepath)\n", 431 | " pdf_writer = PdfFileWriter()\n", 432 | "\n", 433 | " for page_index in range(pdf_reader.getNumPages()):\n", 434 | " pdf_writer.addPage(pdf_reader.getPage(page_index))\n", 435 | "\n", 436 | " # 添加密码\n", 437 | " pdf_writer.encrypt(passwd)\n", 438 | " with open(save_filepath, \"wb\") as out:\n", 439 | " pdf_writer.write(out)\n", 440 | "\n", 441 | "filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf'\n", 442 | "filepath = os.path.join(os.getcwd(), filename)\n", 443 | "save_filepath = os.path.join(os.getcwd(), '易方达中小盘混合型证券投资基金2020年中期报告-加密后.pdf')\n", 444 | "# 文档加密\n", 445 | "encrypt_pdf(filepath, save_filepath, passwd='xiaoyi')" 446 | ] 447 | }, 448 | { 449 | "cell_type": "code", 450 | "execution_count": null, 451 | "metadata": {}, 452 | "outputs": [], 453 | "source": [ 454 | "def decrypt_pdf(filepath, save_filepath, passwd='xiaoyi'):\n", 455 | " \"\"\"\n", 456 | " 解密 PDF 文档并且保存为未加密的 PDF\n", 457 | " @param filepath:PDF文件路径\n", 458 | " @param save_filepath:解密后的文件保存路径\n", 459 | " @param passwd:密码\n", 460 | " @return:\n", 461 | " \"\"\"\n", 462 | " pdf_reader = PdfFileReader(filepath)\n", 463 | " # PDF文档解密\n", 464 | " pdf_reader.decrypt('xiaoyi')\n", 465 | "\n", 466 | " pdf_writer = PdfFileWriter()\n", 467 | " for page_index in range(pdf_reader.getNumPages()):\n", 468 | " pdf_writer.addPage(pdf_reader.getPage(page_index))\n", 469 | "\n", 470 | " with open(save_filepath, \"wb\") as out:\n", 471 | " pdf_writer.write(out)\n", 472 | "\n", 473 | "filename = '易方达中小盘混合型证券投资基金2020年中期报告-加密后.pdf'\n", 474 | "filepath = os.path.join(os.getcwd(), filename)\n", 475 | "save_filepath = os.path.join(os.getcwd(), '易方达中小盘混合型证券投资基金2020年中期报告-解密后.pdf')\n", 476 | "# 文档解密\n", 477 | "decrypt_pdf(filepath, save_filepath, passwd='xiaoyi')" 478 | ] 479 | }, 480 | { 481 | "cell_type": "markdown", 482 | "metadata": {}, 483 | "source": [ 484 | "### **页面旋转**" 485 | ] 486 | }, 487 | { 488 | "cell_type": "code", 489 | "execution_count": 6, 490 | "metadata": {}, 491 | "outputs": [], 492 | "source": [ 493 | "import PyPDF2\n", 494 | "\n", 495 | "filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf'\n", 496 | "filepath = os.path.join(os.getcwd(), filename)\n", 497 | "save_filepath = os.path.join(os.getcwd(), '易方达中小盘混合型证券投资基金2020年中期报告-旋转.pdf')\n", 498 | "pdf_reader = PdfFileReader(filepath)\n", 499 | "page = pdf_reader.getPage(0)\n", 500 | "page.rotateClockwise(90)\n", 501 | "pdf_writer = PdfFileWriter()\n", 502 | "pdf_writer.addPage(page)\n", 503 | "with open(save_filepath, \"wb\") as out:\n", 504 | " pdf_writer.write(out)" 505 | ] 506 | } 507 | ], 508 | "metadata": { 509 | "kernelspec": { 510 | "display_name": "Python 3", 511 | "language": "python", 512 | "name": "python3" 513 | }, 514 | "language_info": { 515 | "codemirror_mode": { 516 | "name": "ipython", 517 | "version": 3 518 | }, 519 | "file_extension": ".py", 520 | "mimetype": "text/x-python", 521 | "name": "python", 522 | "nbconvert_exporter": "python", 523 | "pygments_lexer": "ipython3", 524 | "version": "3.7.4" 525 | } 526 | }, 527 | "nbformat": 4, 528 | "nbformat_minor": 2 529 | } 530 | -------------------------------------------------------------------------------- /Task03-Python与Word和PDF/python与pdf.md: -------------------------------------------------------------------------------- 1 | 2 | # Task 04 Python办公自动化--PDF篇 3 | 4 | PDF 操作是的知识点,初级的 PDF 自动化包括 PDF 文档的拆分、合并、提取等操作,更高级的还包括 WORD与PDF互转等 5 | 6 | 初级操作一般比较常用,也可以解决较多的办公内容,所以本节将会主要介绍 PDF 的初级操作,具体内容将会从以下几个小节展开: 7 | 8 | - [Task 04 Python办公自动化--PDF篇](#task-04-python办公自动化--pdf篇) 9 | - [4.1. 相关介绍](#41-相关介绍) 10 | - [4.2. 批量拆分](#42-批量拆分) 11 | - [4.3. 批量合并](#43-批量合并) 12 | - [4.4. 提取文字内容](#44-提取文字内容) 13 | - [4.5. 提取表格内容](#45-提取表格内容) 14 | - [4.6 提取图片内容](#46-提取图片内容) 15 | - [4.7 转换为图片](#47-转换为图片) 16 | - [4.7.1 安装 pdf2image](#471-安装-pdf2image) 17 | - [4.7.2 安装组件](#472-安装组件) 18 | - [4.8. 添加水印](#48-添加水印) 19 | - [4.9. 文档加密与解密](#49-文档加密与解密) 20 | 21 | 下面直接开始本节内容。 22 | 23 | 24 | 25 | ## 4.1. 相关介绍 26 | 27 | Python 操作 PDF 会用到两个库,分别是:PyPDF2 和 pdfplumber 28 | 29 | 其中 **PyPDF2** 可以更好的读取、写入、分割、合并PDF文件,而 **pdfplumber** 可以更好的读取 PDF 文件中内容和提取 PDF 中的表格 30 | 31 | 对应的官网分别是: 32 | 33 | > PyPDF2:https://pythonhosted.org/PyPDF2/ 34 | > 35 | > pdfplumber:https://github.com/jsvine/pdfplumber 36 | 37 | 由于这两个库都不是 Python 的标准库,所以在使用之前都需要单独安装 38 | 39 | win+r 后输入 cmd 打开 command 窗口,依次输入如下命令进行安装: 40 | 41 | > pip install PyPDF2 42 | > 43 | > pip install pdfplumber 44 | 45 | 安装完成后显示 success 则表示安装成功 46 | 47 | ![](./图片/4.11.png) 48 | 49 | 另外,在下文中需要用到两个文件:一个是本次教程的处理目标PDF、一个是在添加水印章节需要用到的水印PDF文件 50 | 51 | 其中第一个PDF大家可以换成自己想要处理的目标PDF,在下文的代码中修改相应的名称即可;第二个PDF大家可以根据文中给出的教程自行生成水印PDF文件(嫌麻烦的同学可以直接用我提供的水印PDF即可) 52 | 53 | 上述两个文件的下载链接如下:https://pan.baidu.com/s/10OpbHzyQBTWhJ9Rz7t1_BQ 提取码:1025 54 | 55 | > 另外,需要注意的是,第一个PDF和第二个PDF直接放在运行代码的同级目录下即可,运行的时候无需创建文件夹,大家注意下! 56 | 57 | 58 | 59 | ## 4.2. 批量拆分 60 | 61 | 将一个完整的 PDF 拆分成几个小的 PDF,因为主要涉及到 PDF 整体的操作,所以本小节需要用到 PyPDF2 这个库 62 | 63 | 拆分的大概思路如下: 64 | 65 | - 读取 PDF 的整体信息、总页数等 66 | - 遍历每一页内容,以每个 step 为间隔将 PDF 存成每一个小的文件块 67 | - 将小的文件块重新保存为新的 PDF 文件 68 | 69 | 需要注意的是,在拆分的过程中,可以手动设置间隔,例如:每5页保存成一个小的 PDF 文件 70 | 71 | 拆分的代码如下: 72 | 73 | ```python 74 | import os 75 | from PyPDF2 import PdfFileWriter, PdfFileReader 76 | 77 | def split_pdf(filename, filepath, save_dirpath, step=5): 78 | """ 79 | 拆分PDF为多个小的PDF文件, 80 | @param filename:文件名 81 | @param filepath:文件路径 82 | @param save_dirpath:保存小的PDF的文件路径 83 | @param step: 每step间隔的页面生成一个文件,例如step=5,表示0-4页、5-9页...为一个文件 84 | @return: 85 | """ 86 | if not os.path.exists(save_dirpath): 87 | os.mkdir(save_dirpath) 88 | pdf_reader = PdfFileReader(filepath) 89 | # 读取每一页的数据 90 | pages = pdf_reader.getNumPages() 91 | for page in range(0, pages, step): 92 | pdf_writer = PdfFileWriter() 93 | # 拆分pdf,每 step 页的拆分为一个文件 94 | for index in range(page, page+step): 95 | if index < pages: 96 | pdf_writer.addPage(pdf_reader.getPage(index)) 97 | # 保存拆分后的小文件 98 | save_path = os.path.join(save_dirpath, filename+str(int(page/step)+1)+'.pdf') 99 | print(save_path) 100 | with open(save_path, "wb") as out: 101 | pdf_writer.write(out) 102 | 103 | print("文件已成功拆分,保存路径为:"+save_dirpath) 104 | 105 | filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf' 106 | filepath = os.path.join(os.getcwd(), filename) 107 | save_dirpath = os.path.join(os.getcwd(), '易方达中小盘混合型证券投资基金2020年中期报告【拆分】') 108 | split_pdf(filename, filepath, save_dirpath, step=5) 109 | 110 | ``` 111 | 112 | 以“易方达中小盘混合型证券投资基金2020年中期报告”为例,整个 PDF 文件一共 46 页,每5页为间隔,最终生成了10个小的 PDF 文件 113 | 114 | ![](./图片/4.1.png) 115 | 116 | 117 | 118 | **需要注意的是:** 119 | 120 | 如果你是第一次运行代码,在运行过程中报如下的错误,可以通过以下方法解决: 121 | 122 | ![](./图片/4.2.png) 123 | 124 | 如果是在 Pycharm 下,直接通过报错信息,点击 utils.py 文件,定位到第 238 行原文 125 | 126 | 原文中是这样的: 127 | 128 | ```python 129 | r = s.encode('latin-1') 130 | if len(s) < 2: 131 | bc[s] = r 132 | return r 133 | ``` 134 | 135 | 修改为: 136 | 137 | ```python 138 | try: 139 | r = s.encode('latin-1') 140 | if len(s) < 2: 141 | bc[s] = r 142 | return r 143 | except Exception as e: 144 | r = s.encode('utf-8') 145 | if len(s) < 2: 146 | bc[s] = r 147 | return r 148 | ``` 149 | 150 | 如果你使用的是 **anaconda**,对应的文件路径应该为:anaconda\Lib\site-packages\PyPDF2\utils.py,进行同样的修改操作即可 151 | 152 | 153 | 154 | ## 4.3. 批量合并 155 | 156 | 比起拆分来,合并的思路更加简单: 157 | 158 | - 确定要合并的 **文件顺序** 159 | - 循环追加到一个文件块中 160 | - 保存成一个新的文件 161 | 162 | 对应的代码比较简单,基本不会出现问题: 163 | 164 | ```python 165 | import os 166 | from PyPDF2 import PdfFileReader, PdfFileWriter 167 | 168 | def concat_pdf(filename, read_dirpath, save_filepath): 169 | """ 170 | 合并多个PDF文件 171 | @param filename:文件名 172 | @param read_dirpath:要合并的PDF目录 173 | @param save_filepath:合并后的PDF文件路径 174 | @return: 175 | """ 176 | pdf_writer = PdfFileWriter() 177 | # 对文件名进行排序 178 | list_filename = os.listdir(read_dirpath) 179 | list_filename.sort(key=lambda x: int(x[:-4].replace(filename, ""))) 180 | for filename in list_filename: 181 | print(filename) 182 | filepath = os.path.join(read_dirpath, filename) 183 | # 读取文件并获取文件的页数 184 | pdf_reader = PdfFileReader(filepath) 185 | pages = pdf_reader.getNumPages() 186 | # 逐页添加 187 | for page in range(pages): 188 | pdf_writer.addPage(pdf_reader.getPage(page)) 189 | # 保存合并后的文件 190 | with open(save_filepath, "wb") as out: 191 | pdf_writer.write(out) 192 | print("文件已成功合并,保存路径为:"+save_filepath) 193 | 194 | filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf' 195 | read_dirpath = os.path.join(os.getcwd(), '易方达中小盘混合型证券投资基金2020年中期报告【拆分】') 196 | save_filepath = os.path.join(os.getcwd(), '易方达中小盘混合型证券投资基金2020年中期报告-合并后.pdf') 197 | concat_pdf(filename, read_dirpath, save_filepath) 198 | 199 | ``` 200 | 201 | 202 | 203 | ## 4.4. 提取文字内容 204 | 205 | 涉及到具体的 PDF 内容 操作,本小节需要用到 pdfplumber 这个库 206 | 207 | 在进行文字提取的时候,主要用到 extract_text 这个函数 208 | 209 | 具体代码如下: 210 | 211 | ```python 212 | import os 213 | import pdfplumber 214 | 215 | def extract_text_info(filepath): 216 | """ 217 | 提取PDF中的文字 218 | @param filepath:文件路径 219 | @return: 220 | """ 221 | with pdfplumber.open(filepath) as pdf: 222 | # 获取第2页数据 223 | page = pdf.pages[1] 224 | print(page.extract_text()) 225 | 226 | filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf' 227 | filepath = os.path.join(os.getcwd(), filename) 228 | # 提取文字内容 229 | extract_text_info(filepath) 230 | 231 | ``` 232 | 233 | 可以看到,直接通过下标即可定位到相应的页码,从而通过 extract_text 函数提取该也的所有文字 234 | 235 | 而如果想要提取所有页的文字,只需要改成: 236 | 237 | ```python 238 | with pdfplumber.open(filepath) as pdf: 239 | # 获取全部数据 240 | for page in pdf.pages 241 | print(page.extract_text()) 242 | ``` 243 | 244 | 例如,提取“易方达中小盘混合型证券投资基金2020年中期报告” 第一页的内容时,源文件是这样的: 245 | 246 | ![](./图片/4.3.png) 247 | 248 | 运行代码后提取出来是这样的: 249 | 250 | ![](./图片/4.4.png) 251 | 252 | > 拓展一下:此处可以结合前面 word 小节,将内容写入 word 文件中 253 | 254 | 255 | 256 | ## 4.5. 提取表格内容 257 | 258 | 同样的,本节是对具体内容的操作,所以也需要用到 pdfplumber 这个库 259 | 260 | 和提取文字十分类似的是,提取表格内容只是将 extract_text 函数换成了 extract_table 函数 261 | 262 | 对应的代码如下: 263 | 264 | ```python 265 | import os 266 | import pandas as pd 267 | import pdfplumber 268 | 269 | def extract_table_info(filepath): 270 | """ 271 | 提取PDF中的图表数据 272 | @param filepath: 273 | @return: 274 | """ 275 | with pdfplumber.open(filepath) as pdf: 276 | # 获取第18页数据 277 | page = pdf.pages[17] 278 | # 如果一页有一个表格,设置表格的第一行为表头,其余为数据 279 | table_info = page.extract_table() 280 | df_table = pd.DataFrame(table_info[1:], columns=table_info[0]) 281 | df_table.to_csv('dmeo.csv', index=False, encoding='gbk') 282 | 283 | filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf' 284 | filepath = os.path.join(os.getcwd(), filename) 285 | # 提取表格内容 286 | extract_table_info(filepath) 287 | 288 | ``` 289 | 290 | 上面代码可以获取到第 18 页的第一个表格内容,并且将其保存为 csv 文件存在本地 291 | 292 | > 但是,如果说第 18 页有多个表格内容呢? 293 | 294 | 因为读取的表格会被存成二维数组,而多个二维数组就组成一个三维数组 295 | 296 | 遍历这个三位数组,就可以得到该页的每一个表格数据,对应的将 extract_table 函数 改成 extract_tables 即可 297 | 298 | 具体代码如下: 299 | 300 | ```python 301 | import os 302 | import pandas as pd 303 | import pdfplumber 304 | 305 | def extract_table_info(filepath): 306 | """ 307 | 提取PDF中的图表数据 308 | @param filepath: 309 | @return: 310 | """ 311 | with pdfplumber.open(filepath) as pdf: 312 | # 获取第7页数据 313 | page = pdf.pages[6] 314 | # 如果一页有多个表格,对应的数据是一个三维数组 315 | tables_info = page.extract_tables() 316 | for index in range(len(tables_info)): 317 | # 设置表格的第一行为表头,其余为数据 318 | df_table = pd.DataFrame(tables_info[index][1:], columns=tables_info[index][0]) 319 | df_table.to_csv('dmeo.csv', index=False, encoding='gbk') 320 | 321 | filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf' 322 | filepath = os.path.join(os.getcwd(), filename) 323 | # 提取表格内容 324 | extract_table_info(filepath) 325 | 326 | ``` 327 | 328 | 以“易方达中小盘混合型证券投资基金2020年中期报告” 第 xx 页的第一个表格为例: 329 | 330 | 源文件中的表格是这样的: 331 | 332 | ![](./图片/4.5.png) 333 | 334 | 提取并存入 excel 之后的表格是这样的: 335 | 336 | ![](./图片/4.6.png) 337 | 338 | 339 | 340 | ## 4.6 提取图片内容 341 | 342 | 提取 PDF 中的图片和将 PDF 转存为图片是不一样的(下一小节),需要区分开。 343 | 344 | 提取图片:顾名思义,就是将内容中的图片都提取出来;转存为图片:则是将每一页的 PDF 内容存成一页一页的图片,下一小节会详细说明 345 | 346 | 转存为图片中,需要用到一个模块叫 fitz,fitz 的最新版 1.18.13,非最新版的在部分函数名称上存在差异,代码中会标记出来 347 | 348 | 使用 fitz 需要先安装 PyMuPDF 模块,安装方式如下: 349 | 350 | > pip install PyMuPDF 351 | 352 | 提取图片的整体逻辑如下: 353 | 354 | - 使用 fitz 打开文档,获取文档详细数据 355 | - 遍历每一个元素,通过正则找到图片的索引位置 356 | - 使用 Pixmap 将索引对应的元素生成图片 357 | - 通过 size 函数过滤较小的图片 358 | 359 | 实现的具体代码如下: 360 | 361 | ```python 362 | import os 363 | import re 364 | import fitz 365 | 366 | def extract_pic_info(filepath, pic_dirpath): 367 | """ 368 | 提取PDF中的图片 369 | @param filepath:pdf文件路径 370 | @param pic_dirpath:要保存的图片目录路径 371 | @return: 372 | """ 373 | if not os.path.exists(pic_dirpath): 374 | os.makedirs(pic_dirpath) 375 | # 使用正则表达式来查找图片 376 | check_XObject = r"/Type(?= */XObject)" 377 | check_Image = r"/Subtype(?= */Image)" 378 | img_count = 0 379 | 380 | """1. 打开pdf,打印相关信息""" 381 | pdf_info = fitz.open(filepath) 382 | # 1.16.8版本用法 xref_len = doc._getXrefLength() 383 | # 最新版本 384 | xref_len = pdf_info.xref_length() 385 | # 打印PDF的信息 386 | print("文件名:{}, 页数: {}, 对象: {}".format(filepath, len(pdf_info), xref_len-1)) 387 | 388 | """2. 遍历PDF中的对象,遇到是图像才进行下一步,不然就continue""" 389 | for index in range(1, xref_len): 390 | # 1.16.8版本用法 text = doc._getXrefString(index) 391 | # 最新版本 392 | text = pdf_info.xref_object(index) 393 | 394 | is_XObject = re.search(check_XObject, text) 395 | is_Image = re.search(check_Image, text) 396 | # 如果不是对象也不是图片,则不操作 397 | if is_XObject or is_Image: 398 | img_count += 1 399 | # 根据索引生成图像 400 | pix = fitz.Pixmap(pdf_info, index) 401 | pic_filepath = os.path.join(pic_dirpath, 'img_' + str(img_count) + '.png') 402 | """pix.size 可以反映像素多少,简单的色素块该值较低,可以通过设置一个阈值过滤。以阈值 10000 为例过滤""" 403 | # if pix.size < 10000: 404 | # continue 405 | 406 | """三、 将图像存为png格式""" 407 | if pix.n >= 5: 408 | # 先转换CMYK 409 | pix = fitz.Pixmap(fitz.csRGB, pix) 410 | # 存为PNG 411 | pix.writePNG(pic_filepath) 412 | 413 | filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf' 414 | filepath = os.path.join(os.getcwd(), filename) 415 | pic_dirpath = os.path.join(os.getcwd(), '易方达中小盘混合型证券投资基金2020年中期报告【文中图片】') 416 | # 提取图片内容 417 | extract_pic_info(filepath, pic_dirpath) 418 | 419 | ``` 420 | 421 | 以本节示例的“易方达中小盘混合型证券投资基金2020年中期报告” 中的图片为例,代码运行后提取的图片如下: 422 | 423 | ![](./图片/4.7.png) 424 | 425 | 这个结果和文档中的共 1 张图片的 **结果符合** 426 | 427 | 428 | 429 | ## 4.7 转换为图片 430 | 431 | 转换为照片比较简单,就是将一页页的 PDF 转换为一张张的图片。大致过程如下: 432 | 433 | ### 4.7.1 安装 pdf2image 434 | 435 | 首先需要安装对应的库,最新的 pdf2image 库版本应该是 1.14.0 436 | 437 | 它的 github地址 为:https://github.com/Belval/pdf2image ,感兴趣的可以自行了解 438 | 439 | 安装方式如下: 440 | 441 | > pip install pdf2image 442 | 443 | ### 4.7.2 安装组件 444 | 445 | 对于不同的平台,需要安装相应的组件,这里以 windows 平台和 mac 平台为例: 446 | 447 | **Windows 平台** 448 | 449 | 对于 windows 用户需要安装 poppler for Windows,安装链接是:http://blog.alivate.com.au/poppler-windows/ 450 | 451 | 另外,还需要添加环境变量, 将 bin 文件夹的路径添加到环境变量 PATH 中 452 | 453 | > 注意这里配置之后需要重启一下电脑才会生效,不然会报如下错误: 454 | 455 | **Mac** 456 | 457 | 对于 mac 用户,需要安装 poppler for Mac,具体可以参考这个链接:http://macappstore.org/poppler/ 458 | 459 | 460 | 461 | 详细代码如下: 462 | 463 | ```python 464 | import os 465 | from pdf2image import convert_from_path, convert_from_bytes 466 | 467 | def convert_to_pic(filepath, pic_dirpath): 468 | """ 469 | 每一页的PDF转换成图片 470 | @param filepath:pdf文件路径 471 | @param pic_dirpath:图片目录路径 472 | @return: 473 | """ 474 | print(filepath) 475 | if not os.path.exists(pic_dirpath): 476 | os.makedirs(pic_dirpath) 477 | 478 | images = convert_from_bytes(open(filepath, 'rb').read()) 479 | # images = convert_from_path(filepath, dpi=200) 480 | for image in images: 481 | # 保存图片 482 | pic_filepath = os.path.join(pic_dirpath, 'img_'+str(images.index(image))+'.png') 483 | image.save(pic_filepath, 'PNG') 484 | 485 | # PDF转换为图片 486 | convert_to_pic(filepath, pic_dirpath) 487 | 488 | 489 | filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf' 490 | filepath = os.path.join(os.getcwd(), filename) 491 | pic_dirpath = os.path.join(os.getcwd(), '易方达中小盘混合型证券投资基金2020年中期报告【转换为图片】') 492 | # PDF转换为图片 493 | convert_to_pic(filepath, pic_dirpath) 494 | 495 | ``` 496 | 497 | 以本节示例的“易方达中小盘混合型证券投资基金2020年中期报告” 中的图片为例,该文档共 46 页,保存后的 PDF 照片如下: 498 | 499 | ![](./图片/4.8.png) 500 | 501 | 一共 46 张图片 502 | 503 | 504 | 505 | ## 4.8. 添加水印 506 | 507 | PDF 中添加水印,首先需要一个水印PDF文件,然后依次通过 mergePage 操作将每一页的 PDF 文件合并到水印文件上,据此,每一页的 PDF 文件将是一个带有水印的 PDF 文件 508 | 509 | 最后,将每一页的水印 PDF 合并成一个 PDF 文件即可 510 | 511 | **生成水印** 512 | 513 | 生成水印的方式比较多,例如在图片添加水印,然后将图片插入到 word 中,最后将 word 保存成 PDF 文件即可 514 | 515 | 生成一张 A4 纸大小的空白图片,参考这篇文章:[Python 批量加水印!轻松搞定!](https://mp.weixin.qq.com/s/_oJA6lbsdMlRRsBf6DPxsg) 给图片添加水印,最终的水印背景图片是这样的: 516 | 517 | ![](./图片/4.8.png) 518 | 519 | 然后将图片插入到 word 中并最终生成一个水印 PDF 文档 520 | 521 | PDF 文档添加水印的主要代码如下: 522 | 523 | ```python 524 | import os 525 | from copy import copy 526 | from PyPDF2 import PdfFileReader, PdfFileWriter 527 | 528 | def add_watermark(filepath, save_filepath, watermark_filepath): 529 | """ 530 | 添加水印 531 | @param filepath:PDF文件路径 532 | @param save_filepath:最终的文件保存路径 533 | @param watermark_filepath:水印PDF文件路径 534 | @return: 535 | """ 536 | """读取PDF水印文件""" 537 | # 可以先生成一个空白A4大小的png图片,通过 https://mp.weixin.qq.com/s/_oJA6lbsdMlRRsBf6DPxsg 教程的方式给图片加水印,将图片插入到word中并最终生成一个水印PDF文档 538 | watermark = PdfFileReader(watermark_filepath) 539 | watermark_page = watermark.getPage(0) 540 | 541 | pdf_reader = PdfFileReader(filepath) 542 | pdf_writer = PdfFileWriter() 543 | 544 | for page_index in range(pdf_reader.getNumPages()): 545 | current_page = pdf_reader.getPage(page_index) 546 | # 封面页不添加水印 547 | if page_index == 0: 548 | new_page = current_page 549 | else: 550 | new_page = copy(watermark_page) 551 | new_page.mergePage(current_page) 552 | pdf_writer.addPage(new_page) 553 | # 保存水印后的文件 554 | with open(save_filepath, "wb") as out: 555 | pdf_writer.write(out) 556 | 557 | filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf' 558 | filepath = os.path.join(os.getcwd(), filename) 559 | save_filepath = os.path.join(os.getcwd(), '易方达中小盘混合型证券投资基金2020年中期报告-水印.pdf') 560 | watermark_filepath = os.path.join(os.getcwd(), 'watermark.pdf') 561 | # 添加水印 562 | add_watermark(filepath, save_filepath, watermark_filepath) 563 | 564 | ``` 565 | 566 | 以本节示例的“易方达中小盘混合型证券投资基金2020年中期报告” 为例,添加水印后的文档如下: 567 | 568 | ![](https://raw.githubusercontent.com/double-point/GraphBed/master/python_2_pdf/%E5%B8%A6%E6%B0%B4%E5%8D%B0%E7%9A%84PDF.png) 569 | 570 | 571 | 572 | ## 4.9. 文档加密与解密 573 | 574 | 你可能在打开部分 PDF 文件的时候,会弹出下面这个界面: 575 | 576 | ![](./图片/4.10.png) 577 | 578 | 这种就是 PDF 文件被加密了,在打开的时候需要相应的密码才行 579 | 580 | 本节所提到的也只是基于 PDF 文档的加密解密,而不是所谓的 PDF 密码破解。 581 | 582 | 在对 PDF 文件加密需要使用 encrypt 函数,对应的加密代码也比较简单: 583 | 584 | ```python 585 | import os 586 | from PyPDF2 import PdfFileReader, PdfFileWriter 587 | 588 | def encrypt_pdf(filepath, save_filepath, passwd='xiaoyi'): 589 | """ 590 | PDF文档加密 591 | @param filepath:PDF文件路径 592 | @param save_filepath:加密后的文件保存路径 593 | @param passwd:密码 594 | @return: 595 | """ 596 | pdf_reader = PdfFileReader(filepath) 597 | pdf_writer = PdfFileWriter() 598 | 599 | for page_index in range(pdf_reader.getNumPages()): 600 | pdf_writer.addPage(pdf_reader.getPage(page_index)) 601 | 602 | # 添加密码 603 | pdf_writer.encrypt(passwd) 604 | with open(save_filepath, "wb") as out: 605 | pdf_writer.write(out) 606 | 607 | filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf' 608 | filepath = os.path.join(os.getcwd(), filename) 609 | save_filepath = os.path.join(os.getcwd(), '易方达中小盘混合型证券投资基金2020年中期报告-加密后.pdf') 610 | # 文档加密 611 | encrypt_pdf(filepath, save_filepath, passwd='xiaoyi') 612 | 613 | ``` 614 | 615 | 代码执行成功后再次打开 PDF 文件则需要输入密码才行 616 | 617 | 根据这个思路,破解 PDF 也可以通过暴力求解实现,例如:通过本地密码本一个个去尝试,或者根据数字+字母的密码形式循环尝试,最终成功打开的密码就是破解密码 618 | 619 | > 上述破解方法耗时耗力,不建议尝试 620 | 621 | 622 | 623 | 另外,针对已经加密的 PDF 文件,也可以使用 decrypt 函数进行解密操作 624 | 625 | 解密代码如下: 626 | 627 | ```python 628 | def decrypt_pdf(filepath, save_filepath, passwd='xiaoyi'): 629 | """ 630 | 解密 PDF 文档并且保存为未加密的 PDF 631 | @param filepath:PDF文件路径 632 | @param save_filepath:解密后的文件保存路径 633 | @param passwd:密码 634 | @return: 635 | """ 636 | pdf_reader = PdfFileReader(filepath) 637 | # PDF文档解密 638 | pdf_reader.decrypt('xiaoyi') 639 | 640 | pdf_writer = PdfFileWriter() 641 | for page_index in range(pdf_reader.getNumPages()): 642 | pdf_writer.addPage(pdf_reader.getPage(page_index)) 643 | 644 | with open(save_filepath, "wb") as out: 645 | pdf_writer.write(out) 646 | 647 | filename = '易方达中小盘混合型证券投资基金2020年中期报告-加密后.pdf' 648 | filepath = os.path.join(os.getcwd(), filename) 649 | save_filepath = os.path.join(os.getcwd(), '易方达中小盘混合型证券投资基金2020年中期报告-解密后.pdf') 650 | # 文档解密 651 | decrypt_pdf(filepath, save_filepath, passwd='xiaoyi') 652 | 653 | ``` 654 | 655 | 解密完成后的 PDF 文档打开后不再需要输入密码,如需加密可再次执行加密代码。 656 | 657 | ### 页面旋转 658 | 659 | 利用 rotateClockwise()和 rotateCounterClockwise()方法 可以对pdf页面进行90度及其倍数的旋转。 660 | 661 | ```python 662 | import PyPDF2 663 | 664 | filename = '易方达中小盘混合型证券投资基金2020年中期报告.pdf' 665 | filepath = os.path.join(os.getcwd(), filename) 666 | save_filepath = os.path.join(os.getcwd(), '易方达中小盘混合型证券投资基金2020年中期报告-旋转.pdf') 667 | pdf_reader = PdfFileReader(filepath) 668 | page = pdf_reader.getPage(0) 669 | page.rotateClockwise(90) 670 | pdf_writer = PdfFileWriter() 671 | pdf_writer.addPage(page) 672 | with open(save_filepath, "wb") as out: 673 | pdf_writer.write(out) 674 | ``` 675 | 676 | 677 | 678 | **Task04 END.** 679 | 680 | --- By: xiaoyi 681 | 682 | >**Datawhale成员,数据分析从业者,金融风控爱好者** 683 | > 684 | >**公众号:小一的学习笔记** 685 | -------------------------------------------------------------------------------- /Task03-Python与Word和PDF/python与word.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "### **初步认识docx**" 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": null, 13 | "metadata": {}, 14 | "outputs": [], 15 | "source": [ 16 | "# 导入库\n", 17 | "from docx import Document\n", 18 | "\n", 19 | "# 新建空白文档\n", 20 | "doc_1 = Document()\n", 21 | "\n", 22 | "# 添加标题(0相当于文章的题目,默认级别是1,级别范围为0-9)\n", 23 | "doc_1.add_heading('新建空白文档标题,级别为0',level = 0)\n", 24 | "doc_1.add_heading('新建空白文档标题,级别为1',level = 1)\n", 25 | "doc_1.add_heading('新建空白文档标题,级别为2',level = 2)\n", 26 | "\n", 27 | "# 新增段落\n", 28 | "paragraph_1 = doc_1.add_paragraph('这是第一段文字的开始\\n请多多关照!')\n", 29 | "# 加粗\n", 30 | "paragraph_1.add_run('加粗字体').bold = True\n", 31 | "paragraph_1.add_run('普通字体')\n", 32 | "# 斜体\n", 33 | "paragraph_1.add_run('斜体字体').italic =True\n", 34 | "\n", 35 | "# 新段落(当前段落的下方)\n", 36 | "paragraph_2 = doc_1.add_paragraph('新起的第二段文字。')\n", 37 | "\n", 38 | "# 新段落(指定端的上方)\n", 39 | "prior_paragraph = paragraph_1.insert_paragraph_before('在第一段文字前插入的段落')\n", 40 | "\n", 41 | "# 添加分页符(可以进行灵活的排版)\n", 42 | "doc_1.add_page_break()\n", 43 | "# 新段落(指定端的上方)\n", 44 | "paragraph_3 = doc_1.add_paragraph('这是第二页第一段文字!')\n", 45 | "\n", 46 | "# 保存文件(当前目录下)\n", 47 | "doc_1.save('doc_1.docx')" 48 | ] 49 | }, 50 | { 51 | "cell_type": "markdown", 52 | "metadata": {}, 53 | "source": [ 54 | "### **整体页面结构介绍**" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 14, 60 | "metadata": {}, 61 | "outputs": [ 62 | { 63 | "name": "stdout", 64 | "output_type": "stream", 65 | "text": [ 66 | "1\n", 67 | "中国台湾华语流行歌手、音乐创作家、作曲家、作词人、制作人、杰威尔音乐公司老板之一、导演。近年涉足电影行业。周杰伦是2000年后亚洲流行乐坛最具革命性与指标性的创作歌手,有“亚洲流行天王”之称。他突破原有亚洲音乐的主题、形式,融合多元的音乐素材,创造出多变的歌曲风格,尤以融合中西式曲风的嘻哈或节奏蓝调最为著名,可说是开创华语流行音乐“中国风”的先声。周杰伦的出现打破了亚洲流行乐坛长年停滞不前的局面,为亚洲流行乐坛翻开了新的一页!\n" 68 | ] 69 | } 70 | ], 71 | "source": [ 72 | "# 导入库\n", 73 | "from docx import Document\n", 74 | "from docx.shared import RGBColor, Pt,Inches,Cm\n", 75 | "from docx.enum.text import WD_PARAGRAPH_ALIGNMENT\n", 76 | "from docx.oxml.ns import qn\n", 77 | "\n", 78 | "# 新建文档(Datawhale)\n", 79 | "doc_1 = Document()\n", 80 | "\n", 81 | "# 字体设置(全局)\n", 82 | "'''只更改font.name是不够的,还需要调用._element.rPr.rFonts的set()方法。'''\n", 83 | "\n", 84 | "doc_1.styles['Normal'].font.name = u'宋体'\n", 85 | "doc_1.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')\n", 86 | "\n", 87 | "# 添加标题(0相当于文章的题目,默认级别是1,级别范围为0-9,0时候自动带下划线)\n", 88 | "#WD_ALIGN_PARAGRAPH. LEFT:左对齐;\n", 89 | "#WD_ALIGN_PARAGRAPH. CENTER:居中对其;\n", 90 | "#WD_ALIGN_PARAGRAPH. RIGHT:右对齐;\n", 91 | "#WD_ALIGN_PARAGRAPH. JUSTIFY:两端对齐;\n", 92 | "heading_1 = doc_1.add_heading('周杰伦',level = 0)\n", 93 | "heading_1.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER #居中对齐,默认居左对齐\n", 94 | "\n", 95 | "# 新增段落\n", 96 | "paragraph_1 = doc_1.add_paragraph()\n", 97 | "'''\n", 98 | "设置段落格式:首行缩进0.75cm,居左,段后距离1.0英寸,1.5倍行距。\n", 99 | "'''\n", 100 | "paragraph_1.paragraph_format.first_line_indent = Cm(0.75)\n", 101 | "paragraph_1.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.LEFT\n", 102 | "paragraph_1.paragraph_format.space_after = Inches(1.0)\n", 103 | "paragraph_1.paragraph_format.line_spacing = 1.5\n", 104 | "\n", 105 | "text = '中国台湾华语流行歌手、' \\\n", 106 | " '音乐创作家、作曲家、作词人、' \\\n", 107 | " '制作人、杰威尔音乐公司老板之一、导演。' \\\n", 108 | " '近年涉足电影行业。周杰伦是2000年后亚洲流行乐坛最具革命性与指标' \\\n", 109 | " '性的创作歌手,有“亚洲流行天王”之称。他突破原有亚洲音乐的主题、形' \\\n", 110 | " '式,融合多元的音乐素材,创造出多变的歌曲风格,尤以融合中西式曲风的嘻哈' \\\n", 111 | " '或节奏蓝调最为著名,可说是开创华语流行音乐“中国风”的先声。周杰伦的' \\\n", 112 | " '出现打破了亚洲流行乐坛长年停滞不前的局面,为亚洲流行乐坛翻开了新的一页!'\n", 113 | " \n", 114 | "r_1 = paragraph_1.add_run(text)\n", 115 | "r_1.font.size =Pt(10) #字号\n", 116 | "r_1.font.bold =True #加粗\n", 117 | "r_1.font.color.rgb =RGBColor(255,0,0) #颜色\n", 118 | "\n", 119 | "print(len(paragraph_1.runs)) # 查看段落拥有的run对象数量\n", 120 | "print(paragraph_1.runs[0].text) # 查看对应run对象的文本等属性\n", 121 | "\n", 122 | "# 保存文件(当前目录下)\n", 123 | "doc_1.save('周杰伦.docx')" 124 | ] 125 | }, 126 | { 127 | "cell_type": "markdown", 128 | "metadata": {}, 129 | "source": [ 130 | "### **字体设置**" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": null, 136 | "metadata": {}, 137 | "outputs": [], 138 | "source": [ 139 | "'''字体设置1.py'''\n", 140 | "#导入库\n", 141 | "from docx import Document\n", 142 | "from docx.oxml.ns import qn\n", 143 | "from docx.enum.style import WD_STYLE_TYPE\n", 144 | "\n", 145 | "document = Document() # 新建docx文档\n", 146 | "\n", 147 | "# 设置宋体字样式\n", 148 | "style_font = document.styles.add_style('宋体', WD_STYLE_TYPE.CHARACTER)\n", 149 | "style_font.font.name = '宋体'\n", 150 | "document.styles['宋体']._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体')\n", 151 | "\n", 152 | "# 设置楷体字样式\n", 153 | "style_font = document.styles.add_style('楷体', WD_STYLE_TYPE.CHARACTER)\n", 154 | "style_font.font.name = '楷体'\n", 155 | "document.styles['楷体']._element.rPr.rFonts.set(qn('w:eastAsia'), u'楷体')\n", 156 | "\n", 157 | "# 设置华文中宋字样式\n", 158 | "style_font = document.styles.add_style('华文中宋', WD_STYLE_TYPE.CHARACTER)\n", 159 | "style_font.font.name = '华文中宋'\n", 160 | "document.styles['华文中宋']._element.rPr.rFonts.set(qn('w:eastAsia'), u'华文中宋')\n", 161 | "\n", 162 | "paragraph1 = document.add_paragraph() # 添加段落\n", 163 | "run = paragraph1.add_run(u'aBCDefg这是中文', style='宋体') # 设置宋体样式\n", 164 | "\n", 165 | "font = run.font #设置字体\n", 166 | "font.name = 'Cambira' # 设置西文字体\n", 167 | "paragraph1.add_run(u'aBCDefg这是中文', style='楷体').font.name = 'Cambira'\n", 168 | "paragraph1.add_run(u'aBCDefg这是中文', style='华文中宋').font.name = 'Cambira'\n", 169 | "\n", 170 | "document.save('字体设置1.docx')" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": null, 176 | "metadata": {}, 177 | "outputs": [], 178 | "source": [ 179 | "'''字体设置2.py'''\n", 180 | "#导入库\n", 181 | "from docx import Document\n", 182 | "from docx.oxml.ns import qn\n", 183 | "from docx.enum.style import WD_STYLE_TYPE\n", 184 | "\n", 185 | "#定义字体设置函数\n", 186 | "def font_setting(doc,text,font_cn):\n", 187 | " style_add = doc.styles.add_style(font_cn, WD_STYLE_TYPE.CHARACTER)\n", 188 | " style_add.font.name = font_cn\n", 189 | " doc.styles[font_cn]._element.rPr.rFonts.set(qn('w:eastAsia'), font_cn)\n", 190 | " par = doc.add_paragraph()\n", 191 | " text = par.add_run(text, style=font_cn)\n", 192 | "\n", 193 | "doc = Document()\n", 194 | "a = '小朋友 你是否有很多问号'\n", 195 | "b = '为什么 别人在那看漫画'\n", 196 | "c = '我却在学画画 对着钢琴说话'\n", 197 | "\n", 198 | "font_setting(doc,a,'宋体')\n", 199 | "font_setting(doc,b,'华文中宋')\n", 200 | "font_setting(doc,c,'黑体')\n", 201 | "\n", 202 | "doc.save('字体设置2.docx')" 203 | ] 204 | }, 205 | { 206 | "cell_type": "markdown", 207 | "metadata": {}, 208 | "source": [ 209 | "### **插入图片与表格**" 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": 44, 215 | "metadata": {}, 216 | "outputs": [], 217 | "source": [ 218 | "#导入库\n", 219 | "import docx\n", 220 | "from docx import Document\n", 221 | "from docx.shared import Inches\n", 222 | "\n", 223 | "#打开文档\n", 224 | "doc_1 = Document('周杰伦.docx') #上面脚本存储的文档\n", 225 | "#新增图片\n", 226 | "doc_1.add_picture('周杰伦.jpg',width=Inches(1.0), height=Inches(1.0))\n", 227 | "\n", 228 | "# 创建3行1列表格\n", 229 | "table1 = doc_1.add_table(rows=2, cols=1)\n", 230 | "table1.style='Medium Grid 1 Accent 1' #表格样式很多种,如,Light Shading Accent 1等\n", 231 | "\n", 232 | "# 修改第2行第3列单元格的内容为营口\n", 233 | "table1.cell(0, 0).text = '营口'\n", 234 | "# 修改第3行第4列单元格的内容为人民\n", 235 | "table1.rows[1].cells[0].text = '人民'\n", 236 | "\n", 237 | "# 在表格底部新增一行\n", 238 | "row_cells = table1.add_row().cells\n", 239 | "# 新增行的第一列添加内容\n", 240 | "row_cells[0].text = '加油'\n", 241 | "\n", 242 | "doc_1.save('周杰伦为营口加油.docx')" 243 | ] 244 | }, 245 | { 246 | "cell_type": "markdown", 247 | "metadata": {}, 248 | "source": [ 249 | "### **设置页眉页脚**" 250 | ] 251 | }, 252 | { 253 | "cell_type": "code", 254 | "execution_count": null, 255 | "metadata": {}, 256 | "outputs": [], 257 | "source": [ 258 | "from docx import Document\n", 259 | "from docx.enum.text import WD_PARAGRAPH_ALIGNMENT\n", 260 | "\n", 261 | "document = Document() # 新建文档\n", 262 | "\n", 263 | "header = document.sections[0].header # 获取第一个节的页眉\n", 264 | "print('页眉中默认段落数:', len(header.paragraphs))\n", 265 | "paragraph = header.paragraphs[0] # 获取页眉的第一个段落\n", 266 | "paragraph.add_run('这是第一节的页眉') # 添加页面内容\n", 267 | "footer = document.sections[0].footer # 获取第一个节的页脚\n", 268 | "paragraph = footer.paragraphs[0] # 获取页脚的第一个段落\n", 269 | "paragraph.add_run('这是第一节的页脚') # 添加页脚内容\n", 270 | "\n", 271 | "\n", 272 | "'''在docx文档中又添加了2个节,共计3个节,页面和页脚会显示了“与上一节相同”。\n", 273 | "如果不使用上一节的内容和样式要将header.is_linked_to_previous的属性或footer.is_linked_to_previous的属性设置为False,\n", 274 | "用于解除“链接上一节页眉”或者“链接上一节页脚”。'''\n", 275 | "document.add_section() # 添加一个新的节\n", 276 | "document.add_section() # 添加第3个节\n", 277 | "header = document.sections[1].header # 获取第2个节的页眉\n", 278 | "header.is_linked_to_previous = False # 不使用上节内容和样式\n", 279 | "\n", 280 | "#对齐设置\n", 281 | "header = document.sections[1].header # 获取第2个节的页眉\n", 282 | "header.is_linked_to_previous = False # 不使用上节内容和样式\n", 283 | "paragraph = header.paragraphs[0]\n", 284 | "paragraph.add_run('这是第二节的页眉')\n", 285 | "paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER # 设置页眉居中对齐\n", 286 | "document.sections[1].footer.is_linked_to_previous = False\n", 287 | "footer.paragraphs[0].add_run('这是第二节的页脚') # 添加第2节页脚内容\n", 288 | "footer.paragraphs[0].alignment = WD_PARAGRAPH_ALIGNMENT.CENTER # 设置第2节页脚居中对齐\n", 289 | "header = document.sections[2].header # 获取第3个节的页眉\n", 290 | "header.is_linked_to_previous = False # 不使用上节的内容和样式\n", 291 | "paragraph = header.paragraphs[0] # 获取页眉中的段落\n", 292 | "paragraph.add_run('这是第三节的页眉')\n", 293 | "paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT # 设置页眉右对齐\n", 294 | "document.sections[2].footer.is_linked_to_previous = False\n", 295 | "footer.paragraphs[0].add_run('这是第三节的页脚') # 添加第3节页脚内容\n", 296 | "footer.paragraphs[0].alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT # 设置第3节页脚右对齐\n", 297 | "document.save('页眉页脚1.docx') # 保存文档" 298 | ] 299 | }, 300 | { 301 | "cell_type": "markdown", 302 | "metadata": {}, 303 | "source": [ 304 | "### **项目实践**" 305 | ] 306 | }, 307 | { 308 | "cell_type": "code", 309 | "execution_count": null, 310 | "metadata": {}, 311 | "outputs": [], 312 | "source": [ 313 | "# 导入库\n", 314 | "from openpyxl import load_workbook\n", 315 | "from docx import Document\n", 316 | "from docx.enum.text import WD_PARAGRAPH_ALIGNMENT\n", 317 | "from docx.shared import RGBColor, Pt,Inches,Cm\n", 318 | "from docx.oxml.ns import qn\n", 319 | "\n", 320 | "\n", 321 | "path = r'D:\\idea\\cloud_analyse_game_sentiment\\word自动化'\n", 322 | "# 路径为Excel 文件所在的位置,可按实际情况更改\n", 323 | "\n", 324 | "workbook = load_workbook(path + r'\\excel到word.xlsx')\n", 325 | "sheet = workbook.active #默认的WorkSheet\n", 326 | "\n", 327 | "n = 0 #为了不遍历标题(excel的第一行)\n", 328 | "for row in sheet.rows:\n", 329 | " if n:\n", 330 | " company = row[0].value\n", 331 | " office = row[1].value\n", 332 | " name = row[2].value\n", 333 | " date = str(row[3].value).split()[0]\n", 334 | " print(company, office, name, date)\n", 335 | "\n", 336 | "\n", 337 | " doc = Document()\n", 338 | " heading_1 = '邀 请 函'\n", 339 | " paragraph_1 = doc.add_heading(heading_1, level=1)\n", 340 | " # 居中对齐\n", 341 | " paragraph_1.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER\n", 342 | " # 单独修改较大字号\n", 343 | " for run in paragraph_1.runs:\n", 344 | " run.font.size = Pt(17)\n", 345 | "\n", 346 | " greeting_word_1 = '尊敬的'\n", 347 | " greeting_word_2 = '公司'\n", 348 | " greeting_word_3 = ',您好:'\n", 349 | " paragraph_2 = doc.add_paragraph()\n", 350 | "\n", 351 | " paragraph_2.add_run(greeting_word_1)\n", 352 | " r_1 = paragraph_2.add_run(company)\n", 353 | " r_1.font.bold = True # 加粗\n", 354 | " r_1.font.underline = True #下划线\n", 355 | "\n", 356 | " paragraph_2.add_run(greeting_word_2)\n", 357 | "\n", 358 | " r_2 = paragraph_2.add_run(office)\n", 359 | " r_2.font.bold = True # 加粗\n", 360 | " r_2.font.underline = True #下划线\n", 361 | "\n", 362 | " r_3 = paragraph_2.add_run(name)\n", 363 | " r_3.font.bold = True # 加粗\n", 364 | " r_3.font.underline = True #下划线\n", 365 | " paragraph_2.add_run(greeting_word_3)\n", 366 | "\n", 367 | " paragraph_3 = doc.add_paragraph()\n", 368 | " paragraph_3.add_run('现诚挚的邀请您于2021年10月27日参加DataWhale主办的享受开源2050活动,地点在北京鸟巢,希望您届时莅临参加。')\n", 369 | " paragraph_3.paragraph_format.first_line_indent = Cm(0.75)\n", 370 | " paragraph_3.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.LEFT\n", 371 | " paragraph_3.paragraph_format.space_after = Inches(1.0)\n", 372 | " paragraph_3.paragraph_format.line_spacing = 1.5\n", 373 | "\n", 374 | " paragraph_4 = doc.add_paragraph()\n", 375 | " date_word_1 = '邀请时间:'\n", 376 | " paragraph_4.add_run(date_word_1)\n", 377 | " paragraph_4.alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT\n", 378 | " sign_date = \"{}年{}月{}日\".format(date.split('-')[0], date.split('-')[1], date.split('-')[2])\n", 379 | " paragraph_4.add_run(sign_date).underline = True\n", 380 | " paragraph_4.alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT\n", 381 | " \n", 382 | " #设置全文字体\n", 383 | " for paragraph in doc.paragraphs:\n", 384 | " for run in paragraph.runs:\n", 385 | " run.font.color.rgb = RGBColor(0, 0, 0)\n", 386 | " run.font.name = '楷体'\n", 387 | " r = run._element.rPr.rFonts\n", 388 | " r.set(qn('w:eastAsia'), '楷体')\n", 389 | " doc.save(path + \"\\{}-邀请函.docx\".format(name))\n", 390 | " n = n + 1" 391 | ] 392 | } 393 | ], 394 | "metadata": { 395 | "kernelspec": { 396 | "display_name": "Python 3", 397 | "language": "python", 398 | "name": "python3" 399 | }, 400 | "language_info": { 401 | "codemirror_mode": { 402 | "name": "ipython", 403 | "version": 3 404 | }, 405 | "file_extension": ".py", 406 | "mimetype": "text/x-python", 407 | "name": "python", 408 | "nbconvert_exporter": "python", 409 | "pygments_lexer": "ipython3", 410 | "version": "3.7.4" 411 | } 412 | }, 413 | "nbformat": 4, 414 | "nbformat_minor": 2 415 | } 416 | -------------------------------------------------------------------------------- /Task03-Python与Word和PDF/python与word.md: -------------------------------------------------------------------------------- 1 | # Task 03 python与word 2 | 3 | - [Task 03 python与word](#task-03-python与word) 4 | - [3.0 课前准备](#30-课前准备) 5 | - [3.1.知识要点](#31知识要点) 6 | - [3.1.1 初步认识docx](#311-初步认识docx) 7 | - [3.1.2 整体页面结构介绍](#312-整体页面结构介绍) 8 | - [3.1.2字体设置](#312字体设置) 9 | - [3.1.3插入图片与表格](#313插入图片与表格) 10 | - [3.1.4设置页眉页脚](#314设置页眉页脚) 11 | - [3.1.5代码延伸](#315代码延伸) 12 | - [3.2 项目实践](#32-项目实践) 13 | - [3.2.1需求](#321需求) 14 | - [3.2.2需求分析](#322需求分析) 15 | - [3.2.3代码](#323代码) 16 | - [3.3 后记](#33-后记) 17 | 18 | 19 | 20 | ## 3.0 课前准备 21 | 22 | > python 处理 Word 需要用到 python-docx 库,需要注意的是pythonn-docx不支持doc文档,终端执行如下安装命令: 23 | 24 | ```pyhton 25 | pip3 install python-docx 26 | 或 27 | conda install python-docx 28 | ``` 29 | 30 | > 或在pycharm的setting操作安装(示意如下): 31 | 32 | ![](.\图片\3.1.png) 33 | 34 | 35 | 36 | ## 3.1.知识要点 37 | 38 | 项目难度:⭐ 39 | 40 | > 说明: 41 | > 1. 通过小试牛刀初步认识docx,然后系统学习python对word的操作; 42 | > 2. 预估每个知识点需要讲解的时间; 43 | > 3. 研发逻辑就是讲解逻辑,一般从上往下,遵循:`What - Why - How` 或 `Why - What - How` 思路; 44 | 45 | 46 | 47 | ### 3.1.1 初步认识docx 48 | 49 | 相信同学们都进行过word的操作。话不多说,直接上python对word简单操作的代码,先有个直观的感觉,然后再系统学习! 50 | 51 | ```python 52 | # 导入库 53 | from docx import Document 54 | 55 | # 新建空白文档 56 | doc_1 = Document() 57 | 58 | # 添加标题(0相当于文章的题目,默认级别是1,级别范围为0-9) 59 | doc_1.add_heading('新建空白文档标题,级别为0',level = 0) 60 | doc_1.add_heading('新建空白文档标题,级别为1',level = 1) 61 | doc_1.add_heading('新建空白文档标题,级别为2',level = 2) 62 | 63 | # 新增段落 64 | paragraph_1 = doc_1.add_paragraph('这是第一段文字的开始\n请多多关照!') 65 | # 加粗 66 | paragraph_1.add_run('加粗字体').bold = True 67 | paragraph_1.add_run('普通字体') 68 | # 斜体 69 | paragraph_1.add_run('斜体字体').italic =True 70 | 71 | # 新段落(当前段落的下方) 72 | paragraph_2 = doc_1.add_paragraph('新起的第二段文字。') 73 | 74 | # 新段落(指定端的上方) 75 | prior_paragraph = paragraph_1.insert_paragraph_before('在第一段文字前插入的段落') 76 | 77 | # 添加分页符(可以进行灵活的排版) 78 | doc_1.add_page_break() 79 | # 新段落(指定端的上方) 80 | paragraph_3 = doc_1.add_paragraph('这是第二页第一段文字!') 81 | 82 | # 保存文件(当前目录下) 83 | doc_1.save('doc_1.docx') 84 | ``` 85 | 86 | --- 87 | 88 | 上节只是小试牛刀一下,接下来我们系统地学习python自动化之word操作。 89 | 90 | 在操作之前,我们需要了解 Word 文档的页面结构 : 91 | 92 | - 文档 - Document 93 | - 段落 - Paragraph 94 | - 文字块 - Run 95 | 96 | **`python-docx`**将整个文章看做是一个**`Document`**对象 ,其基本结构如下: 97 | 98 | - 每个**`Document`**包含许多个代表“段落”的**`Paragraph`**对象,存放在**`document.paragraphs`**中。 99 | - 每个**`Paragraph`**都有许多个代表"行内元素"的**`Run`**对象,存放在**`paragraph.runs`**中。 100 | 101 | 在**`python-docx`**中,**`run`**是最基本的单位,每个**`run`**对象内的文本样式都是一致的,也就是说,在从**`docx`**文件生成文档对象时,**`python-docx`**会根据样式的变化来将文本切分为一个个的`Run`对象。 102 | 103 | ### 3.1.2 整体页面结构介绍 104 | 105 | 我们以一个小案例为主线把文档,段落和文字块串一下: 106 | 107 | ```python 108 | # 导入库 109 | from docx import Document 110 | from docx.shared import RGBColor, Pt,Inches,Cm 111 | from docx.enum.text import WD_PARAGRAPH_ALIGNMENT 112 | from docx.oxml.ns import qn 113 | 114 | # 新建文档(Datawhale) 115 | doc_1 = Document() 116 | 117 | # 字体设置(全局) 118 | '''只更改font.name是不够的,还需要调用._element.rPr.rFonts的set()方法。''' 119 | 120 | doc_1.styles['Normal'].font.name = u'宋体' 121 | doc_1.styles['Normal']._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体') 122 | 123 | # 添加标题(0相当于文章的题目,默认级别是1,级别范围为0-9,0时候自动带下划线) 124 | #WD_ALIGN_PARAGRAPH. LEFT:左对齐; 125 | #WD_ALIGN_PARAGRAPH. CENTER:居中对其; 126 | #WD_ALIGN_PARAGRAPH. RIGHT:右对齐; 127 | #WD_ALIGN_PARAGRAPH. JUSTIFY:两端对齐; 128 | 129 | heading_1 = doc_1.add_heading('周杰伦',level = 0) 130 | heading_1.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER #居中对齐,默认居左对齐 131 | 132 | # 新增段落 133 | paragraph_1 = doc_1.add_paragraph() 134 | ''' 135 | 设置段落格式:首行缩进0.75cm,居左,段后距离1.0英寸,1.5倍行距。 136 | ''' 137 | paragraph_1.paragraph_format.first_line_indent = Cm(0.75) 138 | paragraph_1.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.LEFT 139 | paragraph_1.paragraph_format.space_after = Inches(1.0) 140 | paragraph_1.paragraph_format.line_spacing = 1.5 141 | 142 | text = '中国台湾华语流行歌手、' \ 143 | '音乐创作家、作曲家、作词人、' \ 144 | '制作人、杰威尔音乐公司老板之一、导演。' \ 145 | '近年涉足电影行业。周杰伦是2000年后亚洲流行乐坛最具革命性与指标' \ 146 | '性的创作歌手,有“亚洲流行天王”之称。他突破原有亚洲音乐的主题、形' \ 147 | '式,融合多元的音乐素材,创造出多变的歌曲风格,尤以融合中西式曲风的嘻哈' \ 148 | '或节奏蓝调最为著名,可说是开创华语流行音乐“中国风”的先声。周杰伦的' \ 149 | '出现打破了亚洲流行乐坛长年停滞不前的局面,为亚洲流行乐坛翻开了新的一页!' 150 | 151 | r_1 = paragraph_1.add_run(text) 152 | r_1.font.size =Pt(10) #字号 153 | r_1.font.bold =True #加粗 154 | r_1.font.color.rgb =RGBColor(255,0,0) #颜色 155 | 156 | print(len(paragraph_1.runs)) # 查看段落拥有的run对象数量 157 | print(paragraph_1.runs[0].text) # 查看对应run对象的文本等属性 158 | 159 | # 保存文件(当前目录下) 160 | doc_1.save('周杰伦.docx') 161 | ``` 162 | 通过上例我们可以看到,最小的操作对象为文字块,通过run的指定进行操作。比如字号,颜色等;而再上一个层级--段落是的格式是通过paragraph_format进行设置; 163 | 164 | ### 3.1.2字体设置 165 | 166 | 通过(1),同学们已经注意到,字体的设置是全局变量。如果我想在不同的部分进行不同字体的设置,那该怎么办呢?这就需要在应用前操作设置一下。 167 | 168 | ```python 169 | '''字体设置1.py''' 170 | #导入库 171 | from docx import Document 172 | from docx.oxml.ns import qn 173 | from docx.enum.style import WD_STYLE_TYPE 174 | 175 | document = Document() # 新建docx文档 176 | 177 | # 设置宋体字样式 178 | style_font = document.styles.add_style('宋体', WD_STYLE_TYPE.CHARACTER) 179 | style_font.font.name = '宋体' 180 | document.styles['宋体']._element.rPr.rFonts.set(qn('w:eastAsia'), u'宋体') 181 | 182 | # 设置楷体字样式 183 | style_font = document.styles.add_style('楷体', WD_STYLE_TYPE.CHARACTER) 184 | style_font.font.name = '楷体' 185 | document.styles['楷体']._element.rPr.rFonts.set(qn('w:eastAsia'), u'楷体') 186 | 187 | # 设置华文中宋字样式 188 | style_font = document.styles.add_style('华文中宋', WD_STYLE_TYPE.CHARACTER) 189 | style_font.font.name = '华文中宋' 190 | document.styles['华文中宋']._element.rPr.rFonts.set(qn('w:eastAsia'), u'华文中宋') 191 | 192 | paragraph1 = document.add_paragraph() # 添加段落 193 | run = paragraph1.add_run(u'aBCDefg这是中文', style='宋体') # 设置宋体样式 194 | 195 | font = run.font #设置字体 196 | font.name = 'Cambira' # 设置西文字体 197 | paragraph1.add_run(u'aBCDefg这是中文', style='楷体').font.name = 'Cambira' 198 | paragraph1.add_run(u'aBCDefg这是中文', style='华文中宋').font.name = 'Cambira' 199 | 200 | document.save('字体设置1.docx') 201 | ``` 202 | 203 | ```python 204 | '''字体设置2.py''' 205 | #导入库 206 | from docx import Document 207 | from docx.oxml.ns import qn 208 | from docx.enum.style import WD_STYLE_TYPE 209 | 210 | #定义字体设置函数 211 | def font_setting(doc,text,font_cn): 212 | style_add = doc.styles.add_style(font_cn, WD_STYLE_TYPE.CHARACTER) 213 | style_add.font.name = font_cn 214 | doc.styles[font_cn]._element.rPr.rFonts.set(qn('w:eastAsia'), font_cn) 215 | par = doc.add_paragraph() 216 | text = par.add_run(text, style=font_cn) 217 | 218 | doc = Document() 219 | a = '小朋友 你是否有很多问号' 220 | b = '为什么 别人在那看漫画' 221 | c = '我却在学画画 对着钢琴说话' 222 | 223 | font_setting(doc,a,'宋体') 224 | font_setting(doc,b,'华文中宋') 225 | font_setting(doc,c,'黑体') 226 | 227 | doc.save('字体设置2.docx') 228 | 229 | ``` 230 | 231 | 我们很容易地看出来,字体设置1.py与字体设置2.py的区别在于是否为同一段落,同时字体设置2.py中自定义了一个函数。同学们可以在实际工作中看具体场景进行选择。 232 | 233 | ### 3.1.3插入图片与表格 234 | 235 | ```python 236 | #导入库 237 | from docx import Document 238 | from docx.shared import Inches 239 | 240 | #打开文档 241 | doc_1 = Document('周杰伦.docx') #上面脚本存储的文档 242 | #新增图片 243 | doc_1.add_picture('周杰伦.jpg',width=Inches(1.0), height=Inches(1.0)) 244 | 245 | # 创建3行1列表格 246 | table1 = doc_1.add_table(rows=2, cols=1) 247 | table1.style='Medium Grid 1 Accent 1' #表格样式很多种,如,Light Shading Accent 1等 248 | 249 | # 修改第2行第3列单元格的内容为营口 250 | table1.cell(0, 0).text = '营口' 251 | # 修改第3行第4列单元格的内容为人民 252 | table1.rows[1].cells[0].text = '人民' 253 | 254 | # 在表格底部新增一行 255 | row_cells = table1.add_row().cells 256 | # 新增行的第一列添加内容 257 | row_cells[0].text = '加油' 258 | 259 | doc_1.save('周杰伦为营口加油.docx') 260 | 261 | ``` 262 | 263 | ### 3.1.4设置页眉页脚 264 | 265 | 在python-docx包中则要使用节(section)中的页眉(header)和页脚(footer)对象来具体设置。 266 | 267 | ```python 268 | from docx import Document 269 | from docx.enum.text import WD_PARAGRAPH_ALIGNMENT 270 | 271 | document = Document() # 新建文档 272 | 273 | header = document.sections[0].header # 获取第一个节的页眉 274 | print('页眉中默认段落数:', len(header.paragraphs)) 275 | paragraph = header.paragraphs[0] # 获取页眉的第一个段落 276 | paragraph.add_run('这是第一节的页眉') # 添加页面内容 277 | footer = document.sections[0].footer # 获取第一个节的页脚 278 | paragraph = footer.paragraphs[0] # 获取页脚的第一个段落 279 | paragraph.add_run('这是第一节的页脚') # 添加页脚内容 280 | 281 | 282 | '''在docx文档中又添加了2个节,共计3个节,页面和页脚会显示了“与上一节相同”。 283 | 如果不使用上一节的内容和样式要将header.is_linked_to_previous的属性或footer.is_linked_to_previous的属性设置为False, 284 | 用于解除“链接上一节页眉”或者“链接上一节页脚”。''' 285 | document.add_section() # 添加一个新的节 286 | document.add_section() # 添加第3个节 287 | header = document.sections[1].header # 获取第2个节的页眉 288 | header.is_linked_to_previous = False # 不使用上节内容和样式 289 | 290 | #对齐设置 291 | header = document.sections[1].header # 获取第2个节的页眉 292 | header.is_linked_to_previous = False # 不使用上节内容和样式 293 | paragraph = header.paragraphs[0] 294 | paragraph.add_run('这是第二节的页眉') 295 | paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER # 设置页眉居中对齐 296 | document.sections[1].footer.is_linked_to_previous = False 297 | footer.paragraphs[0].add_run('这是第二节的页脚') # 添加第2节页脚内容 298 | footer.paragraphs[0].alignment = WD_PARAGRAPH_ALIGNMENT.CENTER # 设置第2节页脚居中对齐 299 | header = document.sections[2].header # 获取第3个节的页眉 300 | header.is_linked_to_previous = False # 不使用上节的内容和样式 301 | paragraph = header.paragraphs[0] # 获取页眉中的段落 302 | paragraph.add_run('这是第三节的页眉') 303 | paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT # 设置页眉右对齐 304 | document.sections[2].footer.is_linked_to_previous = False 305 | footer.paragraphs[0].add_run('这是第三节的页脚') # 添加第3节页脚内容 306 | footer.paragraphs[0].alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT # 设置第3节页脚右对齐 307 | document.save('页眉页脚1.docx') # 保存文档 308 | ``` 309 | 310 | 结果如下: 311 | 312 | ![](.\图片\3.2.png) 313 | 314 | ### 3.1.5代码延伸 315 | 316 | ```python 317 | '''Document的其他常用方法''' 318 | #添加无序列表 319 | document.add_paragraph('前面是圆点', style='List Bullet') 320 | #添加有序列表 321 | document.add_paragraph('前面是数字', style='List Number') 322 | #添加分页符 323 | document.add_page_break() 324 | #添加节 325 | document.add_section() 326 | #设置样式 327 | document.styles.add_style('textstyle', WD_STYLE_TYPE. PARAGRAPH) 328 | 329 | '''对齐设置''' 330 | from docx.enum.text import WD_ALIGN_PARAGRAPH 331 | #LEFT: 左对齐 332 | #CENTER: 文字居中 333 | #RIGHT: 右对齐 334 | #JUSTIFY: 文本两端对齐 335 | 336 | '''设置段落行距''' 337 | from docx.shared import Length 338 | # SINGLE :单倍行距(默认) 339 | #ONE_POINT_FIVE : 1.5倍行距 340 | # DOUBLE2 : 倍行距 341 | #AT_LEAST : 最小值 342 | #EXACTLY:固定值 343 | # MULTIPLE : 多倍行距 344 | 345 | paragraph.line_spacing_rule = WD_LINE_SPACING.EXACTLY #固定值 346 | paragraph_format.line_spacing = Pt(18) # 固定值18磅 347 | paragraph.line_spacing_rule = WD_LINE_SPACING.MULTIPLE #多倍行距 348 | paragraph_format.line_spacing = 1.75 # 1.75倍行间距 349 | 350 | '''设置字体属性''' 351 | from docx.shared import RGBColor,Pt 352 | #all_caps:全部大写字母 353 | #bold:加粗 354 | #color:字体颜色 355 | 356 | #double_strike:双删除线 357 | #hidden : 隐藏 358 | #imprint : 印记 359 | #italic : 斜体 360 | #name :字体 361 | #shadow :阴影 362 | #strike : 删除线 363 | #subscript :下标 364 | #superscript :上标 365 | #underline :下划线 366 | 367 | 368 | ``` 369 | 370 | ## 3.2 项目实践 371 | 372 | 项目难度:⭐ ⭐ ⭐ 373 | 374 | ### 3.2.1需求 375 | 376 | > 你是公司的行政人员,对合作伙伴进行邀请,参加公司的会议; 377 | > 378 | > 参会人名单如下: 379 | 380 | ![](.\图片\3.3.png) 381 | 382 | 拟定的邀请函样式如下: 383 | 384 | ![](.\图片\3.4.png) 385 | 386 | **根据参会人名单,利用python批量生成邀请函。** 387 | 388 | 389 | 390 | ### 3.2.2需求分析 391 | 392 | > 逻辑相对简单: 393 | > 394 | > - 获取 Excel 文件中每一行的信息,提取 参数;结合获取的参数设计邀请函样式并输出 395 | > - 设计word段落及字体等样式。 396 | 397 | ### 3.2.3代码 398 | 399 | ```python 400 | # 导入库 401 | from openpyxl import load_workbook 402 | from docx import Document 403 | from docx.enum.text import WD_PARAGRAPH_ALIGNMENT 404 | from docx.shared import RGBColor, Pt,Inches,Cm 405 | from docx.oxml.ns import qn 406 | 407 | 408 | path = r'D:\idea\cloud_analyse_game_sentiment\word自动化' 409 | # 路径为Excel 文件所在的位置,可按实际情况更改 410 | 411 | workbook = load_workbook(path + r'\excel到word.xlsx') 412 | sheet = workbook.active #默认的WorkSheet 413 | 414 | n = 0 #为了不遍历标题(excel的第一行) 415 | for row in sheet.rows: 416 | if n: 417 | company = row[0].value 418 | office = row[1].value 419 | name = row[2].value 420 | date = str(row[3].value).split()[0] 421 | print(company, office, name, date) 422 | 423 | 424 | doc = Document() 425 | heading_1 = '邀 请 函' 426 | paragraph_1 = doc.add_heading(heading_1, level=1) 427 | # 居中对齐 428 | paragraph_1.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER 429 | # 单独修改较大字号 430 | for run in paragraph_1.runs: 431 | run.font.size = Pt(17) 432 | 433 | greeting_word_1 = '尊敬的' 434 | greeting_word_2 = '公司' 435 | greeting_word_3 = ',您好:' 436 | paragraph_2 = doc.add_paragraph() 437 | 438 | paragraph_2.add_run(greeting_word_1) 439 | r_1 = paragraph_2.add_run(company) 440 | r_1.font.bold = True # 加粗 441 | r_1.font.underline = True #下划线 442 | 443 | paragraph_2.add_run(greeting_word_2) 444 | 445 | r_2 = paragraph_2.add_run(office) 446 | r_2.font.bold = True # 加粗 447 | r_2.font.underline = True #下划线 448 | 449 | r_3 = paragraph_2.add_run(name) 450 | r_3.font.bold = True # 加粗 451 | r_3.font.underline = True #下划线 452 | paragraph_2.add_run(greeting_word_3) 453 | 454 | paragraph_3 = doc.add_paragraph() 455 | paragraph_3.add_run('现诚挚的邀请您于2021年10月27日参加DataWhale主办的享受开源2050活动,地点在北京鸟巢,希望您届时莅临参加。') 456 | paragraph_3.paragraph_format.first_line_indent = Cm(0.75) 457 | paragraph_3.paragraph_format.alignment = WD_PARAGRAPH_ALIGNMENT.LEFT 458 | paragraph_3.paragraph_format.space_after = Inches(1.0) 459 | paragraph_3.paragraph_format.line_spacing = 1.5 460 | 461 | paragraph_4 = doc.add_paragraph() 462 | date_word_1 = '邀请时间:' 463 | paragraph_4.add_run(date_word_1) 464 | paragraph_4.alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT 465 | sign_date = "{}年{}月{}日".format(date.split('-')[0], date.split('-')[1], date.split('-')[2]) 466 | paragraph_4.add_run(sign_date).underline = True 467 | paragraph_4.alignment = WD_PARAGRAPH_ALIGNMENT.RIGHT 468 | 469 | #设置全文字体 470 | for paragraph in doc.paragraphs: 471 | for run in paragraph.runs: 472 | run.font.color.rgb = RGBColor(0, 0, 0) 473 | run.font.name = '楷体' 474 | r = run._element.rPr.rFonts 475 | r.set(qn('w:eastAsia'), '楷体') 476 | doc.save(path + "\{}-邀请函.docx".format(name)) 477 | n = n + 1 478 | ``` 479 | 480 | ## 3.3 后记 481 | 482 | > 本案例也可适用于批量生产固定格式的word,如工资条,通知单等,面对这种相似且重复的任务,python的自动化运行能大幅提升当前的工作效率。 483 | 484 | -------------------------------------------------------------------------------- /Task03-Python与Word和PDF/watermark.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task03-Python与Word和PDF/watermark.pdf -------------------------------------------------------------------------------- /Task03-Python与Word和PDF/易方达中小盘混合型证券投资基金2020年中期报告.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task03-Python与Word和PDF/易方达中小盘混合型证券投资基金2020年中期报告.pdf -------------------------------------------------------------------------------- /Task04-简单的python爬虫/爬虫参考结果/datawhale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task04-简单的python爬虫/爬虫参考结果/datawhale.png -------------------------------------------------------------------------------- /Task04-简单的python爬虫/爬虫参考结果/wuhan_ziru.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task04-简单的python爬虫/爬虫参考结果/wuhan_ziru.csv -------------------------------------------------------------------------------- /Task04-简单的python爬虫/爬虫参考结果/鲁迅文章.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task04-简单的python爬虫/爬虫参考结果/鲁迅文章.txt -------------------------------------------------------------------------------- /Task04-简单的python爬虫/简单的python爬虫.md: -------------------------------------------------------------------------------- 1 | # Task 04简单的Python爬虫 2 | - [Task 04简单的Python爬虫](#task-04简单的python爬虫) 3 | - [4.0 前言](#40-前言) 4 | - [4.1 Requests简介](#41-requests简介) 5 | - [4.1.1 访问百度](#411-访问百度) 6 | - [4.1.2 下载txt文件](#412-下载txt文件) 7 | - [4.1.3 下载图片](#413-下载图片) 8 | - [4.2 HTML解析和提取](#42-html解析和提取) 9 | - [4.3 BeautifulSoup简介](#43-beautifulsoup简介) 10 | - [4.4 实践项目1:自如公寓数据抓取](#44-实践项目1自如公寓数据抓取) 11 | - [4.5 实践项目2:36kr信息抓取与邮件发送](#45-实践项目236kr信息抓取与邮件发送) 12 | 13 | 14 | ## 4.0 前言 15 | 16 | 对于自动化办公而言,网络数据的批量获取完数据可以节约相当的时间,因此爬虫在自动化办公中占据了一个比较重要的位置。 17 | 18 | 因而本节针对爬虫项目进行一个介绍,力求最大程度还原实际的办公场景。 19 | 20 | ## 4.1 Requests简介 21 | 22 | Requests是一款目前非常流行的http请求库,使用python编写,能非常方便的对网页Requests进行爬取,也是爬虫最常用的发起请求第三方库。 23 | 24 | 安装方法: 25 | 26 | ``` python 27 | pip install requests 28 | 或者conda安装 29 | conda install requests 30 | ``` 31 | 32 | ``` python 33 | re.status_code 响应的HTTP状态码 34 | re.text 响应内容的字符串形式 35 | rs.content 响应内容的二进制形式 36 | rs.encoding 响应内容的编码 37 | ``` 38 | 39 | ### 4.1.1 访问百度 40 | 41 | 试一试对百度首页进行数据请求: 42 | 43 | 项目难度:⭐ 44 | 45 | ```python 46 | import requests 47 | # 发出http请求 48 | re=requests.get("https://www.baidu.com") 49 | # 查看响应状态 50 | print(re.status_code) 51 | #输出:200 52 | #200就是响应的状态码,表示请求成功 53 | #我们可以通过res.status_code的值来判断请求是否成功。 54 | ``` 55 | 56 | **res.text** 返回的是服务器响应内容的字符串形式,也就是文本内容 57 | 58 | ### 4.1.2 下载txt文件 59 | 60 | 例:用爬虫下载孔乙己的文章,网址是https://apiv3.shanbay.com/codetime/articles/mnvdu 61 | 62 | 我们打开这个网址 可以看到是鲁迅的文章 63 | 64 | 我们尝试着用爬虫保存文章的内容 65 | 66 | 项目难度:⭐ 67 | 68 | ```python 69 | import requests 70 | # 发出http请求 71 | re = requests.get('https://apiv3.shanbay.com/codetime/articles/mnvdu') 72 | # 查看响应状态 73 | print('网页的状态码为%s'%re.status_code) 74 | with open('鲁迅文章.txt', 'w') as file: 75 | # 将数据的字符串形式写入文件中 76 | print('正在爬取小说') 77 | file.write(re.text) 78 | ``` 79 | 80 | re.txt就是网页中的内容,将内容保存到txt文件中 81 | 82 | ### 4.1.3 下载图片 83 | 84 | **re.text用于文本内容的获取、下载 85 | re.content用于图片、视频、音频等内容的获取、下载** 86 | 87 | 项目难度:⭐⭐ 88 | 89 | ```python 90 | import requests 91 | # 发出http请求 92 | #下载图片 93 | res=requests.get('https://img-blog.csdnimg.cn/20210424184053989.PNG') 94 | # 以二进制写入的方式打开一个名为 info.jpg 的文件 95 | with open('datawhale.png','wb') as ff: 96 | # 将数据的二进制形式写入文件中 97 | print('爬取图片') 98 | ff.write(res.content) 99 | ``` 100 | 101 | **re.encoding** 爬取内容的编码形似,常见的编码方式有 ASCII、GBK、UTF-8 等。如果用和文件编码不同的方式去解码,我们就会得到一些乱码。 102 | 103 | ## 4.2 HTML解析和提取 104 | 105 | **浏览器工作原理:** 106 | 107 | 向浏览器中输入某个网址,浏览器回向服务器发出请求,然后服务器就会作出响应。其实,服务器返回给浏览器的这个结果就是HTML代码,浏览器会根据这个HTML代码将网页解析成平时我们看到的那样 108 | 109 | 比如我们来看看百度的html页面 110 | 111 | ```python 112 | import requests 113 | res=requests.get('https://baidu.com') 114 | print(res.text) 115 | ``` 116 | 117 | 将会看到很多带有标签的信息 118 | 119 | **HTML(Hyper Text Markup Language)**是一种超文本标记语言,是由一堆标记组成。 120 | 121 | 例如 122 | 123 | ```html 124 | 125 | 126 | 我的网页 127 | 128 | 129 | Hello,World 130 | 131 | 132 | ``` 133 | 134 | 上面即为一个最简单的html,我们所需要的信息就是夹在标签中 135 | 136 | 想对html有根据深入的了解,可以html菜鸟教程 137 | 138 | 139 | 140 | 那么我们如何解析html页面呢? 141 | 142 | ## 4.3 BeautifulSoup简介 143 | 144 | 我们一般会使用BeautifulSoup这个第三方库 145 | 146 | 安装方法: 147 | 148 | ``` python 149 | pip install bs4 150 | 或 151 | conda install bs4 152 | ``` 153 | 154 | 我们来解析豆瓣读书 Top250 155 | 156 | 它的网址是: 157 | 158 | 项目难度:⭐⭐ 159 | 160 | ```python 161 | import io 162 | import sys 163 | import requests 164 | from bs4 import BeautifulSoup 165 | ###运行出现乱码时可以修改编码方式 166 | #sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='gb18030') 167 | ### 168 | headers = { 169 | 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36' 170 | } 171 | res = requests.get('https://book.douban.com/top250', headers=headers) 172 | soup = BeautifulSoup(res.text, 'lxml') 173 | print(soup) 174 | ``` 175 | 176 | python 打印信息时会有限制 我们将打印的编码改成gb18030 177 | 178 | headers表示我们的请求网页的头,对于没有headers的请求可能会被服务器判定为爬虫而拒绝提供服务 179 | 180 | 通过 from bs4 import BeautifulSoup 语句导入 BeautifulSoup 181 | 182 | 然后使用 BeautifulSoup(res.text, lxmlr’) 语句将网页源代码的字符串形式解析成了 BeautifulSoup 对象 183 | 184 | 解析成了 BeautifulSoup 对象可以较为方便的提取我们需要的信息 185 | 186 | 那么如何提取信息呢? 187 | 188 | BeautifulSoup 为我们提供了一些方法 189 | 190 | **find()方法**和**find_all()方法**: 191 | 192 | - **find()** 返回符合条件的**首个**数据 193 | - **find_all()** 返回符合条件的**所有**数据 194 | 195 | ```python 196 | import io 197 | import sys 198 | import requests 199 | from bs4 import BeautifulSoup 200 | #如果出现了乱码报错,可以修改编码形式 201 | #sys.stdout = io.TextIOWrapper(sys.stdout.buffer,encoding='gb18030') 202 | # 203 | headers = { 204 | 'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Safari/537.36' 205 | } 206 | res = requests.get('https://book.douban.com/top250', headers=headers) 207 | soup = BeautifulSoup(res.text, 'lxml') 208 | print(soup.find('a')) 209 | # 210 | print(soup.find_all('a')) 211 | #返回一个列表 包含了所有的标签 212 | ``` 213 | 214 | 除了传入 HTML 标签名称外,BeautifulSoup 还支持其他的定位 215 | 216 | ```python 217 | # 定位div开头 同时id为'doubanapp-tip的标签 218 | soup.find('div', id='doubanapp-tip') 219 | # 定位a抬头 同时class为rating_nums的标签 220 | soup.find_all('span', class_='rating_nums') 221 | #class是python中定义类的关键字,因此用class_表示HTML中的class 222 | ``` 223 | 224 | HTML定位方法:https://www.cnblogs.com/bosslv/p/8992410.html 225 | 226 | 理论看百遍,不如上手一练 227 | 228 | ## 4.4 实践项目1:自如公寓数据抓取 229 | 230 | > 首先是先说一声抱歉,在课程设计时,没有想到自如公寓在价格上增加一定程度的反爬措施,因此自如公寓的价格在本节不讨论,在以后的课程中,我们会详细讲解相关的方法。 231 | > 232 | > 本节内容为作者原创的项目,整体爬取过程有4星的难度,建议读者跟着课程一步一步的来,如果有不明白的地方,可以在群里面与其他伙伴进行交流。 233 | > 234 | > 在输出本节内容时,请注明来源,Datawhale自动化办公课程,谢谢~ 235 | 236 | 日前 , 国务院办公厅印发《关于加快培育和发展住房租赁市场的若干意见》,你是某新媒体公司的一名员工,老板希望对武汉的租房情况进行深度调研与分析,你想调查自如公寓的数据情况。根据工作的安排,你调研的是自如公寓武汉房屋出租分析的任务。 237 | 238 | 项目难度:⭐⭐⭐⭐ 239 | 240 | 自如公寓官网:https://wh.ziroom.com/z/z/ 241 | 242 | 通过观察官网你发现 243 | 244 | 第1页的网页为:https://wh.ziroom.com/z/p1/ 245 | 246 | 第2页的网页为:https://wh.ziroom.com/z/p2/ 247 | 248 | 第3页的网页为:https://wh.ziroom.com/z/p3/ 249 | 250 | ... 251 | 252 | 第50页的网页为:https://wh.ziroom.com/z/p50/ 253 | 254 | 你继续观察,发现 255 | 256 | 房屋的信息网页为类似于:https://wh.ziroom.com/x/741955798.html 257 | 258 | 即:https://wh.ziroom.com/x/XXXX.html 259 | 260 | 因此你有了思路,通过访问自如公寓的网站,获取每个房间后面的数字号 然后通过数字号访问房屋的直接信息,然后抓取房屋的信息保存在excel中 261 | 262 | 于是你访问了房屋的网页: 263 | 264 | 通过观察房屋的网页,你发现是这些信息是你需要的 265 | 266 | 房屋的名称,房屋的面积,房屋的朝向,房屋的户型,房屋的位置,房屋的楼层,是否有电梯,房屋的年代,门锁情况,绿化情况 267 | 268 | 但是你遇到了困难,不知道这些信息的标签信息,不能用beautifulsoup对他们进行定位 269 | 270 | 通过百度查询,浏览器按F12时能进入源代码模式 或者 点击右键进入审查元素 271 | 272 | ![微信截图_20210510110008](.\图片\图5.1.png) 273 | 274 | 点击左上角的箭头,可以定位到元素的位置 275 | 276 | 方法掌握后你开始写代码了 277 | 278 | ```python 279 | import requests 280 | from bs4 import BeautifulSoup 281 | import random 282 | import time 283 | import csv 284 | ``` 285 | 286 | 写到这里的时候,你想到,我多次访问自如的官网,如果只用一个UA头岂不是很容易被反爬虫识别 287 | 288 | 你想到,我可以做很多个UA头,然后每次访问的时候可以随机选一个,想到这里,你直呼自己是个天才 289 | 290 | 于是,你到网上找到了很多UA头信息 291 | 292 | ```python 293 | #这里增加了很多user_agent 294 | #能一定程度能保护爬虫 295 | user_agent = [ 296 | "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50", 297 | "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50", 298 | "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0", 299 | "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; InfoPath.3; rv:11.0) like Gecko", 300 | "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)", 301 | "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)", 302 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)", 303 | "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)", 304 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1", 305 | "Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1", 306 | "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11", 307 | "Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11", 308 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11", 309 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)", 310 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)", 311 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)", 312 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; The World)", 313 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SE 2.X MetaSr 1.0; SE 2.X MetaSr 1.0; .NET CLR 2.0.50727; SE 2.X MetaSr 1.0)", 314 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)", 315 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Avant Browser)"] 316 | ``` 317 | 318 | 现在开始正式开始爬取数据了 319 | 320 | 房屋的名称,房屋的价格,房屋的面积,房屋的朝向,房屋的户型,房屋的位置,房屋的楼层,是否有电梯,房屋的年代,门锁情况,绿化情况 321 | 322 | 你思考爬取的信息应该保存到csv文件中,于是你导入了csv包 并简单的了解了CSV包的用法 323 | 324 | 第一步,是要获取房屋的数字标签 325 | 326 | 于是你打开了自如的官网,用浏览器的元素进行定位 327 | 328 | 发现房屋的信息标签都是这个 329 | 330 | < a href="dd//wh.ziroom.com/x/741955798.html" target="_blank"> 房屋名称< /a > 331 | 332 | 聪明的你,随手写下了这个代码,便能爬取自如前50页 333 | 334 | ```python 335 | def get_info(): 336 | csvheader=['名称','面积','朝向','户型','位置','楼层','是否有电梯','建成时间',' 门锁','绿化'] 337 | with open('wuhan_ziru.csv', 'a+', newline='') as csvfile: 338 | writer = csv.writer(csvfile) 339 | writer.writerow(csvheader) 340 | for i in range(1,50): #总共有50页 341 | print('正在爬取自如第%s页'%i) 342 | timelist=[1,2,3] 343 | print('有点累了,需要休息一下啦(¬㉨¬)') 344 | time.sleep(random.choice(timelist)) #休息1-3秒,防止给对方服务器过大的压力!!! 345 | url='https://wh.ziroom.com/z/p%s/'%i 346 | headers = {'User-Agent': random.choice(user_agent)} 347 | r = requests.get(url, headers=headers) 348 | r.encoding = r.apparent_encoding 349 | soup = BeautifulSoup(r.text, 'lxml') 350 | all_info = soup.find_all('div', class_='info-box') 351 | print('开始干活咯(๑>؂<๑)') 352 | for info in all_info: 353 | href = info.find('a') 354 | if href !=None: 355 | href='https:'+href['href'] 356 | try: 357 | print('正在爬取%s'%href) 358 | house_info=get_house_info(href) 359 | writer.writerow(house_info) 360 | except: 361 | print('出错啦,%s进不去啦( •̥́ ˍ •̀ू )'%href) 362 | ``` 363 | 364 | 通过研究发现了你需要定位的信息 通过标签头 h1 li span 和class的值对标签进行定位 365 | 366 | ``` 367 |

自如友家·电建地产盛世江城·4居室-05卧

368 | ---- 369 |
370 |
371 |
372 |
8.4㎡
373 |
使用面积
374 |
375 |
376 |
朝南
377 |
朝向
378 |
379 |
380 |
4室1厅
381 |
户型
382 |
383 |
384 |
385 | ---- 386 |
    387 |
  • 388 | 位置 389 | 小区距2号线长港路站步行约231米 390 |
  • 391 | 楼层6/43 392 | 393 |
  • 394 | 电梯 395 |
  • 396 |
  • 397 | 年代2016年建成 398 |
  • 399 |
  • 400 | 门锁智能门锁 401 |
  • 402 |
  • 403 | 绿化35% 404 |
  • 405 |
406 | ``` 407 | 408 | 通过对上面标签的研究你完成了所有的代码 409 | 410 | ```python 411 | import requests 412 | from bs4 import BeautifulSoup 413 | import random 414 | import time 415 | import csv 416 | 417 | #这里增加了很多user_agent 418 | #能一定程度能保护爬虫 419 | user_agent = [ 420 | "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50", 421 | "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50", 422 | "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0", 423 | "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; InfoPath.3; rv:11.0) like Gecko", 424 | "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)", 425 | "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)", 426 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)", 427 | "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)", 428 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1", 429 | "Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1", 430 | "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11", 431 | "Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11", 432 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11", 433 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)", 434 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)", 435 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)", 436 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; The World)", 437 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SE 2.X MetaSr 1.0; SE 2.X MetaSr 1.0; .NET CLR 2.0.50727; SE 2.X MetaSr 1.0)", 438 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)", 439 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Avant Browser)"] 440 | 441 | def get_info(): 442 | csvheader=['名称','面积','朝向','户型','位置','楼层','是否有电梯','建成时间',' 门锁','绿化'] 443 | with open('wuhan_ziru.csv', 'a+', newline='') as csvfile: 444 | writer = csv.writer(csvfile) 445 | writer.writerow(csvheader) 446 | for i in range(1,50): #总共有50页 447 | print('正在爬取自如第%s页'%i) 448 | timelist=[1,2,3] 449 | print('有点累了,需要休息一下啦(¬㉨¬)') 450 | time.sleep(random.choice(timelist)) #休息1-3秒,防止给对方服务器过大的压力!!! 451 | url='https://wh.ziroom.com/z/p%s/'%i 452 | headers = {'User-Agent': random.choice(user_agent)} 453 | r = requests.get(url, headers=headers) 454 | r.encoding = r.apparent_encoding 455 | soup = BeautifulSoup(r.text, 'lxml') 456 | all_info = soup.find_all('div', class_='info-box') 457 | print('开始干活咯(๑>؂<๑)') 458 | for info in all_info: 459 | href = info.find('a') 460 | if href !=None: 461 | href='https:'+href['href'] 462 | try: 463 | print('正在爬取%s'%href) 464 | house_info=get_house_info(href) 465 | writer.writerow(house_info) 466 | except: 467 | print('出错啦,%s进不去啦( •̥́ ˍ •̀ू )'%href) 468 | 469 | def get_house_info(href): 470 | #得到房屋的信息 471 | time.sleep(1) 472 | headers = {'User-Agent': random.choice(user_agent)} 473 | response = requests.get(url=href, headers=headers) 474 | response=response.content.decode('utf-8', 'ignore') 475 | soup = BeautifulSoup(response, 'lxml') 476 | name = soup.find('h1', class_='Z_name').text 477 | sinfo=soup.find('div', class_='Z_home_b clearfix').find_all('dd') 478 | area=sinfo[0].text 479 | orien=sinfo[1].text 480 | area_type=sinfo[2].text 481 | dinfo=soup.find('ul',class_='Z_home_o').find_all('li') 482 | location=dinfo[0].find('span',class_='va').text 483 | loucen=dinfo[1].find('span',class_='va').text 484 | dianti=dinfo[2].find('span',class_='va').text 485 | niandai=dinfo[3].find('span',class_='va').text 486 | mensuo=dinfo[4].find('span',class_='va').text 487 | lvhua=dinfo[5].find('span',class_='va').text 488 | ['名称','面积','朝向','户型','位置','楼层','是否有电梯','建成时间',' 门锁','绿化'] 489 | room_info=[name,area,orien,area_type,location,loucen,dianti,niandai,mensuo,lvhua] 490 | return room_info 491 | 492 | if __name__ == '__main__': 493 | get_info() 494 | ``` 495 | 496 | 运行完成后,会在文件夹中看到刚才爬取好的信息保存在wuhan_ziru.csv中 497 | 498 | ## 4.5 实践项目2:36kr信息抓取与邮件发送 499 | 500 | > 本节内容为作者原创的项目,课程难度为5星,建议读者跟着课程一步一步的来,如果有不明白的地方,可以在群里面与其他伙伴进行交流。 501 | > 502 | > 在输出本节内容时,请注明来源,Datawhale自动化办公课程,谢谢~ 503 | > 504 | > 如果没有多个邮箱,可以百度搜索临时邮箱进行实践学习 505 | 506 | 项目难度:⭐⭐⭐⭐⭐ 507 | 508 | 完成了上面的实践项目1后,你膨胀到不行,觉得自己太厉害了。通过前面的学习,你了解到使用python进行电子邮件的收发,突然有一天你想到,如果我用A账户进行发送,同时用B账户进行接受,在手机上安装一个邮件接受的软件,这样就能完成信息从pc端投送到移动端。 509 | 510 | 在这样的思想上,就可以对动态变化的信息进行监控,一旦信息触发了发送的条件,可以将信息通过邮件投送到手机上,从而让自己最快感知到。 511 | 512 | 具体路径是: 513 | 514 | python爬虫-->通过邮件A发送-->服务器--->通过邮件B接收 515 | 516 | 因此我们本节的内容就是爬取36kr的信息然后通过邮件发送 517 | 518 | 36kr官网: 519 | 520 | 通过python发送邮件需要获得pop3的授权码 521 | 522 | 具体获取方式可参考: 523 | 524 | 525 | 526 | 接下来就爬取36Kr的网站 527 | 528 | 通过观察我们发现 消息的标签为 529 | 530 | ```javascript 531 |
中国平安:推动新方正集团聚集医疗健康等核心业务发展 532 | ``` 533 | 534 | 因此我们爬取的代码为 535 | 536 | 需要注意的是,邮箱发送消息用的HTML的模式,而HTML模式下换行符号为 < br> 537 | 538 | ```python 539 | def main(): 540 | print('正在爬取数据') 541 | url = 'https://36kr.com/newsflashes' 542 | headers = {'User-Agent': random.choice(user_agent)} 543 | response = requests.get(url, headers=headers) 544 | response=response.content.decode('utf-8', 'ignore') 545 | soup = BeautifulSoup(response, 'lxml') 546 | news = soup.find_all('a', class_='item-title') 547 | news_list=[] 548 | for i in news: 549 | title=i.get_text() 550 | href='https://36kr.com'+i['href'] 551 | news_list.append(title+'
'+href) 552 | info='

'.join(news_list) 553 | ``` 554 | 555 | 接下来就是配置邮箱的发送信息 556 | 557 | ```python 558 | smtpserver = 'smtp.qq.com' 559 | 560 | # 发送邮箱用户名密码 561 | user = '' 562 | password = '' 563 | 564 | # 发送和接收邮箱 565 | sender = '' 566 | receive = '' 567 | 568 | def send_email(content): 569 | # 通过QQ邮箱发送 570 | title='36kr快讯' 571 | subject = title 572 | msg = MIMEText(content, 'html', 'utf-8') 573 | msg['Subject'] = Header(subject, 'utf-8') 574 | msg['From'] = sender 575 | msg['To'] = receive 576 | # SSL协议端口号要使用465 577 | smtp = smtplib.SMTP_SSL(smtpserver, 465) # 这里是服务器端口! 578 | # HELO 向服务器标识用户身份 579 | smtp.helo(smtpserver) 580 | # 服务器返回结果确认 581 | smtp.ehlo(smtpserver) 582 | # 登录邮箱服务器用户名和密码 583 | smtp.login(user, password) 584 | smtp.sendmail(sender, receive, msg.as_string()) 585 | smtp.quit() 586 | ``` 587 | 588 | 最后我们的整个代码文件为 589 | 590 | ```python 591 | import requests 592 | import random 593 | from bs4 import BeautifulSoup 594 | import smtplib # 发送邮件模块 595 | from email.mime.text import MIMEText # 定义邮件内容 596 | from email.header import Header # 定义邮件标题 597 | 598 | smtpserver = 'smtp.qq.com' 599 | 600 | # 发送邮箱用户名密码 601 | user = '' 602 | password = '' 603 | 604 | # 发送和接收邮箱 605 | sender = '' 606 | receive = '' 607 | 608 | user_agent = [ 609 | "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50", 610 | "Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50", 611 | "Mozilla/5.0 (Windows NT 10.0; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0", 612 | "Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.30729; .NET CLR 3.5.30729; InfoPath.3; rv:11.0) like Gecko", 613 | "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)", 614 | "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0)", 615 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)", 616 | "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)", 617 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv:2.0.1) Gecko/20100101 Firefox/4.0.1", 618 | "Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1", 619 | "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; en) Presto/2.8.131 Version/11.11", 620 | "Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11", 621 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_0) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11", 622 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Maxthon 2.0)", 623 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; TencentTraveler 4.0)", 624 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)", 625 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; The World)", 626 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SE 2.X MetaSr 1.0; SE 2.X MetaSr 1.0; .NET CLR 2.0.50727; SE 2.X MetaSr 1.0)", 627 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)", 628 | "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Avant Browser)"] 629 | 630 | def main(): 631 | print('正在爬取数据') 632 | url = 'https://36kr.com/newsflashes' 633 | headers = {'User-Agent': random.choice(user_agent)} 634 | response = requests.get(url, headers=headers) 635 | response=response.content.decode('utf-8', 'ignore') 636 | soup = BeautifulSoup(response, 'lxml') 637 | news = soup.find_all('a', class_='item-title') 638 | news_list=[] 639 | for i in news: 640 | title=i.get_text() 641 | href='https://36kr.com'+i['href'] 642 | news_list.append(title+'
'+href) 643 | info='

'.join(news_list) 644 | print('正在发送信息') 645 | send_email(info) 646 | 647 | def send_email(content): 648 | # 通过QQ邮箱发送 649 | title='36kr快讯' 650 | subject = title 651 | msg = MIMEText(content, 'html', 'utf-8') 652 | msg['Subject'] = Header(subject, 'utf-8') 653 | msg['From'] = sender 654 | msg['To'] = receive 655 | # SSL协议端口号要使用465 656 | smtp = smtplib.SMTP_SSL(smtpserver, 465) # 这里是服务器端口! 657 | # HELO 向服务器标识用户身份 658 | smtp.helo(smtpserver) 659 | # 服务器返回结果确认 660 | smtp.ehlo(smtpserver) 661 | # 登录邮箱服务器用户名和密码 662 | smtp.login(user, password) 663 | smtp.sendmail(sender, receive, msg.as_string()) 664 | smtp.quit() 665 | 666 | if __name__ == '__main__': 667 | main() 668 | ``` 669 | 670 | **Task5 END.** 671 | 672 | --- By: 牧小熊 673 | 674 | > 华中农业大学研究生,Datawhale成员, Datawhale优秀原创作者 675 | > 676 | > 知乎:https://www.zhihu.com/people/muxiaoxiong 677 | 678 | 关于Datawhale: Datawhale是一个专注于数据科学与AI领域的开源组织,汇集了众多领域院校和知名企业的优秀学习者,聚合了一群有开源精神和探索精神的团队成员。Datawhale 以“for the learner,和学习者一起成长”为愿景,鼓励真实地展现自我、开放包容、互信互助、敢于试错和勇于担当。同时 Datawhale 用开源的理念去探索开源内容、开源学习和开源方案,赋能人才培养,助力人才成长,建立起人与人,人与知识,人与企业和人与未来的联结。 本次数据挖掘路径学习,专题知识将在天池分享,详情可关注Datawhale: 679 | 680 | [![logo.png](https://camo.githubusercontent.com/8578ee173c78b587d5058439bbd0b98fa39c173def229a8c3d957e62aac0b649/68747470733a2f2f696d672d626c6f672e6373646e696d672e636e2f323032303039313330313032323639382e706e67237069635f63656e746572)](https://camo.githubusercontent.com/8578ee173c78b587d5058439bbd0b98fa39c173def229a8c3d957e62aac0b649/68747470733a2f2f696d672d626c6f672e6373646e696d672e636e2f323032303039313330313032323639382e706e67237069635f63656e746572) -------------------------------------------------------------------------------- /Task05-Python操作钉钉自动化/Python操作钉钉自动化.md: -------------------------------------------------------------------------------- 1 | # Task 05 Python操作钉钉自动化 2 | - [Task 05 Python操作钉钉自动化](#task-05-python操作钉钉自动化) 3 | - [1、创建钉钉群机器人](#1创建钉钉群机器人) 4 | - [2、使用自定义机器人实战](#2使用自定义机器人实战) 5 | - [①文本TEXT](#文本text) 6 | - [②链接LINK](#链接link) 7 | - [③markdown类型](#markdown类型) 8 | - [④整体跳转ActionCard类型](#整体跳转actioncard类型) 9 | - [⑤独立跳转ActionCard类型](#独立跳转actioncard类型) 10 | - [⑥FeedCard类型](#feedcard类型) 11 | 12 | 本文主要是对群聊消息的发送提供参考案例,实现对文字消息、图片消息、卡片消息等常规消息的发送,具体的,本文通过钉钉接口实现上述群聊消息的几大功能。更多详尽内容可参考:[钉钉机器人开发文档](https://open.dingtalk.com/document/robots/custom-robot-access) 13 | 14 | > 通过Python操作钉钉群聊机器人发送消息 15 | 16 | > 环境:python3.x 17 | 18 | > 使用模块:requests,json 19 | 20 | ## 1、创建钉钉群机器人 21 | 22 | 使用钉钉机器人发送群消息的第一步是需要创建一个群聊。步骤如下: 23 | 24 | 首先我们打开钉钉,随便拉2个人,创建一个群聊,点击群设置中的智能群助手。 25 | 26 | ![](./asset/01.png) 27 | 28 | 进入到机器人管理页面,点击添加机器人,进入机器人选择页面,这里选择自定义机器人。 29 | 30 | ![](./asset/02.png) 31 | 32 | 需要给机器人修改头像和名称,在安全设置里面,建议最好把自定义关键字也勾选上,比如我这里设置的是:定时推送,然后其他的可以默认,点击完成后在新的页面有一个webhook 33 | 34 | ![](./asset/03.png) 35 | 36 | ![](./asset/04.png) 37 | 38 | 这个webhook比较重要,后面要用到,一定要备份好,不要公布在外部网站上,泄露后有安全风险。到此,群聊机器人就创建完成了。 39 | 40 | 需要注意的有两个:一个机器人的webhook,一个是自定义关键字 41 | 42 | 43 | 44 | ## 2、使用自定义机器人实战 45 | 46 | 获取到Webhook地址后,用户可以向这个地址发起HTTP POST 请求,即可实现给该钉钉群发送消息。 47 | 48 | > 钉钉群聊机器人最新规定: 49 | > 50 | > - 发起POST请求时,必须将字符集编码设置成UTF-8。 51 | > - 每个机器人每分钟最多发送20条。消息发送太频繁会严重影响群成员的使用体验,大量发消息的场景 (譬如系统监控报警) 可以将这些信息进行整合,通过markdown消息以摘要的形式发送到群里。 52 | 53 | 目前支持发送的消息有5种,分别是:**文本 (text)、链接 (link)、markdown、ActionCard、FeedCard**。个人使用中比较常用的有两种:分别是文本和链接,企业使用的时候,对于ActionCard类型的也比较常用。 54 | 55 | 具体需要根据自己的场景进行选择,以便能达到最好的展示样式。 56 | 57 | 自定义机器人发送消息时,可以通过手机号码指定“被@人列表”。在“被@人列表”里面的人员收到该消息时,会有@消息提醒。免打扰会话仍然通知提醒,首屏出现“有人@你” 58 | 59 | ### ①文本TEXT 60 | 61 | 文本型的消息类型,具体代码如下: 62 | 63 | ``` 64 | { 65 | "at": { 66 | "atMobiles":[ 67 | "180xxxxxx" 68 | ], 69 | "atUserIds":[ 70 | "user123" 71 | ], 72 | "isAtAll": false 73 | }, 74 | "text": { 75 | "content":"我就是我, @XXX 是不一样的烟火" 76 | }, 77 | "msgtype":"text" 78 | } 79 | 80 | ``` 81 | 82 | 上述中涉及的参数类型分别如下: 83 | 84 | | **参数** | **参数类型** | **是否必填** | **说明** | 85 | | --------- | ------------ | ------------ | ------------------------------------------------------------ | 86 | | msgtype | String | 是 | 消息类型,此时固定为:text。 | 87 | | content | String | 是 | 消息内容。 | 88 | | atMobiles | Array | 否 | 被@人的手机号。**注意** 在content里添加@人的手机号,且只有在群内的成员才可被@,非群内成员手机号会被脱敏。 | 89 | | atUserIds | Array | 否 | 被@人的用户userid。**注意** 在content里添加@人的userid。 | 90 | | isAtAll | Boolean | 否 | 是否@所有人。 | 91 | 92 | 实战代码,可直接复制,替换自己的access_token即可运行 93 | 94 | ```python 95 | import requests 96 | import json 97 | import datetime 98 | 99 | def send_msg(token_dd, date_str, msg, at_all=False): 100 | """ 101 | 通过钉钉机器人发送内容 102 | @param date_str: 103 | @param msg: 104 | @param at_all: 105 | @return: 106 | """ 107 | url = 'https://oapi.dingtalk.com/robot/send?access_token=' + token_dd 108 | headers = {'Content-Type': 'application/json;charset=utf-8'} 109 | content_str = "{0}定时推送:\n\n{1}\n".format(date_str, msg) 110 | 111 | data = { 112 | "msgtype": "text", 113 | "text": { 114 | "content": content_str 115 | }, 116 | "at": { 117 | "isAtAll": at_all 118 | }, 119 | } 120 | res = requests.post(url, data=json.dumps(data), headers=headers) 121 | print(res.text) 122 | 123 | return res.text 124 | 125 | if __name__ == '__main__': 126 | token_dd = '你自己的webhook后面的access_token复制在此' 127 | note_str = "DataWhale自动化组队学习中..." 128 | date_str = datetime.datetime.now().strftime('%H:%M') 129 | send_msg(token_dd, date_str, note_str, True) 130 | 131 | ``` 132 | 133 | 具体效果如下: 134 | 135 | ![](./asset/05.png) 136 | 137 | ### ②链接LINK 138 | 139 | 链接型的消息类型,具体代码如下: 140 | 141 | ``` 142 | { 143 | "msgtype": "link", 144 | "link": { 145 | "text": "这个即将发布的新版本,创始人xx称它为红树林。而在此之前,每当面临重大升级,产品经理们都会取一个应景的代号,这一次,为什么是红树林", 146 | "title": "时代的火车向前开", 147 | "picUrl": "", 148 | "messageUrl": "https://www.dingtalk.com/s?__biz=MzA4NjMwMTA2Ng==&mid=2650316842&idx=1&sn=60da3ea2b29f1dcc43a7c8e4a7c97a16&scene=2&srcid=09189AnRJEdIiWVaKltFzNTw&from=timeline&isappinstalled=0&key=&ascene=2&uin=&devicetype=android-23&version=26031933&nettype=WIFI" 149 | } 150 | } 151 | ``` 152 | 153 | 上述中涉及的参数类型分别如下: 154 | 155 | | **参数** | **参数类型** | 是否必填 | **说明** | 156 | | ---------- | ------------ | -------- | ------------------------------------------------------------ | 157 | | msgtype | String | 是 | 消息类型,此时固定为:link。 | 158 | | title | String | 是 | 消息标题。 | 159 | | text | String | 是 | 消息内容。如果太长只会部分展示。 | 160 | | messageUrl | String | 是 | 点击消息跳转的URL,打开方式如下:移动端,在钉钉客户端内打开PC端默认侧边栏打开希望在外部浏览器打开,请参考[消息链接说明](https://open.dingtalk.com/document/app/message-link-description#section-7w8-4c2-9az) | 161 | | picUrl | String | 否 | 图片URL。 | 162 | 163 | 实战代码,可直接复制,替换自己的access_token即可运行 164 | 165 | ```python 166 | import requests 167 | import json 168 | import datetime 169 | 170 | def send_msg(token_dd, date_str): 171 | """ 172 | 通过钉钉机器人发送内容 173 | @param date_str: 174 | @return: 175 | """ 176 | url = 'https://oapi.dingtalk.com/robot/send?access_token=' + token_dd 177 | headers = {'Content-Type': 'application/json;charset=utf-8'} 178 | content_str = "{0}定时推送:\n\n{1}\n".format(date_str, "Datawhale和学习者一起成长") 179 | 180 | data = { 181 | "msgtype": "link", 182 | "link": { 183 | "title": content_str, 184 | "text": "Datawhale是一个专注于AI领域的开源组织,致力于构建一个纯粹的学习圈子,帮助学习者更好地成长。我们专注于机器学习,深度学习,编程和数学等AI领域内容的产出与学习。Datawhale发展于2018年12月6日。团队成员规模也在不断扩大,有来自双非院校的优秀同学,也有来自上交、武大、清华等名校的小伙伴,同时也有来自微软、字节、百度等企业的工程师。我们将一群有能力有想法的理想青年们集结在一起,共同改善学习的模式。在这里,我们没有标签。如果你是小白,我们将为你提供学习和改变的机会,如果你是优秀的学习者,我们将向你提供提升和发展的平台。", 185 | "picUrl": "https://camo.githubusercontent.com/442350f57282653c563b8c6181f561452b6258632dc79d8a5298b724a37a42f7/68747470733a2f2f747661312e73696e61696d672e636e2f6c617267652f65366339643234656c793168316f69646f617469716a3231313030753061626e2e6a7067", 186 | "messageUrl": "https://linklearner.com/datawhale-homepage/index.html#/article/1" 187 | } 188 | } 189 | res = requests.post(url, data=json.dumps(data), headers=headers) 190 | print(res.text) 191 | 192 | return res.text 193 | 194 | if __name__ == '__main__': 195 | token_dd = '你自己的webhook后面的access_token复制在此' 196 | note_str = "DataWhale自动化组队学习中..." 197 | date_str = datetime.datetime.now().strftime('%H:%M') 198 | send_msg(token_dd, date_str, note_str, True) 199 | ``` 200 | 201 | 具体效果如下(文中的卡片可以直接跳转到DataWhale官网): 202 | 203 | ![](./asset/06.png) 204 | 205 | 206 | 207 | ### ③markdown类型 208 | 209 | markdown的消息类型,具体代码如下: 210 | 211 | ```json 212 | { 213 | "msgtype": "markdown", 214 | "markdown": { 215 | "title":"杭州天气", 216 | "text": "#### 杭州天气 @150XXXXXXXX \n > 9度,西北风1级,空气良89,相对温度73%\n > ![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png)\n > ###### 10点20分发布 [天气](https://www.dingtalk.com) \n" 217 | }, 218 | "at": { 219 | "atMobiles": [ 220 | "150XXXXXXXX" 221 | ], 222 | "atUserIds": [ 223 | "user123" 224 | ], 225 | "isAtAll": false 226 | } 227 | } 228 | ``` 229 | 230 | 上述中涉及的参数类型分别如下: 231 | 232 | | **参数** | **类型** | 是否必填 | **说明** | 233 | | --------- | -------- | -------- | ------------------------------------------------------------ | 234 | | msgtype | String | 是 | 消息类型,此时固定为:markdown。 | 235 | | title | String | 是 | 首屏会话透出的展示内容。 | 236 | | text | String | 是 | markdown格式的消息。 | 237 | | atMobiles | Array | 否 | 被@人的手机号。**注意** 在text内容里要有@人的手机号,只有在群内的成员才可被@,非群内成员手机号会被脱敏。 | 238 | | atUserIds | Array | 否 | 被@人的用户userid。**注意** 在content里添加@人的userid。 | 239 | | isAtAll | Boolean | 否 | 是否@所有人。 | 240 | 241 | 实战代码,可直接复制,替换自己的access_token即可运行 242 | 243 | ```python 244 | import requests 245 | import json 246 | import datetime 247 | 248 | def send_msg(token_dd, date_str, at_all): 249 | """ 250 | 通过钉钉机器人发送内容 251 | @param date_str: 252 | @param at_all: 253 | @return: 254 | """ 255 | url = 'https://oapi.dingtalk.com/robot/send?access_token=' + token_dd 256 | headers = {'Content-Type': 'application/json;charset=utf-8'} 257 | title_str = "{0}定时推送:\n\n{1}\n".format(date_str, "Datawhale和学习者一起成长") 258 | 259 | data = { 260 | "msgtype": "markdown", 261 | "markdown": { 262 | "title": title_str, 263 | "text": "### Datawhale \n > Datawhale是一个专注于AI领域的开源组织,致力于构建一个纯粹的学习圈子,帮助学习者更好地成长。我们专注于机器学习,深度学习,编程和数学等AI领域内容的产出与学习。\n\n > Datawhale发展于**2018年12月6日**。团队成员规模也在不断扩大,有来自双非院校的优秀同学,也有来自上交、武大、清华等名校的小伙伴,同时也有来自微软、字节、百度等企业的工程师。\n\n > 我们将一群有能力有想法的理想青年们集结在一起,共同改善学习的模式。**在这里,我们没有标签**。如果你是小白,我们将为你提供学习和改变的机会,如果你是优秀的学习者,我们将向你提供提升和发展的平台。", 264 | }, 265 | "at": { 266 | "isAtAll": at_all 267 | } 268 | } 269 | 270 | res = requests.post(url, data=json.dumps(data), headers=headers) 271 | print(res.text) 272 | 273 | return res.text 274 | 275 | 276 | if __name__ == '__main__': 277 | token_dd = '你自己的webhook后面的access_token复制在此' 278 | date_str = datetime.datetime.now().strftime('%H:%M') 279 | send_msg(token_dd, date_str, at_all=True) 280 | ``` 281 | 282 | 具体效果如下: 283 | 284 | ![](./asset/07.png) 285 | 286 | ### ④整体跳转ActionCard类型 287 | 288 | 整体跳转ActionCard的消息类型,具体代码如下: 289 | 290 | ``` 291 | { 292 | "actionCard": { 293 | "title": "乔布斯 20 年前想打造一间苹果咖啡厅,而它正是 Apple Store 的前身", 294 | "text": "![screenshot](https://gw.alicdn.com/tfs/TB1ut3xxbsrBKNjSZFpXXcXhFXa-846-786.png) 295 | ### 乔布斯 20 年前想打造的苹果咖啡厅 296 | Apple Store 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划", 297 | "btnOrientation": "0", 298 | "singleTitle" : "阅读全文", 299 | "singleURL" : "https://www.dingtalk.com/" 300 | }, 301 | "msgtype": "actionCard" 302 | } 303 | ``` 304 | 305 | 上述中涉及的参数类型分别如下: 306 | 307 | | **参数** | **类型** | **是否必填** | **说明** | 308 | | -------------- | -------- | ------------ | ------------------------------------------------------------ | 309 | | msgtype | String | 是 | 消息类型,此时固定为:actionCard。 | 310 | | title | String | 是 | 首屏会话透出的展示内容。 | 311 | | text | String | 是 | markdown格式的消息。 | 312 | | singleTitle | String | 是 | 单个按钮的标题。**注意** 设置此项和singleURL后,btns无效。 | 313 | | singleURL | String | 是 | 点击消息跳转的URL,打开方式如下:移动端,在钉钉客户端内打开PC端默认侧边栏打开希望在外部浏览器打开,请参考[消息链接说明](https://open.dingtalk.com/document/app/message-link-description#section-7w8-4c2-9az) | 314 | | btnOrientation | String | 否 | 0:按钮竖直排列1:按钮横向排列 | 315 | 316 | 实战代码,可直接复制,替换自己的access_token即可运行 317 | 318 | ``` 319 | import requests 320 | import json 321 | import datetime 322 | 323 | def send_msg(token_dd, date_str): 324 | """ 325 | 通过钉钉机器人发送内容 326 | @param date_str: 327 | @return: 328 | """ 329 | url = 'https://oapi.dingtalk.com/robot/send?access_token=' + token_dd 330 | headers = {'Content-Type': 'application/json;charset=utf-8'} 331 | title_str = "{0}定时推送:\n\n{1}\n".format(date_str, "Datawhale和学习者一起成长") 332 | 333 | data = { 334 | "msgtype": "actionCard", 335 | "actionCard": { 336 | "title": title_str, 337 | "text": "### Datawhale \n > Datawhale是一个专注于AI领域的开源组织,致力于构建一个纯粹的学习圈子,帮助学习者更好地成长。我们专注于机器学习,深度学习,编程和数学等AI领域内容的产出与学习...", 338 | "btnOrientation": "0", 339 | "singleTitle": "阅读全文", 340 | "singleURL": "https://linklearner.com/datawhale-homepage/index.html#/article/1" 341 | } 342 | } 343 | 344 | res = requests.post(url, data=json.dumps(data), headers=headers) 345 | print(res.text) 346 | 347 | return res.text 348 | 349 | if __name__ == '__main__': 350 | token_dd = '你自己的webhook后面的access_token复制在此' 351 | date_str = datetime.datetime.now().strftime('%H:%M') 352 | send_msg(token_dd, date_str) 353 | ``` 354 | 355 | 具体效果如下: 356 | 357 | ![](./asset/08.png) 358 | 359 | ### ⑤独立跳转ActionCard类型 360 | 361 | 独立跳转ActionCard的消息类型,具体代码如下: 362 | 363 | ``` 364 | { 365 | "msgtype": "actionCard", 366 | "actionCard": { 367 | "title": "我 20 年前想打造一间苹果咖啡厅,而它正是 Apple Store 的前身", 368 | "text": "![screenshot](https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png) \n\n #### 乔布斯 20 年前想打造的苹果咖啡厅 \n\n Apple Store 的设计正从原来满满的科技感走向生活化,而其生活化的走向其实可以追溯到 20 年前苹果一个建立咖啡馆的计划", 369 | "btnOrientation": "0", 370 | "btns": [ 371 | { 372 | "title": "内容不错", 373 | "actionURL": "https://www.dingtalk.com/" 374 | }, 375 | { 376 | "title": "不感兴趣", 377 | "actionURL": "https://www.dingtalk.com/" 378 | } 379 | ] 380 | } 381 | } 382 | ``` 383 | 384 | 上述中涉及的参数类型分别如下: 385 | 386 | | **参数** | **类型** | 是否必填 | 说明 | 387 | | -------------- | -------- | -------- | ------------------------------------------------------------ | 388 | | msgtype | String | 是 | 此消息类型为固定actionCard。 | 389 | | title | String | 是 | 首屏会话透出的展示内容。 | 390 | | text | String | 是 | markdown格式的消息。 | 391 | | btns | Array | 是 | 按钮。 | 392 | | title | String | 是 | 按钮标题。 | 393 | | actionURL | String | 是 | 点击按钮触发的URL,打开方式如下:移动端,在钉钉客户端内打开PC端默认侧边栏打开希望在外部浏览器打开,请参考[消息链接说明](https://open.dingtalk.com/document/app/message-link-description#section-7w8-4c2-9az) | 394 | | btnOrientation | String | 否 | 0:按钮竖直排列1:按钮横向排列 | 395 | 396 | 实战代码,可直接复制,替换自己的access_token即可运行 397 | 398 | ``` 399 | import requests 400 | import json 401 | import datetime 402 | 403 | def send_msg(token_dd, date_str): 404 | """ 405 | 通过钉钉机器人发送内容 406 | @param date_str: 407 | @return: 408 | """ 409 | url = 'https://oapi.dingtalk.com/robot/send?access_token=' + token_dd 410 | headers = {'Content-Type': 'application/json;charset=utf-8'} 411 | title_str = "{0}定时推送:\n\n{1}\n".format(date_str, "Datawhale和学习者一起成长") 412 | 413 | data = { 414 | "msgtype": "actionCard", 415 | "actionCard": { 416 | "text": "### Datawhale \n > Datawhale是一个专注于AI领域的开源组织,致力于构建一个纯粹的学习圈子,帮助学习者更好地成长。我们专注于机器学习,深度学习,编程和数学等AI领域内容的产出与学习...", 417 | "title": title_str, 418 | "btnOrientation": "0", 419 | "btns": [ 420 | { 421 | "title": "阅读全文1", 422 | "actionURL": "https://linklearner.com/datawhale-homepage/index.html#/article/1" 423 | }, 424 | { 425 | "title": "阅读全文2", 426 | "actionURL": "https://linklearner.com/datawhale-homepage/index.html#/article/1" 427 | } 428 | ] 429 | } 430 | } 431 | 432 | res = requests.post(url, data=json.dumps(data), headers=headers) 433 | print(res.text) 434 | 435 | return res.text 436 | 437 | if __name__ == '__main__': 438 | token_dd = '你自己的webhook后面的access_token复制在此' 439 | date_str = datetime.datetime.now().strftime('%H:%M') 440 | send_msg(token_dd, date_str) 441 | ``` 442 | 443 | 444 | 445 | 具体效果如下: 446 | 447 | ![](./asset/09.png) 448 | 449 | 450 | 451 | ### ⑥FeedCard类型 452 | 453 | FeedCard的消息类型,具体代码如下: 454 | 455 | ``` 456 | { 457 | "msgtype":"feedCard", 458 | "feedCard": { 459 | "links": [ 460 | { 461 | "title": "时代的火车向前开1", 462 | "messageURL": "https://www.dingtalk.com/", 463 | "picURL": "https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png" 464 | }, 465 | { 466 | "title": "时代的火车向前开2", 467 | "messageURL": "https://www.dingtalk.com/", 468 | "picURL": "https://img.alicdn.com/tfs/TB1NwmBEL9TBuNjy1zbXXXpepXa-2400-1218.png" 469 | } 470 | ] 471 | } 472 | } 473 | ``` 474 | 475 | 上述中涉及的参数类型分别如下: 476 | 477 | | **参数** | **类型** | 是否必填 | **说明** | 478 | | ---------- | -------- | -------- | ------------------------------------------------------------ | 479 | | msgtype | String | 是 | 此消息类型为固定feedCard。 | 480 | | title | String | 是 | 单条信息文本。 | 481 | | messageURL | String | 是 | 点击单条信息到跳转链接。**说明** PC端跳转目标页面的方式,参考[消息链接在PC端侧边栏或者外部浏览器打开](https://open.dingtalk.com/document/app/message-link-description#section-7w8-4c2-9az)。 | 482 | | picURL | String | 是 | 单条信息后面图片的URL。 | 483 | 484 | 实战代码,可直接复制,替换自己的access_token即可运行 485 | 486 | ``` 487 | import requests 488 | import json 489 | import datetime 490 | 491 | def send_msg(token_dd): 492 | """ 493 | 通过钉钉机器人发送内容 494 | """ 495 | url = 'https://oapi.dingtalk.com/robot/send?access_token=' + token_dd 496 | headers = {'Content-Type': 'application/json;charset=utf-8'} 497 | title_str = "定时推送:\n\n{0}\n".format("Datawhale和学习者一起成长1") 498 | 499 | data = { 500 | "msgtype": "feedCard", 501 | "feedCard": { 502 | "links": [{ 503 | "title": title_str, 504 | "messageURL": "https://linklearner.com/datawhale-homepage/index.html#/article/1", 505 | "picURL": "https://camo.githubusercontent.com/442350f57282653c563b8c6181f561452b6258632dc79d8a5298b724a37a42f7/68747470733a2f2f747661312e73696e61696d672e636e2f6c617267652f65366339643234656c793168316f69646f617469716a3231313030753061626e2e6a7067" 506 | }, { 507 | "title": "Datawhale和学习者一起成长2", 508 | "messageURL": "https://linklearner.com/datawhale-homepage/index.html#/article/1", 509 | "picURL": "https://camo.githubusercontent.com/442350f57282653c563b8c6181f561452b6258632dc79d8a5298b724a37a42f7/68747470733a2f2f747661312e73696e61696d672e636e2f6c617267652f65366339643234656c793168316f69646f617469716a3231313030753061626e2e6a7067" 510 | } 511 | ] 512 | } 513 | } 514 | 515 | res = requests.post(url, data=json.dumps(data), headers=headers) 516 | print(res.text) 517 | 518 | return res.text 519 | 520 | if __name__ == '__main__': 521 | token_dd = '你自己的webhook后面的access_token复制在此' 522 | send_msg(token_dd) 523 | ``` 524 | 525 | 具体效果如下: 526 | 527 | ![](./asset/10.png) 528 | 529 | 530 | 531 | 以上就是本小节的所有内容。 532 | 533 | Task04 END 534 | 535 | > By: xiaoyi 536 | > 537 | > Datawhale成员,数据分析从业者 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | -------------------------------------------------------------------------------- /Task05-Python操作钉钉自动化/asset/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task05-Python操作钉钉自动化/asset/01.png -------------------------------------------------------------------------------- /Task05-Python操作钉钉自动化/asset/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task05-Python操作钉钉自动化/asset/02.png -------------------------------------------------------------------------------- /Task05-Python操作钉钉自动化/asset/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task05-Python操作钉钉自动化/asset/03.png -------------------------------------------------------------------------------- /Task05-Python操作钉钉自动化/asset/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task05-Python操作钉钉自动化/asset/04.png -------------------------------------------------------------------------------- /Task05-Python操作钉钉自动化/asset/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task05-Python操作钉钉自动化/asset/05.png -------------------------------------------------------------------------------- /Task05-Python操作钉钉自动化/asset/06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task05-Python操作钉钉自动化/asset/06.png -------------------------------------------------------------------------------- /Task05-Python操作钉钉自动化/asset/07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task05-Python操作钉钉自动化/asset/07.png -------------------------------------------------------------------------------- /Task05-Python操作钉钉自动化/asset/08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task05-Python操作钉钉自动化/asset/08.png -------------------------------------------------------------------------------- /Task05-Python操作钉钉自动化/asset/09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task05-Python操作钉钉自动化/asset/09.png -------------------------------------------------------------------------------- /Task05-Python操作钉钉自动化/asset/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task05-Python操作钉钉自动化/asset/10.png -------------------------------------------------------------------------------- /Task06-其它推荐软件和网页/asset/01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task06-其它推荐软件和网页/asset/01.png -------------------------------------------------------------------------------- /Task06-其它推荐软件和网页/asset/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task06-其它推荐软件和网页/asset/02.png -------------------------------------------------------------------------------- /Task06-其它推荐软件和网页/asset/03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task06-其它推荐软件和网页/asset/03.png -------------------------------------------------------------------------------- /Task06-其它推荐软件和网页/asset/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task06-其它推荐软件和网页/asset/04.png -------------------------------------------------------------------------------- /Task06-其它推荐软件和网页/asset/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task06-其它推荐软件和网页/asset/05.png -------------------------------------------------------------------------------- /Task06-其它推荐软件和网页/asset/06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task06-其它推荐软件和网页/asset/06.png -------------------------------------------------------------------------------- /Task06-其它推荐软件和网页/asset/Desktopcal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task06-其它推荐软件和网页/asset/Desktopcal.png -------------------------------------------------------------------------------- /Task06-其它推荐软件和网页/asset/clippingmagic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task06-其它推荐软件和网页/asset/clippingmagic.png -------------------------------------------------------------------------------- /Task06-其它推荐软件和网页/asset/geek.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/Task06-其它推荐软件和网页/asset/geek.png -------------------------------------------------------------------------------- /Task06-其它推荐软件和网页/其它优秀的小工具.md: -------------------------------------------------------------------------------- 1 | # Task 06 其它优秀的小工具 2 | 3 | > 在平时的办公中,可能有一些优秀的在线网站或者小工具能立马解决我们相关的问题,从而提升工作效率,在本节中推荐的在线网站和小工具都是在我实际工作中发现的优秀网站或工具,当然如果你有推荐的网站或工具,请联系我,让本项目持续更新... 4 | > 5 | > 最近更新时间:2022-05-31 6 | 7 | - [Task 06 其它优秀的小工具](#task-06-其它优秀的小工具) 8 | - [在线网站类](#在线网站类) 9 | - [1.在线格式转化](#1在线格式转化) 10 | - [2.PDF24 Tools](#2pdf24-tools) 11 | - [3.I❤PDF](#3ipdf) 12 | - [4.bigjpg](#4bigjpg) 13 | - [5.magiceraser](#5magiceraser) 14 | - [6.clippingmagic](#6clippingmagic) 15 | - [软件类](#软件类) 16 | - [1.Everything](#1everything) 17 | - [2.Geek uninstaller](#2geek-uninstaller) 18 | - [3.桌面日历](#3桌面日历) 19 | 20 | ## 在线网站类 21 | 22 | ### 1.在线格式转化 23 | 24 | Link:https://www.alltoall.net/ 25 | 26 | ![01](.\asset\01.png) 27 | 28 | 非常经典的在线格式转化网站,支持多种不同的格式进行互相转化,特别是PDF文件转成其它格式相当好用。 29 | 30 | ### 2.PDF24 Tools 31 | 32 | Link:https://tools.pdf24.org/zh/ 33 | 34 | ![02](.\asset\02.png) 35 | 36 | 非常好用的PDF在线工具,特别是PDF压缩功能,对于有文件大小限制的填表网站简直就是神器。 37 | 38 | ### 3.I❤PDF 39 | 40 | Link:https://www.ilovepdf.com/zh-cn 41 | 42 | ![03](.\asset\03.png) 43 | 44 | PDF工具合集包,能够满足PDF的各种需求 45 | 46 | ### 4.bigjpg 47 | 48 | Link:https://bigjpg.com/ 49 | 50 | ![04](.\asset\04.png) 51 | 52 | 基于深度学习方法的图片放大工具,免费版可将图片放大 2~4 倍,可选择不同程度的图片降噪 53 | 54 | ### 5.magiceraser 55 | 56 | Link:https://www.magiceraser.io/ 57 | 58 | ![05](.\asset\05.png) 59 | 60 | 在线的P图网站,能够删除图片中不需要的部分 61 | 62 | ### 6.clippingmagic 63 | 64 | Link:https://zh.clippingmagic.com/ 65 | 66 | ![clippingmagic](.\asset\clippingmagic.png) 67 | 68 | 在线抠图网站,能扣掉背景图,非常方便 69 | 70 | ## 软件类 71 | 72 | ### 1.Everything 73 | 74 | 下载地址:https://www.voidtools.com/zh-cn/ 75 | 76 | ![06](.\asset\06.png) 77 | 78 | 快速搜索电脑中的各种文件及文件夹,同时还支持正则表达式的匹配 79 | 80 | ### 2.Geek uninstaller 81 | 82 | Link:https://geekuninstaller.com/ 83 | 84 | ![geek](.\asset\geek.png) 85 | 86 | 非常好用的软件卸载工具,能同时卸载软件并删除注册表 87 | 88 | ### 3.桌面日历 89 | 90 | Link:http://chs.desktopcal.com/chs/ 91 | 92 | ![Desktopcal](.\asset\Desktopcal.png) 93 | 94 | 可以直接将需要做的事情写在日历里面,非常方便 -------------------------------------------------------------------------------- /自动化办公.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/datawhalechina/office-automation/cabf0171cefe33aa53c547cc40e9910f752dbf06/自动化办公.pptx --------------------------------------------------------------------------------