我心中的王者:Python-第18章 使用Python处理PDF文件

我心中的王者:Python-第18章 使用Python处理PDF文件

PDF文件和Word文件一样是二进制(binary)文件,所以处理起来步骤会多一点,不过,读者不用担心,笔者将以实例一步一步讲解,相信读完本章读者也可以很轻松学会使用Python处理PDF文件。

本章内容需要使用外部模块PyPDF2,下载此模块时指令如下:

pip install PyPDF2

程序导入使用import时是:

import PyPDF2

18-1 打开PDF文件

我们可以使用open( )打开PDF文件,语法如下:

   pdfObj = open(‘pdf_file', ‘rb')
 # ‘rb'表示以二进制打开

上述pdf_file是要打开的文件,开档成功后会传回所打开PDF文件的文件对象,在上述语法中笔者将所打开PDF文件的文件对象设定给pdfObj,未来就用pdfObj代表所打开的PDF文件。本书使用的PDF文件travel.pdf内容有3页,下列是第1页内容。

18-2 获得PDF文件的页数

打开PDF文件成功后,可以使用PdfFileReader( )方法读取这个PDF文件,下列是语法内容:

 pdfRd = PyPDF2.PdfFileReader(pdfObj)  # 读取PDF内容

上述会将所读取的内容放在pdfRd对象变量内,这个对象变量内含numPages属性记录此PDF文件的页数。

程序实例ch18_1.py:计算travel.pdf的页数,这个文件在ch18文件夹内。

# ch18_1.py
import PyPDF2

fn = 'travel.pdf'           # 设定欲读取的PDF档案
pdfObj = open(fn,'rb')      # 以二进制方式开启
pdfRd = PyPDF2.PdfReader(pdfObj)
print("PDF页数是 = ", len(pdfRd.pages))

执行结果 读者可检查页面,这个PDF文件的确是3页。

PDF页数是 =  3

18-3 读取PDF页面内容

使用PdfFileReader( )方法读取这个PDF文件后,可以使用getPage(n)取得第n页的PDF内容,如下所示:

 pdfContentObj = pdfRd.getPage(n)  # 读取第n页内容

PDF页面也是从第0页开始计算,页面内容被读入pdfContentObj对象后,可以使用extractText( )取得该页的字符串内容。需留意,PyPDF2模块对于读取英文文件,比较没有障碍,对于中文内容会出现乱码。另外,PyPDF2无法读取图表或表格数据。

程序实例ch18_2.py:读取travel.pdf的第0页内容。

# ch18_2.py
import PyPDF2

fn = 'travel.pdf'                       # 设定欲读取的PDF档案
pdfObj = open(fn,'rb')                  # 以二进制方式开启
pdfRd = PyPDF2.PdfReader(pdfObj)    # 读取PDF档案
pageObj = pdfRd.Pages[0]              # 将第10页内容读入pageObj
txt = pageObj.extractText()             # 撷取页面内容
print(txt)

执行结果

Traveling in the USA
Jiin-Kwei Hung
Kwei Travel AgencyTraffic ontheroad
Famous scenic area

18-4 检查PDF是否被加密

初次执行“pdfRd = PyPDF2.PdfFileReader(pdfObj)”之后,pdfRd对象会有isEncryted属性,如果此属性是True,表示文件有加密。如果此属性是False,表示文件没有加密。

程序实例ch18_3.py:检查文件是否加密,在ch18文件夹内有travel.pdf和encrypttravel.pdf文件,本程序会测试这2个文件。

# ch18_3.py
import PyPDF2

def encryptYorN(fn):
    '''检查档案是否加密'''
    pdfObj = open(fn,'rb')
    pdfRd = PyPDF2.PdfReader(pdfObj)
    if pdfRd.is_encrypted:       # 由这个属性判断是否加密
        print("%s 档案有加密" % fn)
    else:
        print("%s 档案没有加密" % fn)

encryptYorN('travel.pdf')
encryptYorN('encrypttravel.pdf')

执行结果

travel.pdf 档案没有加密
encrypttravel.pdf 档案有加密

18-5 解密PDF文件

对于加密的PDF文件,我们可以使用decrypt( )执行解密,如果解密成功decrypt( )会传回1,如果失败则传回0。

程序实例ch18_4.py:读取使用密码‘jiinkwei’加密的encrypttravel.pdf文件。

# ch18_4.py
import PyPDF2

pdfObj = open('encrypttravel.pdf','rb')
pdfRd = PyPDF2.PdfReader(pdfObj)
if pdfRd.decrypt('jiinkwe1'):           # 检查密码是否正确
    pageObj = pdfRd.getPage(0)          # 密码正确则读取第0页
    txt = pageObj.extractText()
    print(txt)
