15

我是一名刚毕业的纯数学专业的毕业生,只上过很少的基础编程课程。我正在实习,并且有一个内部数据分析项目。我必须分析过去几年的内部 PDF。PDF 是“安全的”。换句话说,它们是加密的。我们没有 PDF 密码,更重要的是,我们不确定密码是否存在。但是,我们拥有所有这些文件,我们可以手动阅读它们。我们也可以打印它们。我们的目标是用 Python 阅读它们,因为这是我们有一些想法的语言。

首先,我尝试使用一些 Python 库来阅读 PDF。但是,我发现的 Python 库不读取加密的 PDF。当时,我也无法使用 Adob​​e Reader 导出信息。

其次,我决定解密 PDF。我成功使用了 Python 库 pykepdf。Pykepdf效果很好!但是,解密后的 PDF 也无法使用前一点的 Python 库(PyPDF2Tabula)读取。此时,我们做了一些改进,因为使用 Adob​​e Reader 我可以从解密的 PDF 中导出信息,但目标是使用 Python 完成所有工作。

我展示的代码完美适用于未加密的 PDF,但不适用于加密的 PDF。它也不适用于使用 pykepdf 获得的解密 PDF。

我没有写代码。我在 Python 库PykepdfTabula的文档中找到了它。PyPDF2 解决方案是由Al Sweigart在他的书“用 Python 自动化无聊的东西”中编写的,我强烈推荐这本书。我还检查了代码是否工作正常,具有我之前解释的限制。

第一个问题,如果程序使用从未加密的文件,为什么我无法读取解密的文件?

第二个问题,我们可以用 Python 读取解密的文件吗?哪个图书馆可以做到或不可能?所有解密的 PDF 都可以提取吗?

感谢您的时间和帮助!!!

我使用 Python 3.7、Windows 10、Jupiter Notebooks 和 Anaconda 2019.07 找到了这些结果。

Python

import pikepdf
with pikepdf.open("encrypted.pdf") as pdf:
  num_pages = len(pdf.pages)
  del pdf.pages[-1]
  pdf.save("decrypted.pdf")

import tabula
tabula.read_pdf("decrypted.pdf", stream=True)

import PyPDF2
pdfFileObj=open("decrypted.pdf", "rb")
pdfReader=PyPDF2.PdfFileReader(pdfFileObj)
pdfReader.numPages
pageObj=pdfReader.getPage(0)
pageObj.extractText()

使用 Tabula,我收到消息“输出文件为空”。

使用 PyPDF2,我只得到 '/n'

更新 2019 年 10 月 3 日 Pdfminer.six(2018 年 11 月版本)

使用DuckPuncher发布的解决方案,我得到了更好的结果。对于解密的文件,我得到了标签,但没有得到数据。加密文件也是如此。对于从未加密过的文件来说,它是完美的。由于我需要加密或解密文件的数据和标签,因此此代码对我不起作用。对于该分析,我使用了 pdfminer.six,它是 2018 年 11 月发布的 Python 库。Pdfminer.six 包含一个库 pycryptodome。根据他们的文档“ PyCryptodome是一个自包含的 Python 低级加密原语包..”

代码在堆栈交换问题中: Extracting text from a PDF file using PDFMiner in python?

如果你想重复我的实验,我会很高兴。这是描述:

1) 使用任何从未加密的 PDF 运行此问题中提到的代码。

2) 对 PDF“安全”(这是 Adob​​e 使用的术语)执行相同操作,我将其称为加密 PDF。使用可通过 Google 找到的通用表单。下载后,您需要填写字段。否则,您将检查标签,而不是字段。数据在字段中。

3) 使用 Pykepdf 解密加密的 PDF。这将是解密的 PDF。

4) 使用解密的 PDF 再次运行代码。

更新 2019 年 10 月 4 日 Camelot(2019 年 7 月版)

我找到了 Python 库 Camelot。请注意,您需要camelot-py 0.7.3。

它非常强大,适用于 Python 3.7。此外,它非常易于使用。首先,您还需要安装Ghostscript。否则,它将无法正常工作。您还需要安装Pandas不要使用 pip install camelot-py。而是使用pip install camelot-py[cv]

该程序的作者是 Vinayak Mehta。Frank Du 在 YouTube 视频“使用 Python 使用 Camelot 从 PDF 中提取表格数据”中分享了此代码。

