在我看来,PyPDF2 的当前版本(撰写本文时为 1.19)存在一些与 Python 3 兼容性有关的错误,这就是导致这两条错误消息的原因。GitHub 上 PyPDF2 的更改日志表明 Python 3 支持是在 1.16 版中添加的,该版本仅在 3 1/2 个月前发布,因此可能尚未报告或修复此错误。GitHub 还显示该项目有一个专门用于支持 Python 3.3 的分支,目前尚未合并回主分支。
这两个错误都发生在 PyPDF2 模块的 pdf.py 文件中。这是正在发生的事情:
PyPDF2 模块创建一些额外的字节作为填充并将其与您的密码连接起来。如果 Python 版本小于 3,则将填充创建为字符串文字。如果版本为 3 或更高,则填充使用“latin-1”编码进行编码。在 Python 3 中,这意味着填充是一个字节对象,并且将其与字符串对象(您的密码)连接会产生您看到的 TypeError。在 Python 2 下,连接会起作用,因为两个对象都是相同的类型。
当您使用“utf-8”对密码进行编码时,您可以解决该问题,因为在这种情况下密码和填充都是字节对象。但是,您最终会在模块的后面遇到第二个错误。pdf.py 文件创建并使用一个变量“keylen”,如下所示:
keylen = 128 / 8
... # later on in the code...
key = md5_hash[:keylen]
除法运算符在 Python 2.2 中发生了变化,从 Python 3 开始改变了它的默认行为。简而言之,“/”在 Python 2 中表示地板除法并返回一个 int,但在 Python 3 中它表示真正的除法并返回一个浮点数。因此,“keylen”在 Python 2 中为 16,而在 Python 3 中为 16.0。与 int 不同,浮点数不能用于拼接数组,因此 Python 3 会抛出您在计算 md5_hash[:keylen] 时看到的 TypeError。Python 2 将毫无错误地运行它,因为 keylen 将是一个 int。
您可以通过更改模块的源代码以使用“//”运算符来解决第二个问题(这意味着除法并在 Python 2 和 3 中返回一个 int):
keylen = 128 // 8
但是,您稍后会在代码中遇到第三个错误,也与 Python 3 兼容性有关。我不会通过描述来强调这一点。那么,据我所知,对您的问题的简短回答是要么使用 Python 2,要么修补各种代码兼容性问题,或者为 Python 使用不同的 PDF 库,它对 Python 3 有更好的支持(如果存在满足您的特定要求)。