else:
    print('解密失败')

执行结果 执行结果可以参考ch18_2.py。

解密失败

在ch18文件夹有ch18_4_1.py文件,这个文件第6行,笔者故意将密码写错,将印出‘解密失败’的信息,读者可以试着执行体会结果。读者需留意的是使用decrypt( )解密时,是解pdfRd对象的密码不是整份PDF,未来如果其他程序要使用这个PDF,仍须执行解密才可阅读使用。

18-6 建立新的PDF文件

目前PyPDF2模块只能将其他的PDF页面转存成PDF文件,还无法将Word、PowerPoint等文件转成PDF文件。它的基本流程如下:

①建立一个PdfWr对象(名称可以自取),未来写入用。

②将已有pdfRd对象一次一页复制到pdfWr对象。

③使用write( )方法将pdfWriter对象写入PDF文件。

一次一页PDF的复制可以使用addPage( ),细节可参考ch18_5.pdf第7和8行。最后使用write( )将pdfWr写入文件可参考第10和11行。

程序实例ch18_5.py:将travel.pdf的第一页复制到out18_5.pdf。

# ch18_5.py
import PyPDF2

pdfObj = open('travel.pdf','rb')
pdfRd = PyPDF2.PdfReader(pdfObj)

pdfWr = PyPDF2.PdfWriter()          # 新的PDF物件
pdfWr.add_page(pdfRd.pages[0])         # 将第0页放入新的PDF对象

pdfOutFile = open('out18_5.pdf', 'wb')  # 开启二进制文件供写入
pdfWr.write(pdfOutFile)                 # 执行写入
pdfOutFile.close()

执行结果 程序执行后在ch18文件夹可以看到out18_5.pdf文件。

如果要执行整个PDF文件的复制,可以将上述第8行改成for循环,就可以一次一页执行文件的复制。

程序实例ch18_6.py:将travel.pdf复制至out18_6.pdf。

# ch18_6.py
import PyPDF2

pdfObj = open('travel.pdf','rb')
pdfRd = PyPDF2.PdfReader(pdfObj)

pdfWr = PyPDF2.PdfWriter()              # 新的PDF物件
for pageNum in range(len(pdfRd.pages)):
    pdfWr.add_page(pdfRd.pages[pageNum])   # 一次将一页放入新的PDF对象

pdfOutFile = open('out18_6.pdf', 'wb')      # 开启二进制文件供写入
pdfWr.write(pdfOutFile)                     # 执行写入
pdfOutFile.close()

执行结果 你可以在ch18文件夹看到out18_6.pdf,内容与travel.pdf相同。

18-7 PDF页面的旋转

在浏览PDF文件时,可以旋转PDF页面。rotateClockwise( )可以执行页面顺时针旋转,rotateCounterClockwise( )可以执行逆时针旋转。在这2个方法内可以传入90、180、270度执行旋转工作。

程序实例ch18_7.py:将travel.pdf的第0页旋转90度,然后存入out18_7.pdf。

# ch18_7.py
import PyPDF2

pdfObj = open('travel.pdf','rb')
pdfRd = PyPDF2.PdfReader(pdfObj)

pdfWr = PyPDF2.PdfWriter()          # 新的PDF物件
pageR = pdfRd.pages[0]                # 原始第0页
pageR = pageR.rotate(90)       # 第0页炫转90度
pdfWr.add_page(pageR)                    # 将炫转后的第0页放入新的PDF对象

pdfOutFile = open('out18_7.pdf', 'wb')  # 开启二进制文件供写入
pdfWr.write(pdfOutFile)                 # 执行写入
pdfOutFile.close()

执行结果 这个程序会建立out18_7.pdf,下列是第0页内容。

在这里插入图片描述

18-8 加密PDF文件

若是想要将PDF文件加密,可以在将pdfWr对象正式使用write( )方法写入前调用encrypt( )执行,加密的密码当作参数放在encrypt( )方法内。

程序实例ch18_8.py:将travel.pdf文件加密储存在output.pdf内,密码是deepstone。

# ch18_8.py
import PyPDF2

pdfObj = open('travel.pdf','rb')
pdfRd = PyPDF2.PdfReader(pdfObj)

pdfWr = PyPDF2.PdfWriter()              # 新的PDF物件
for pageNum in range(len(pdfRd.pages)):
    pdfWr.add_page(pdfRd.pages[pageNum])   # 一次将一页放入新的PDF对象