我检查了代码,它正在处理未加密的文件。但是,它不适用于加密和解密文件,这就是我的目标

Camelot 旨在从 PDF 中获取表格。

这是代码:

Python

import camelot
import pandas
name_table = camelot.read_pdf("uncrypted.pdf")
type(name_table)

#This is a Pandas dataframe
name_table[0]

first_table = name_table[0]   

#Translate camelot table object to a pandas dataframe
first_table.df

first_table.to_excel("unencrypted.xlsx")
#This creates an excel file.
#Same can be done with csv, json, html, or sqlite.

#To get all the tables of the pdf you need to use this code.
for table in name_table:
   print(table.df)

更新 10/7/2019 我发现了一个技巧。如果我使用 Adob​​e Reader 打开受保护的 pdf,然后使用 Microsoft 将其打印为 PDF,并将其另存为 PDF,则可以使用该副本提取数据。我还可以将 PDF 文件转换为 JSON、Excel、SQLite、CSV、HTML 和其他格式。这是我的问题的可能解决方案。但是,我仍然在寻找一种不使用该技巧的选项,因为目标是 100% 使用 Python 来完成。我还担心,如果使用更好的加密方法,这个技巧可能不会奏效。有时您需要多次使用 Adob​​e Reader 才能获得可提取的副本。

2019 年 10 月 8 日更新。第三个问题。 我现在有第三个问题。所有安全/加密的 pdf 都受密码保护吗?为什么 pikepdf 不起作用?我的猜测是当前版本的 pikepdf 可以破坏某些类型的加密,但不是全部。@constt 提到 PyPDF2 可以破坏某种类型的保护。但是,我回复他说,我发现一篇文章说 PyPDF2 可以破解使用 Adob​​e Acrobat Pro 6.0 进行的加密,但不能破解后期版本。

4

3 回答 3

10

最后更新时间:2019 年 10 月 11 日

我不确定我是否完全理解你的问题。下面的代码可以改进,但它读取加密或未加密的 PDF 并提取文本。如果我误解了您的要求,请告诉我。

from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter
from pdfminer.converter import TextConverter
from pdfminer.layout import LAParams
from pdfminer.pdfpage import PDFPage
from io import StringIO

def extract_encrypted_pdf_text(path, encryption_true, decryption_password):

  output = StringIO()

  resource_manager = PDFResourceManager()
  laparams = LAParams()

  device = TextConverter(resource_manager, output, codec='utf-8', laparams=laparams)

  pdf_infile = open(path, 'rb')
  interpreter = PDFPageInterpreter(resource_manager, device)

  page_numbers = set()

  if encryption_true == False:
    for page in PDFPage.get_pages(pdf_infile, page_numbers, maxpages=0, caching=True, check_extractable=True):
      interpreter.process_page(page)

  elif encryption_true == True:
    for page in PDFPage.get_pages(pdf_infile, page_numbers, maxpages=0, password=decryption_password, caching=True, check_extractable=True):
      interpreter.process_page(page)

 text = output.getvalue()
 pdf_infile.close()
 device.close()
 output.close()
return text

results = extract_encrypted_pdf_text('encrypted.pdf', True, 'password')
print (results)

我注意到您用于打开加密 PDF的pikepdf代码缺少密码,这应该会引发以下错误消息:

pikepdf._qpdf.PasswordError: encrypted.pdf: 密码无效

import pikepdf

with pikepdf.open("encrypted.pdf", password='password') as pdf:
num_pages = len(pdf.pages)
del pdf.pages[-1]
pdf.save("decrypted.pdf")

您可以使用tika从 pikepdf 创建的decrypted.pdf中提取文本。

from tika import parser

parsedPDF = parser.from_file("decrypted.pdf")
pdf = parsedPDF["content"]
pdf = pdf.replace('\n\n', '\n')

此外,pikepdf当前不实现文本提取,包括最新版本 v1.6.4。


我决定使用各种加密的 PDF 文件进行几次测试。

