从加密的PDF中提取Python数据


12

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

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

其次,我决定解密PDF。我成功使用了Python库pykepdf。Pykepdf效果很好!但是,解密的PDF不能与上一点的Python库(PyPDF2Tabula)一起读取。目前,我们已经进行了一些改进,因为使用Adobe Reader可以从解密的PDF中导出信息,但是目标是使用Python进行所有操作。

我要显示的代码可以与未加密的PDF完美配合,但不能与加密的PDF完美配合。它也不适用于通过pykepdf获取的解密PDF。

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

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

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

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

我使用Python 3.7,Windows 10,Jupiter Notebook和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'

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

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

代码在堆栈交换问题中: 使用python中的PDFMiner从PDF文件提取文本?

如果您想重复我的实验,我会很高兴。说明如下:

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

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

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

4)使用解密的PDF重新运行代码。

更新10/4/2019 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视频“使用Camelot使用Python从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 我发现了一个窍门。如果使用Adobe Reader打开受保护的pdf,并使用Microsoft将其打印为PDF,然后将其另存为PDF,则可以使用该副本提取数据。我还可以将PDF文件转换为JSON,Excel,SQLite,CSV,HTML和其他格式。这是我的问题的可能解决方案。但是,我仍在寻找一种没有这种技巧的选择,因为目标是使用Python 100%做到这一点。我还担心,如果使用更好的加密方法,该技巧可能会失效。有时您需要多次使用Adobe Reader来获取可提取的副本。

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


2
我无法用复制这些问题PyPDF2,一切都很好。我使用pdftk在线服务以及在线服务来加密文件。您可以发布“麻烦的” PDF文件的链接吗?
constt

1
好,谢谢!您是否尝试过用来qpdf解密文件?在这种情况下,您可以subprocess在解析文件之前从脚本使用模块调用该文件以解密文件。
constt

1
首先,PyPDF2无法解密Acrobat PDF文件=> 6.0。其次,pikepdf当前没有文本提取工具。
生活很复杂

1
@Beginner我想这可能与pykepdf用于写入未加密的PDF的基础格式有关。
人生错综复杂

2
“所有受保护/加密的pdf均受密码保护吗?” -不 也有使用基于X509证书的私钥/公钥密码术加密的pdf。
mkl

Answers:


8

最后更新10-11-2019

我不确定我是否完全理解您的问题。可以完善下面的代码,但它可以读取加密的或未加密的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:加密.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")

您可以使用tikapikepdf创建的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无法正确提取文本
    • 蒂卡可以正确提取文本
  2. Adobe Acrobat 6.0及更高版本-加密级别128位RC4

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

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

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

    • 使用pdfprotectfree创建
    • pikepdf能够解密此文件
    • PyPDF2可以正确提取文本
    • 蒂卡可以正确提取文本

PyPDF2能够从未使用Adobe Acrobat创建的解密PDF文件中提取文本。

我认为这些故障与Adobe 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

20159月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的更新的答复。

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

Adobe PDF的安全级别

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

  1. 文档加密(通过文档打开密码强制执行)

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

    • 允许打印
    • 允许更改

下图显示了使用256位AES加密进行加密的Adobe PDF。要打开或打印此PDF,需要输入密码。当您使用密码在Adobe Reader中打开此文档时,标题将显示为SECURED

password_level_encryption

本文档要求使用此答案中提到的Python模块打开密码。如果您尝试使用Adobe Reader打开加密的PDF。您应该看到以下内容:

password_prompt

如果未收到此警告,则说明该文档没有启用安全控制,或者仅启用了限制性的编辑和打印功能。

下图显示了使用PDF文档中的密码启用的严格编辑。注意打印已启用。要打开或打印此PDF,不需要密码。当您在没有密码的情况下在Adobe Reader中打开此文档时,标题将显示SECURED (警告)。这与使用密码打开的加密PDF相同。

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

password_level_restrictive_editing

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

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

关于破解PDF加密

无论PyPDF2PyPDF4是要打破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。它将在不添加SECURED警告的情况下将该文件保存到新的PDF。该蒂卡代码将从新的文件解析的内容。

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)

2
如何在不提供密码的情况下打开安全的PDF文件?
生活很复杂,

1
您仅指限制性编辑保护吗?
人生错综复杂

1
使用已启用限制性编辑保护但允许打印的PDF的代码更新了答案。
人生错综复杂

1
可以使用XHTML吗?
人生错综复杂

1
我修改了答案以输出XHTML。JSON是可能的,但是它需要挖掘与tika解析器相关的github项目代码。
人生错综复杂

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,则可以提供密码。


1
谢谢您的回答!我们正在尝试不使用密码来阅读它。目前,我们能够使用我的UPDATE

这远远不能回答问题。似乎您尚未阅读完整的问题。
shoonya ek

1
当密码的默认值为“无”时,这将处理那些通常在pikepdf失败的受保护的PDF。通过传递一个空字符串,它可以正确地打开并解析受保护的PDF文档(在我运行的测试案例中)。
Mahendra Singh,

1
@Beginner在这种情况下,您不必在这里转换PDF。这仅是基于我以前的经验,即通过提供空密码来保护PDF安全。
Mahendra Singh,

1
@Beginner这是我的整个代码。这只会从pikepdf返回pdf_object。如果要保存此pdf,只需使用pdf_obj.save('your_file_path')保存返回的对象。之后,您可以使用此PDF解析文本和其他对象。我使用一个名为PdfPlumber的库来提取文本。
马亨德拉·辛格

1

对于表格格式,您可以尝试使用read_pdf的密码选项。它取决于tabula-java的功能,因此我不确定是否支持哪种加密。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.