pdfWr.encrypt('deepstone')                  # 执行加密
encryptPdf = open('output.pdf', 'wb')       # 开启二进制文件供写入
pdfWr.write(encryptPdf)                     # 执行写入
encryptPdf.close()

执行结果 执行打开output.pdf后将看到要求输入密码的窗口。

在这里插入图片描述

上述程序的关键是第11行,先对pdfWr对象加密,加密完成后,第13行再将pdfWr对象写入新打开的二进制文件对象encryptPdf,最后关闭此文件。

18-9 处理PDF页面重叠

有2个PDF文件分别是sse.pdf和secret.pdf,内容如下左边两图:

sse.pdf是一般的PDF文件,secret.pdf是含水印的PDF文件,所谓的页面重叠,就是将2个PDF页面组合。如右下图所示。
在这里插入图片描述

要完成这个工作,步骤如下,下列是用程序实例ch18_9.py为例说明:

①打开一般PDF文件,然后将页面内容放入ssePage对象(4-6行)。

②打开一般水印文件,然后将页面内容放入secretPage对象(8-10行)。

③使用下列指令执行重叠。

 ssePage.merge(secretPage)  # 重叠结果放在ssePage对象(12行)

④打开新的对象pdfWr,将ssePage结果存入新对象pdfWr(14-15行)。

⑤打开新的文件out18_9.pdf,此文件对象名称是mergePdf(17行)。

⑥将pdfWr写入mergePdf(18行)。

程序实例ch18_9.py:sse.Pdf文件与secret.pdf文件合并,同时将结果存入out18_9.pdf。

# ch18_9.py
import PyPDF2

pdfSSE = open('sse.pdf','rb')               # 开启一般pdf档案
pdfRdSSE = PyPDF2.PdfReader(pdfSSE)
ssePage = pdfRdSSE.pages[0]

pdfSecret = open('secret.pdf', 'rb')        # 开启水印pdf档案
pdfRdSecret = PyPDF2.PdfReader(pdfSecret)
secretPage = pdfRdSecret.pages[0]

ssePage.merge_page(secretPage)               # 执行重迭合并

pdfWr = PyPDF2.PdfWriter()              # 新的PDF物件
pdfWr.add_page(ssePage)                      # 将重迭页放入新的PDF对象

mergePdf = open('out18_9.pdf', 'wb')        # 开启二进制文件供写入
pdfWr.write(mergePdf)                       # 执行写入
mergePdf.close()

执行结果 打开out18_9.py后可以得到本节解说的文件。

18-10 破解密码的程序设计

有时候自己设计了一个PDF,但是忘记了密码怎么办?其实Python的功能可以让我们设计破解密码程序,先从简单开始吧!

破解3位数字的密码
如果密码是由3个阿拉伯数字组成,表示有3个位数,每个位数是由0-1所组成,读者可以使用下列方式设计密码。

程序实例ch18_10.py:破解3位数字的密码,程序的密码是在第3行设定,程序执行过程会将所测试失败的密码不断打印出来直到找到密码,此时会列出Bingo!字符串。为了让读者明白工作原理,所以一个密码用一行输出,在真实工作中可以不用如此,密码间空一格即可,不输出测试密码也不好,因为无法知道测试密码的进度。

# ch18_10.py

secretcode = '888'                                      # 设定密码
codeNotFound = True                                     # 尚未找到密码为True
for i1 in range(0, 10):                                 # 第一位数
    if codeNotFound:            # 检查是否找到没有找到才会往下执行
        for i2 in range(0, 10):                         # 第二位数
            if codeNotFound:    # 检查是否找到没有找到才会往下执行
                for i3 in range(0, 10):                 # 第三位数
                    code = str(i1) + str(i2) + str(i3)  # 组成密码
                    if code == secretcode:              # 比对密码
                        print('Bingo!', code)
                        codeNotFound = False            # 注明已经比对成功
                        break
                    else:
                        print(code)                     # 打印无效码

执行结果

883
884
885
886
887
Bingo! 888

如果密码位数比较多,只要第9行下面增加循环数即可。读者可能会想密码一般是由英文字母组成,其实用英文字母也可以,只不过是增加一些转换上的问题。

程序实例ch18_11.py:设定密码位数有3位,是由纯英文字母大写所组成。

# ch18_11.py