我将所有加密文件命名为“encrypted.pdf”,它们都使用相同的加密和解密密码。

  1. Adobe Acrobat 9.0 及更高版本 - 加密级别 256 位 AES

    • pikepdf 能够解密这个文件
    • PyPDF2 无法正确提取文本
    • tika 可以正确提取文本
  2. Adobe Acrobat 6.0 及更高版本 - 加密级别 128 位 RC4

    • pikepdf 能够解密这个文件
    • PyPDF2 无法正确提取文本
    • tika 可以正确提取文本
  3. Adobe Acrobat 3.0 及更高版本 - 加密级别 40 位 RC4

    • pikepdf 能够解密这个文件
    • PyPDF2 无法正确提取文本
    • tika 可以正确提取文本
  4. Adobe Acrobat 5.0 及更高版本 - 加密级别 128 位 RC4

    • 使用 Microsoft Word 创建
    • pikepdf 能够解密这个文件
    • PyPDF2 可以正确提取文本
    • tika 可以正确提取文本
  5. Adobe Acrobat 9.0 及更高版本 - 加密级别 256 位 AES

    • 使用 pdfprotectfree 创建
    • pikepdf 能够解密这个文件
    • PyPDF2 可以正确提取文本
    • tika 可以正确提取文本

PyPDF2 能够从不是用 Adob​​e Acrobat 创建的解密 PDF 文件中提取文本。

我认为这些失败与 Adob​​e Acrobat 创建的 PDF 中的嵌入格式有关。需要更多的测试来证实这个关于格式的猜想。

tika 能够从使用 pikepdf 解密的所有文档中提取文本。


 import pikepdf
 with pikepdf.open("encrypted.pdf", password='password') as pdf:
    num_pages = len(pdf.pages)
    del pdf.pages[-1]
    pdf.save("decrypted.pdf")


 from PyPDF2 import PdfFileReader

 def text_extractor(path):
   with open(path, 'rb') as f:
     pdf = PdfFileReader(f)
     page = pdf.getPage(1)
     print('Page type: {}'.format(str(type(page))))
     text = page.extractText()
     print(text)

    text_extractor('decrypted.pdf')

PyPDF2无法解密 Acrobat PDF 文件 => 6.0

自2015 年 9 月 15 日以来,此问题已向模块所有者开放。与此问题相关的评论中不清楚项目所有者何时会解决此问题。最后一次提交是 2018 年 6 月 25 日。

PyPDF4解密问题

PyPDF4 是 PyPDF2 的替代品。该模块还存在用于加密 PDF 文件的某些算法的解密问题。

测试文件:Adobe Acrobat 9.0 及更高版本 - 加密级别 256 位 AES

PyPDF2 错误信息:仅支持算法代码 1 和 2

PyPDF4 错误信息:仅支持算法代码 1 和 2。此 PDF 使用代码 5


更新部分 10-11-2019

本部分是为了响应您在 10-07-2019 和 10-08-2019 的更新。

在您的更新中,您声明您可以打开“使用 Adob​​e Reader 保护的 pdf”并将文档打印到另一个 PDF,从而删除“SECURED”标志。在做了一些测试之后,我相信已经弄清楚了在这种情况下发生了什么。

Adobe PDF 安全级别

Adobe PDF 具有多种类型的安全控制,可由文档所有者启用。可以使用密码或证书来实施控制。

  1. 文档加密(使用文档打开密码强制执行)

    • 加密所有文档内容(最常见)
    • 加密除元数据以外的所有文档内容 => Acrobat 6.0
    • 仅加密文件附件 => Acrobat 7.0
  2. 限制性编辑和打印(使用权限密码强制执行)

    • 允许打印
    • 允许更改

下图显示了使用 256 位 AES 加密技术加密的 Adob​​e PDF。要打开或打印此 PDF,需要密码。当您使用密码在 Adob​​e Reader 中打开此文档时,标题将显示为SECURED

密码级别加密

本文档需要密码才能使用此答案中提到的 Python 模块打开。如果您尝试使用 Adob​​e Reader 打开加密的 PDF。你应该看到这个:

密码提示

如果您没有收到此警告,则该文档要么没有启用安全控制,要么只启用了限制性编辑和打印控制。

下图显示了在 PDF 文档中使用密码启用限制性编辑。便笺打印已启用。打开或打印此 PDF不需要密码。当您在没有密码的情况下在 Adob​​e Reader 中打开此文档时,标题将显示SECURED 这与使用密码打开的加密 PDF 相同的警告。

