Loading... # GitHub 源码&使用说明: [PDF tools by PyPDF2][1] **更新:思路不变,但后来调整了代码,已更新至GitHub。** # 0 需求 有时候我们需要对PDF进行分割、合并、旋转等操作。当数量较小时,现有软件如PDF Expert可以满足需求。但是如果数量较多,比如通过爬虫得到的,常规软件很难高效处理。 # 1 PyPDF简介 以上需求均可用Python中的PyPDF进行处理。 安装PyPDF: pip install pypdf2 PyPDF的用法可参照:[利用python处理pdf][2] # 2 用PyPDF编写PDF合并程序 PyPDF的合并操作参考上面的资料很容易学会。不过为了使程序具有一定的通用性,需要解决以下问题: ## 2.1 读取文件列表 `os`模块提供了`walk`和`listdir`两种方法获取指定目录下的文件列表。前者返回三个值,依次是当前目录路径、当前路径下所有子目录、当前路径下所有非目录子文件。它会遍历指定目录下**所有**子文件夹和子文件夹中的**所有**文件。 而在这个程序中,我们一般用到的是后者,它返回一个list,只会列出指定路径下的所有文件和文件夹。 files = os.listdir(self.DIR_PATH) ## 2.2 按照一定顺序遍历文件 文件列表中的文件的文件名中可能有表示其序号的数字(或者其它方便编程获得的顺序特征)。仅仅通过`os.listdir`获得的列表的顺序不一定是我们想要的,这就需要用一种方法按序遍历。 于是解决方案分为两部分,从文件名提取序号和按序遍历。 ### 2.2.1 正则表达式提取序号/特征 如果结合`lambda`表达式采用2.2.2.1中的list排序方法,这一部分是不需要的。 Python中用re模块可以实现正则表达式匹配。我这次需要合并的PDF的文件名为: xxx(中文+标点字符串)xxx_{num1}-{num2}.pdf `{num1}`和`{num2}`分别代表两个数字。我想按照`{num1}`的顺序遍历文件。于是需要对文件名依次应用2个正则表达式获取`{num1}`。以下代码依次定义了包含这2个正则表达式的`list`,然后把它们编译为`pattern`对象。(具体细节参考`Python`的`re`模块详解。) # the list of regular expressions RE_LIST = [ '-{1,1}\d+', '\d+' ] # patterns that will be generated according to RE_LIST self.PATTERNS = [] # compile regular expressions to patterns for regexp in RE_LIST: self.PATTERNS.append(re.compile(regexp)) 定义一个方法获得`index` def __get_index(self, name): original_name = name # perform the regular expression matching sequentially for regpat in self.PATTERNS: name = regpat.search(name) if name is None: # ignore the unexpected file print(' find an invalid file: ', original_name) return self.INF_VALUE else: name = name.group() return int(name) 其中,不是我们需要合并的PDF文件,如`.DS_Store`,它的名字无法匹配成功,`search`方法会返回`None`,此时该方法返回定义好的`self.INF_VALUE`。 ### 2.2.2 按序遍历 解决方法有两种,我这次用的是第二种。 #### 2.2.2.1 list排序 利用`list`的`sort`方法可以对列表排序后再遍历以达到目的。如果文件名中的序号比较容易获得,比如在末尾,可以结合`lambda`表达式使用`sort`方法。例如:[参考资料链接][3] #### 2.2.2.2 构建字典(映射) 通过构建一个从序号`index`到文件名的字典`self.map`(详见代码中的`__construct_dict`方法),然后遍历`index`得到文件名即可实现按序遍历文件名。 for i in range(self.START_INDEX, end): try: # support for the discontinuous index f_name = self.map[i] except KeyError as err_key: # when the key doesn't exists print('Can\'t find key: ', err_key, ' , ignored.') continue 这里引入了异常处理,以便**支持不连续的`index`**。比如1,3,5,7……从1开始遍历,偶数(如2)不是字典中的`key`,便会引发`KeyError`异常,然后`continue`跳过此`index`继续遍历。 ### 2.2.3 合并PDF 终于到了合并PDF的环节!将合并PDF的代码放入遍历文件名的循环中即可。这里有三种方式可以合并,调用的是`PyPDF2`中的三种不同的方法。这三种不同的实现方法我写成了三个不同的方法`work1`,`work2`,`work3`。使用时调用其中一个即可(改变调用实例`work`方法中的参数即可切换方法)。 三种方法的不同可以在代码中看出来。在性能上,用PyCharm运行,合并428个文件(每个文件1页,大小在4KB~496KB间分布,加上一个1.7MB的封面)时,第1、3种方法耗时约为2.7s,而直接调用官方`PdfFileMerger`类的第2种方法反而耗时长,约5s左右。 用终端运行时,第2种方法中途会报错(`OSError`)。另两种方法运行正常,但是运行速度是原来的2倍多。 ### 3 总结 这个小程序涉及到的内容: `list`、`dictionary`的基本使用; `os`模块操作文件; `re`模块正则匹配; `try... except...`处理异常; `PyPDF2`模块处理PDF; …… ---------- 其它(部分)参考资料: [PyPDF2 Documentation][4] [python正则表达式从字符串中提取数字][5] [Python3 正则表达式中group()方法获得匹配结果][6] [1]: https://github.com/Co1lin/PDF_Tools [2]: https://blog.csdn.net/qq_18055167/article/details/89811725 [3]: https://blog.csdn.net/qq_22227123/article/details/79903116 [4]: https://pythonhosted.org/PyPDF2/index.html [5]: https://blog.csdn.net/u010412858/article/details/83062200 [6]: https://blog.csdn.net/m0_37360684/article/details/84139047 Last modification:May 2, 2020 © Allow specification reprint Support Appreciate the author Like 0 如果觉得我的文章对你有用,请随意赞赏