Python处理PDF文件-简译与总结

最近看到一篇介绍Python中pyPDF模块的文章,详细介绍了使用pyPDF模块获取PDF文件信息,合并拆分PDF文件等功能。很方便,在此搬运分享以下:

How to Work With a PDF in Python

全文介绍了以下几方面的功能

  • 提取文件信息
  • 旋转页面
  • 合并文件
  • 拆分文件
  • 添加水印
  • 加密文件

这里我主要尝试了前几个功能的实现,添加水印与加密文件不是很用得上就不再详细尝试了。

pyPdf,PyPDF2以及PyPDF4的发展历程

最初的pyPdf模块发布与2005年,但并不支持Python3。PyPDF2目前也基本停用,最新版本的PyPDF4支持PyPDF2的大多数功能,但也有部分功能不兼容。原文中使用的是PyPDF2模块,此处我改用最新的PyPDF4进行尝试。

安装

如果你已经安装了Anaconda,可以使用pip或者conda直接安装:
pip install PyPDF4

功能实现

提取PDF文件信息

我们可以通过PdfFileReader来实现对以下信息的提取:

  • 作者
  • 创建者
  • 生产商
  • 主题
  • 题目
  • 页数

代码如下:

from PyPDF4 import PdfFileReader,PdfFileWriter

def extract_information(pdf_path):
    with open(pdf_path,'rb') as f:
        pdf=PdfFileReader(f)
        information=pdf.getDocumentInfo()
        number_of_pages=pdf.getNumPages()
        
    txt=f"""
    Information about {pdf_path}
    
    Author: {information.author}
    Creator: {information.creator}
    Producer: {information.producer}
    Subject: {information.subject}
    Title: {information.title}
    Number of pages: {number_of_pages}
    """
    print(txt)
    return information

if __name__ == '__main__':
    path='visa_letter.pdf'
    extract_information(path)

主要通过getDocumentInfo()getNumPages()两个函数来实现读取信息的功能。如果想进一步读取PDF中的文本信息,可以参照PDFMiner.six(Python3支持)

if __name__ == '__main__'则放在自己创建的模块底部,指明当该模块作为main函数调用时的执行情况。

旋转页面

旋转页面功能需要导入PdfFileWriter模块,上文中以导入,此处不再重复,代码如下:

def rotate_pages(pdf_path,pages=[0],direction='right'):
    """Parameters: filename, pages want to rotate and rotate direction"""
    pdf_writer=PdfFileWriter()
    pdf_reader=PdfFileReader(pdf_path)
    outputname=pdf_path[:-4]+'-rotate.pdf'
    for page in pages:
        # Rotate page 90 degrees to the right
        if direction=='right':
            page_r=pdf_reader.getPage(page).rotateClockwise(90)
            pdf_writer.addPage(page_r)
        # Rotate page 90 degrees to the left
        elif direction=='left':
            page_r=pdf_reader.getPage(page).rotateCounterClockwise(90)
            pdf_writer.addPage(page_r)
        else:
            print("Encountered an improper argument! Input right or left.")
    
    with open(outputname,'wb') as fh:
        pdf_writer.write(fh)

旋转页码通过getPage()rotateClockwise() rotateCounterClockwise()实现,getPage()获取要旋转页的索引,rotateClockwise() rotateCounterClockwise()控制旋转方向。

addPage()将要输出的页添加到流(不清楚该如何严谨表述,依稀记得C++里是这么称呼的)中,最后的新文件通过write()创建。

这里我将原文的函数改进了以下,加入两个表明要旋转的页码以及旋转方向的关键字参数,页码通过列表传递,默认将第一页顺时针旋转90度。

这里的else情况似乎抛出个异常更为合理,但我还没有掌握异常的处理方法。

合并文件

合并文件的核心则是对每个文件的每一页进行枚举,再逐次添加到流里,最后统一输出:

def merge_pdfs(paths,output):
    pdf_writer=PdfFileWriter()
    for path in paths:
        pdf_reader=PdfFileReader(path)
        for page in range(pdf_reader.getNumPages()):
            pdf_writer.addPage(pdf_reader.getPage(page))
            
    with open(output,'wb') as out:
        pdf_writer.write(out)

要合并的文件以列表的形式传入即可。

拆分文件

这里遇到了个问题,使用PyPDF4在拆分循环中会出现PdfFileWriter' object has no attribute 'stream'的错误,似乎是个bug。

这可能是因为将读入文件步骤写在了循环外导致写出过程紊乱,在循环内再此读入文件可以解决,详情见PyPDF2 PdfFileWriter has no attribute stream

PyPDF2有时也会遇到同样的错误,但此处使用PyPDF2不会有该错误。

def split(pdf_path,name_of_split):
    pdf_reader=PdfFileReader(pdf_path)
    for page in range(pdf_reader.getNumPages()):
        pdf_reader=PdfFileReader(pdf_path)
        pdf_writer=PdfFileWriter()
        pdf_writer.addPage(pdf_reader.getPage(page))
        output=f'{name_of_split}{page}.pdf'
        with open(output,'wb') as output_pdf:
            pdf_writer.write(output_pdf)

总结

至此一个具有简易功能的PDF编辑器就基本实现了,在使用时可以将PDFeditor.py最后一部分修改为:

def main():
    pass

if __name__ == '__main__':
    main()

防止直接调用,具体调用方法则为:

import PDFeditor
split('visa_letter.pdf','s')
merge_pdfs(['s0.pdf','s1.pdf'],'m.pdf')
rotate_pages('m.pdf',[0,1],'left')
extract_information('m.pdf')
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,884评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,212评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,351评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,412评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,438评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,127评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,714评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,636评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,173评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,264评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,402评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,073评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,763评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,253评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,382评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,749评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,403评论 2 358

推荐阅读更多精彩内容