使用Python检查PDF文件是否有效

24

我通过HTTP上传获取了一个文件,需要确保它是PDF文件。 编程语言是Python,但这并不重要。

我想到了以下解决方案:

  1. 检查字符串的前几个字节是否为%PDF这不是一个很好的检查方法,但可以防止用户意外上传其他文件。

  2. 使用libmagic(在bash中使用 file命令)。这与(1)中的检查完全相同。

  3. 使用一个库尝试从文件中读取页数。 如果该库能够读取出页数,则应该是一个有效的PDF文件。问题是:我不知道有一个Python库可以做到这一点

是否有使用库或其他技巧的解决方案?

7个回答

26

当前的解决方案(截至2023年)是使用pypdf并捕获异常(以及可能分析reader.metadata)。

from pypdf import PdfReader
from pypdf.errors import PdfReadError

with open("testfile.txt", "w") as f:
    f.write("hello world!")

try:
    PdfReader("testfile.txt")
except PdfReadError:
    print("invalid PDF file")
else:
    pass

这给我ZIP文件的误报。错误信息是:“incorrect startxref pointer(1)”。 - JulianWgs
使用PdfReader("testfile.txt", strict=True)和一个裸的except。 - JulianWgs

13

在我的项目中,我需要检查一些上传文件的MIME类型。我只是像这样使用file命令:

from subprocess import Popen, PIPE
filetype = Popen("/usr/bin/file -b --mime -", shell=True, stdout=PIPE, stdin=PIPE).communicate(file.read(1024))[0].strip()

当然,你可能想将实际命令移动到某个配置文件中,因为命令行选项在操作系统(例如mac)之间也会有所不同。

如果你只需要知道它是不是PDF文件,并且不需要对其进行任何处理,我认为file命令比lib库更快。当然,手动检查也是可以的,但如果你想检查不同类型的文件,file命令可能会给你更多的灵活性。


如果您只想确保您拥有的文件是PDF格式,那么这个方法既简单又快速。 - Roger Heathcote
这不是一个解决方案,因为它并不适用于所有的pdf文件。我有一个损坏的文件(无法在Adobe Reader、evince等中读取),但是file -b --mime返回application/pdf; charset=binary。 - rosch

13

Python中最常用的两个PDF库是:

这两个库都是纯Python编写的,因此安装应该很容易,并且跨平台。

使用pypdf可能只需要简单地执行以下操作:

from pypdf import PdfReader
reader = PdfReader("upload.pdf")

这应该足够了,但是如果您想进行进一步的检查,reader 现在将具有 metadatapages 属性。

正如 Carl 所回答的那样,pdftotext 也是一个很好的解决方案,并且在处理非常大的文档(特别是带有许多交叉引用的文档)时可能会更快。但是,由于派生新进程等系统开销,它在处理小型 PDF 时可能会稍微慢一些。


3

如果你使用的是Linux或OS X系统,你可以使用Pdftotext(Xpdf的一部分,在这里可以找到)。如果你传递给pdftotext的不是PDF文件,它肯定会报错,你可以使用commands.getstatusoutput来获取输出并解析这些警告。

如果你正在寻找一个平台无关的解决方案,你可能可以利用pypdf

编辑: 这不太优雅,但看起来pypdf的PdfReader会在尝试加载非PDF文件时抛出IOError(22)异常。


1

我遇到了同样的问题,但并不需要使用编程语言来管理这个任务。我使用了pypdf,但对我来说效率不高,因为它会在一些损坏的文件上无限挂起。

然而,我至今发现这个软件很有用。

祝你好运。

https://sourceforge.net/projects/corruptedpdfinder/


1

这里有一个使用pdfminersix的解决方案,可以使用pip install pdfminer.six进行安装:

from pdfminer.high_level import extract_text

def is_pdf(path_to_file):
    try:
        extract_text(path_to_file)
        return True
    except:
        return False

你也可以使用filetypepip install filetype):
import filetype

def is_pdf(path_to_file):
    return filetype.guess(path_to_file).mime == 'application/pdf'

这两种解决方案都不是理想的。

  1. filetype 解决方案的问题在于它不能告诉你 PDF 文件本身是否可读。它只会告诉你文件是否为 PDF,但它可能是一个损坏的 PDF。
  2. pdfminer 解决方案只有在 PDF 实际上是可读的情况下才应返回 True。但它是一个庞大的库,对于这样一个简单的函数来说似乎过于复杂。

我已经在这里开始了另一个 线程,询问如何检查文件是否为有效的 PDF 而不使用库(或使用较小的库)。


这个使用pypdf的解决方案怎么样?https://gist.github.com/gvangool/129962/775b05d07a2a3d5dafe1ee6253220d7c47f37e99它是否比pdfminer.six更少占用资源,因为它只创建了一个阅读器? - Ryan Eom

-1
“有效”是指可以被PDF查看器显示,还是指可以提取文本?这两个概念是完全不同的。
如果您只想检查上传的文件是否真的是PDF文件,那么pypdf解决方案或类似的解决方案都可以使用。
然而,如果您想检查文本是否可以被提取,那么您将会面临一整个痛苦的世界!使用pdftotext可能是一个简单的解决方案,在大多数情况下都可以工作,但它并不是100%成功。我们发现许多PDF文件无法从pdftotext中提取,但Java库,如iText和PDFBox可以做到。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接