当您将此文档打印到新的 PDF 时,SECURED警告将被删除,因为限制性编辑已被删除。

密码级别限制编辑

所有 Adob​​e 产品都会强制执行权限密码设置的限制。但是,如果第三方产品不支持这些设置,文档接收者可以绕过部分或全部限制设置。

因此,我假设您要打印到 PDF 的文档启用了限制性编辑,并且没有启用打开所需的密码。

关于破解 PDF 加密

PyPDF2或PyPDF4都不是为了破坏 PDF 文档的文档打开密码功能而设计的。如果这两个模块尝试打开受密码保护的加密 PDF 文件,它们都会抛出以下错误。

PyPDF2.utils.PdfReadError:文件尚未解密

可以使用多种方法绕过加密 PDF 文件的打开密码功能,但单一技术可能无法使用,并且由于包括密码复杂性在内的多种因素,某些技术可能无法接受。

PDF 加密在内部使用 40、128 或 256 位的加密密钥,具体取决于 PDF 版本。二进制加密密钥源自用户提供的密码。密码受长度和编码限制。

例如,PDF 1.7 Adob​​e Extension Level 3 (Acrobat 9 - AES-256) 引入了 Unicode 字符(65,536 个可能的字符),并将密码的 UTF-8 表示形式的最大长度增加到 127 个字节。


下面的代码将打开一个启用了限制性编辑的 PDF。它将将此文件保存到新的 PDF 中,而不会添加 SECURED 警告。tika代码将解析新文件中的内容。

from tika import parser
import pikepdf

# opens a PDF with restrictive editing enabled, but that still 
# allows printing.
with pikepdf.open("restrictive_editing_enabled.pdf") as pdf:
  pdf.save("restrictive_editing_removed.pdf")

  # plain text output
  parsedPDF = parser.from_file("restrictive_editing_removed.pdf")

  # XHTML output
  # parsedPDF = parser.from_file("restrictive_editing_removed.pdf", xmlContent=True)

  pdf = parsedPDF["content"]
  pdf = pdf.replace('\n\n', '\n')
  print (pdf)

此代码检查打开文件是否需要密码。这段代码可以细化,可以添加其他功能。还有其他几个可以添加的功能,但是 pikepdf 的文档与代码库中的注释不匹配,因此需要更多的研究来改进这一点。

# this would be removed once logging is used
############################################
import sys
sys.tracebacklimit = 0
############################################

import pikepdf
from tika import parser

def create_pdf_copy(pdf_file_name):
  with pikepdf.open(pdf_file_name) as pdf:
    new_filename = f'copy_{pdf_file_name}'
    pdf.save(new_filename)
    return  new_filename

def extract_pdf_content(pdf_file_name):
  # plain text output
  # parsedPDF = parser.from_file("restrictive_editing_removed.pdf")

  # XHTML output
  parsedPDF = parser.from_file(pdf_file_name, xmlContent=True)

  pdf = parsedPDF["content"]
  pdf = pdf.replace('\n\n', '\n')
  return pdf

def password_required(pdf_file_name):
  try:
    pikepdf.open(pdf_file_name)

  except pikepdf.PasswordError as error:
    return ('password required')

  except pikepdf.PdfError as results:
    return ('cannot open file')


filename = 'decrypted.pdf'
password = password_required(filename)
if password != None:
  print (password)
elif password == None:
  pdf_file = create_pdf_copy(filename)
  results = extract_pdf_content(pdf_file)
  print (results)
于 2019-10-09T01:27:36.767 回答
1

当您在没有密码的情况下打开这些文件时,您可以尝试处理这些文件产生的错误。

import pikepdf

def open_pdf(pdf_file_path, pdf_password=''):
    try:
        pdf_obj = pikepdf.Pdf.open(pdf_file_path)

    except pikepdf._qpdf.PasswordError:
        pdf_obj = pikepdf.Pdf.open(pdf_file_path, password=pdf_password)

    finally:
        return pdf_obj

您可以使用返回的 pdf_obj 进行解析工作。此外,如果您有加密的 PDF,您可以提供密码。

于 2019-10-10T03:47:59.853 回答
1

对于 tabula-py,您可以尝试使用 read_pdf 的密码选项。这取决于 tabula-java 的功能,所以我不确定支持哪种加密。

于 2019-11-24T00:34:10.257 回答