secretcode = 'DAY'                                              # 设定密码
codeNotFound = True                                             # 尚未找到密码为True
for i1 in range(1, 27):                                         # 第一位数
    if codeNotFound:            # 检查是否找到没有找到才会往下执行
        for i2 in range(1, 27):                                 # 第二位数
            if codeNotFound:    # 检查是否找到没有找到才会往下执行
                for i3 in range(1, 27):                         # 第三位数
                    code = chr(i1+64) + chr(i2+64) + chr(i3+64) # 组成密码
                    if code == secretcode:                      # 比对密码
                        print('Bingo!', code)
                        codeNotFound = False                    # 注明已经比对成功
                        break
                    else:
                        print(code, end=' ')                    # 打印无效码

执行结果

NG BNH BNI BNJ BNK BNL BNM BNN BNO BNP BNQ BNR BNS BNT BNU BNV BNW BNX BNY BNZ BOA BOB BOC BOD BOE BOF BOG BOH BOI BOJ BOK BOL BOM BON BOO BOP BOQ BOR BOS BOT BOU BOV BOW BOX BOY BOZ BPA BPB BPC BPD BPE BPF BPG BPH BPI BPJ BPK BPL BPM BPN BPO BPP BPQ BPR BPS BPT BPU BPV BPW BPX BPY BPZ BQA BQB BQC BQD BQE BQF BQG BQH BQI BQJ BQK BQL BQM BQN BQO BQP BQQ BQR BQS BQT BQU BQV BQW BQX BQY BQZ BRA BRB BRC BRD BRE BRF BRG BRH BRI BRJ BRK BRL BRM BRN BRO BRP BRQ BRR BRS BRT BRU BRV BRW BRX BRY BRZ BSA BSB BSC BSD BSE BSF BSG BSH BSI BSJ BSK BSL BSM BSN BSO BSP BSQ BSR BSS BST BSU BSV BSW BSX BSY BSZ BTA BTB BTC BTD BTE BTF BTG BTH BTI BTJ BTK BTL BTM BTN BTO BTP BTQ BTR BTS BTT BTU BTV BTW BTX BTY BTZ BUA BUB BUC BUD BUE BUF BUG BUH BUI BUJ BUK BUL BUM BUN BUO BUP BUQ BUR BUS BUT BUU BUV BUW BUX BUY BUZ BVA BVB BVC BVD BVE BVF BVG BVH BVI BVJ BVK BVL BVM BVN BVO BVP BVQ BVR BVS BVT BVU BVV BVW BVX BVY BVZ BWA BWB BWC BWD BWE BWF BWG BWH BWI BWJ BWK BWL BWM BWN BWO BWP BWQ BWR BWS BWT BWU BWV BWW BWX BWY BWZ BXA BXB BXC BXD BXE BXF BXG BXH BXI BXJ BXK BXL BXM BXN BXO BXP BXQ BXR BXS BXT BXU BXV BXW BXX BXY BXZ BYA BYB BYC BYD BYE BYF BYG BYH BYI BYJ BYK BYL BYM BYN BYO BYP BYQ BYR BYS BYT BYU BYV BYW BYX BYY BYZ BZA BZB BZC BZD BZE BZF BZG BZH BZI BZJ BZK BZL BZM BZN BZO BZP BZQ BZR BZS BZT BZU BZV BZW BZX BZY BZZ CAA CAB CAC CAD CAE CAF CAG CAH CAI CAJ CAK CAL CAM CAN CAO CAP CAQ CAR CAS CAT CAU CAV CAW CAX CAY CAZ CBA CBB CBC CBD CBE CBF CBG CBH CBI CBJ CBK CBL CBM CBN CBO CBP CBQ CBR CBS CBT CBU CBV CBW CBX CBY CBZ CCA CCB CCC CCD CCE CCF CCG CCH CCI CCJ CCK CCL CCM CCN CCO CCP CCQ CCR CCS CCT CCU CCV CCW CCX CCY CCZ CDA CDB CDC CDD CDE CDF CDG CDH CDI CDJ CDK CDL CDM CDN CDO CDP CDQ CDR CDS CDT CDU CDV CDW CDX CDY CDZ CEA CEB CEC CED CEE CEF CEG CEH CEI CEJ CEK CEL CEM CEN CEO CEP CEQ CER CES CET CEU CEV CEW CEX CEY CEZ CFA CFB CFC CFD CFE CFF CFG CFH CFI CFJ CFK CFL CFM CFN CFO CFP CFQ CFR CFS CFT CFU CFV CFW CFX CFY CFZ CGA CGB CGC CGD CGE CGF CGG CGH CGI CGJ CGK CGL CGM CGN CGO CGP CGQ CGR CGS CGT CGU CGV CGW CGX CGY CGZ CHA CHB CHC CHD CHE CHF CHG CHH CHI CHJ CHK CHL CHM CHN CHO CHP CHQ CHR CHS CHT CHU CHV CHW CHX CHY CHZ CIA CIB CIC CID CIE CIF CIG CIH CII CIJ CIK CIL CIM CIN CIO CIP CIQ CIR CIS CIT CIU CIV CIW CIX CIY CIZ CJA CJB CJC CJD CJE CJF CJG CJH CJI CJJ CJK CJL CJM CJN CJO CJP CJQ CJR CJS CJT CJU CJV CJW CJX CJY CJZ CKA CKB CKC CKD CKE CKF CKG CKH CKI CKJ CKK CKL CKM CKN CKO CKP CKQ CKR CKS CKT CKU CKV CKW CKX CKY CKZ CLA CLB CLC CLD CLE CLF CLG CLH CLI CLJ CLK CLL CLM CLN CLO CLP CLQ CLR CLS CLT CLU CLV CLW CLX CLY CLZ CMA CMB CMC CMD CME CMF CMG CMH CMI CMJ CMK CML CMM CMN CMO CMP CMQ CMR CMS CMT CMU CMV CMW CMX CMY CMZ CNA CNB CNC CND CNE CNF CNG CNH CNI CNJ CNK CNL CNM CNN CNO CNP CNQ CNR CNS CNT CNU CNV CNW CNX CNY CNZ COA COB COC COD COE COF COG COH COI COJ COK COL COM CON COO COP COQ COR COS COT COU COV COW COX COY COZ CPA CPB CPC CPD CPE CPF CPG CPH CPI CPJ CPK CPL CPM CPN CPO CPP CPQ CPR CPS CPT CPU CPV CPW CPX CPY CPZ CQA CQB CQC CQD CQE CQF CQG CQH CQI CQJ CQK CQL CQM CQN CQO CQP CQQ CQR CQS CQT CQU CQV CQW CQX CQY CQZ CRA CRB CRC CRD CRE CRF CRG CRH CRI CRJ CRK CRL CRM CRN CRO CRP CRQ CRR CRS CRT CRU CRV CRW CRX CRY CRZ CSA CSB CSC CSD CSE CSF CSG CSH CSI CSJ CSK CSL CSM CSN CSO CSP CSQ CSR CSS CST CSU CSV CSW CSX CSY CSZ CTA CTB CTC CTD CTE CTF CTG CTH CTI CTJ CTK CTL CTM CTN CTO CTP CTQ CTR CTS CTT CTU CTV CTW CTX CTY CTZ CUA CUB CUC CUD CUE CUF CUG CUH CUI CUJ CUK CUL CUM CUN CUO CUP CUQ CUR CUS CUT CUU CUV CUW CUX CUY CUZ CVA CVB CVC CVD CVE CVF CVG CVH CVI CVJ CVK CVL CVM CVN CVO CVP CVQ CVR CVS CVT CVU CVV CVW CVX CVY CVZ CWA CWB CWC CWD CWE CWF CWG CWH CWI CWJ CWK CWL CWM CWN CWO CWP CWQ CWR CWS CWT CWU CWV CWW CWX CWY CWZ CXA CXB CXC CXD CXE CXF CXG CXH CXI CXJ CXK CXL CXM CXN CXO CXP CXQ CXR CXS CXT CXU CXV CXW CXX CXY CXZ CYA CYB CYC CYD CYE CYF CYG CYH CYI CYJ CYK CYL CYM CYN CYO CYP CYQ CYR CYS CYT CYU CYV CYW CYX CYY CYZ CZA CZB CZC CZD CZE CZF CZG CZH CZI CZJ CZK CZL CZM CZN CZO CZP CZQ CZR CZS CZT CZU CZV CZW CZX CZY CZZ DAA DAB DAC DAD DAE DAF DAG DAH DAI DAJ DAK DAL DAM DAN DAO DAP DAQ DAR DAS DAT DAU DAV DAW DAX Bingo! DAY

由于有26个英文字母,所以所有循环均是执行26圈,range(1, 26),由于字母A的Unicode是65,所以i1(i2或i3也是)值加上64就会是相对应的英文字母,这个程序会执行所有可能的比对。

其实上述程序可以扩充到密码由大小写英文字以及数字所组成,只是所花的时间会比较久,程序运行期间所花的只是CPU时间。读到这里各位应可以体会目前几乎所有银行进入网络银行需要有验证码,这是做双重保险,同时无法由机器人操作。但是最终建议是不论在哪一种场合设定密码时,尽量由英文字母大小写与数字组成,比较安全。
在这里插入图片描述

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值