PDF文件这东西,做技术的几乎天天都会碰到。很多朋友一接到"处理PDF"的需求就在网上现找代码,要么是pypdf的过期写法,要么是某些老旧库的API变化大,复制下来跑不通。我在实际项目里断断续续折腾了几年PDF相关的自动化,从提取合同文本、生成电子发票PDF,到拆分上千页的扫描件、给报告批量加水印,踩过的坑凑一凑也够写一份完整踩坑记录了。
这篇文章就把我实际使用Python处理PDF文件的思路、方案、代码和问题排查经验整理出来。不管你只是偶尔提取一下PDF里的表格,还是想搭一个PDF批处理工具,都可以对照着来,我会把方案选型和"为什么这么选"都讲清楚。
1. 项目思路拆解:PDF处理到底在解什么题
1.1 先认清PDF是"假文档"这个本质
很多新手在处理PDF时第一次懵圈,是因为用Python打开PDF想直接改文字,却发现每行代码都在报错。这里的底层原因很关键:PDF本质上不是像Word那样存储文字和段落结构的"流式文档",它更像一张张固定在画布上的"印刷页"。每个字符的位置、字体、颜色都是被精确定位过的坐标信息,甚至有些PDF的文本根本就是曲线轮廓。
这就决定了PDF处理绝对不是什么"统一API就能搞定所有操作"的事情。你的需求不同,底层该用的库也完全不同:
- 只想提取文本内容,主要用的是解析引擎,工作量集中在解析字体编码和字符块拼接。
- 想把PDF拆开、合并、旋转、加密,主要用的是操作PDF底层对象树,重排Pages、Outlines、Metadata这些。
- 想从零生成一份美观的PDF报表,核心工作是排版引擎,得自己定义坐标系和元素。
所以做任何PDF需求,第一步永远是问自己:我要改的是"内容"还是"结构"还是"生成新文档"。方向错了,后面全是白费功夫。
1.2 常用Python PDF库的选型对比
让我直接整理一张选型表,都是我在项目里实际用过的组合,标注了具体场景下的推荐度:
| 需求场景 | 推荐库 | 说明与注意事项 |
|---|---|---|
| 读取文本、合并拆分 | pypdf(原PyPDF2) | API相对稳定,社区案例多,适合结构操作 |
| 提取高精度文本和图片 | PyMuPDF(fitz) | 底层是MuPDF引擎,渲染和提取速度极快 |
| 提取表格数据 | pdfplumber | 基于PDFMiner,能定位线和字符坐标,抽表利器 |
| 从零生成PDF | reportlab | 企业级报表、票据生成最靠谱的选择 |
| PDF转Word | pdf2docx | 开源实现,转换后版面还原度对比同类型里算高的 |
| 扫描件OCR | PaddleOCR / pytesseract | 需要配合图像预处理,见第4部分 |
| 批量添加水印与页码 | pypdf + reportlab 组合 | 一个负责画水印页面,一个负责打散合并 |
我现在的通用建议:新项目一律直接用pypdf而不是旧版PyPDF2,因为PyPDF2在2.x之后部分接口迁移到了pypdf,很多老教程的API已经过时了。选型上不要一个大而全的库硬撑所有功能,PDF处理就该"专材专用",比如pdfplumber的表格提取能力就远胜通用库,但它的文件体积和依赖也更大。
1.3 典型的PDF处理流程框架
在实际写代码之前,我会先把整个流程拆成五个环节。这个流程框架适用于绝大部分PDF自动化需求:
- 输入解析:读取PDF文件路径、加密状态、页数、元数据,判断是否可以解析。
- 内容提取:根据需求提取文本、表格、图片或指定页面的内容。
- 内容加工:定义加工规则,如替换关键词、抽取摘要、按规则改名分类。
- 结构重组或生成:合并、拆分、旋转、加密,或者绘制新页面并输出PDF。
- 结果校验:输出页数、抽样渲染出图片核对视觉效果,这一步很多人会漏掉。
这个框架看起来简单,但我自己很多次返工都是因为跳过最后一步。PDF的文本提取有个特殊性:提取出来看着是乱码,渲染成图片却是正常的,这种"伪乱码"问题不通过视觉校验很难发现,后面我会专门讲。
2. 核心功能实现:读取、提取、生成与编辑
2.1 提取PDF文本内容的正确姿势
文本提取是需求最密集的板块。这里需要区分"好提取的文本型PDF"和"需要OCR的扫描版PDF"。
先说文本型PDF。所谓文本型,指的是文字以标准文本对象(Tj、TJ操作符)存储的PDF,这种文件可以用pypdf或PyMuPDF直接提取。
使用pypdf提取文本最基础的写法是这样的:
from pypdf import PdfReader reader = PdfReader("example.pdf") print("总页数:", len(reader.pages)) for i, page in enumerate(reader.pages): text = page.extract_text() print(f"--- 第{i+1}页 ---") print(text)这里有个很多人不知道的细节:extract_text()的返回值并不是从左到右完美拼接的段落文本,而是按PDF内部内容流顺序输出的字符片段。如果你处理的是双栏排版、流程图、页眉页脚混杂的文档,提取顺序往往不对。所以我的经验是:文本提取不能只看能不能出文字,还要看顺序和完整性。
单个页面内部怎么提取得更干净?PyMuPDF提供了更精细的控制:
import fitz doc = fitz.open("example.pdf") for page in doc: text = page.get_text("text") # 按阅读顺序输出纯文本 blocks = page.get_text("blocks") # 按块输出,含坐标 words = page.get_text("words") # 按单词输出,含坐标其中get_text("blocks")很关键,它会把页面里的文本按视觉块分组并返回每个块的坐标。有了坐标信息,你甚至可以自行判断哪些块是正文、哪些是页眉页脚,做定制化过滤。这个能力在处理复杂的政府公开文件时特别有用,比如自动识别并删除"第 X 页 共 Y 页"这种页脚。
提取扫描版PDF就完全是另一条路线了。扫描件本质上是一堆图片,必须先做两层预处理:
第一层是图像增强:用OpenCV把人眼都看不清的扫描件调成可识别的效果。提高对比度、灰度化、二值化去噪点,一条基础处理链路大概是这样:
import cv2 img = cv2.imread("scan_page.png", cv2.IMREAD_GRAYSCALE) img = cv2.resize(img, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC) img = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 31, 15) cv2.imwrite("scan_page_enhanced.png", img)第二层是OCR,我建议优先用PaddleOCR,因为它的中文识别效果在开源方案里属于第一梯队。配合前面增强过的图片,识别率会有明显提升。注意一个陷阱:原PDF如果是300dpi以上扫描的,就不用强行放大图片,放大后反而容易在文字边缘产生锯齿,降低识别率。
2.2 抽取表格数据:pdfplumber的高阶用法
从PDF里抽表格是痛点中的痛点。直接看一个完整的操作示例:
import pdfplumber with pdfplumber.open("report.pdf") as pdf: page = pdf.pages[3] # 假设表格在第四页 tables = page.extract_tables() for table in tables: for row in table: cleaned_row = [cell.replace("\n", " ").strip() if cell else "" for cell in row] print(cleaned_row)如果你的表格带比较复杂的合并单元格,上面的代码可能返回很多None或错位的单元格。不要把extract_tables()当作万能方案,它本质是"按坐标对线找单元格"。
实战里我一般先画一个表格区域再进行提取,更稳妥。比如先用可视化工具确认表格所在区域的坐标(左下角和右上角的x,y),再做裁剪提取和参数调优:
with pdfplumber.open("report.pdf") as pdf: page = pdf.pages[3] bbox = (40, 150, 550, 520) # (x0, y0, x1, y1) cropped = page.crop(bbox) table = cropped.extract_table({ "vertical_strategy": "lines", "horizontal_strategy": "lines", "intersection_tolerance": 8, })这里设计参数有几个心思:vertical_strategy和horizontal_strategy都设置成lines,意思是只按实际的线条来切分单元格,避免把文字间距误判为列。intersection_tolerance默认可能太小,扫描件的线条交叉点会有几像素偏差,调大到8左右可以容忍轻微扭曲。如果表格外的文字被误包含,那就直接用crop()把区域先圈出来。
但注意,如果你拿到的PDF表格只是"看起来像表格",实际并没有线条(用制表位或空格对齐的假表格),pdfplumber的extract_table()就抓瞎了。这种只能退而求其次,先整页提取文本,再用正则按行切分。
2.3 拆分、合并与旋转PDF
合并PDF是最常见需求之一。这里有个坑特别常见:旧教程里会写PdfFileReader和PdfFileMerger,这些都是PyPDF2老版本API,在新版pypdf里已经更换了命名。新版写法是:
from pypdf import PdfWriter, PdfReader writer = PdfWriter() for file in ["a.pdf", "b.pdf", "c.pdf"]: reader = PdfReader(file) for page in reader.pages: writer.add_page(page) with open("merged.pdf", "wb") as f: writer.write(f)更灵活的做法还支持指定合并范围和旋转方向:
from pypdf import PdfReader, PdfWriter reader1 = PdfReader("a.pdf") writer = PdfWriter() writer.append_pages_from_reader(reader1, pages=[0, 1, 3]) # 按索引取页 writer.pages[0].rotate(90) # 旋转第一页 with open("partial_merged.pdf", "wb") as f: writer.write(f)旋转的语义要特别注意:rotate(90)表示顺时针旋转90度,不是逆时针。如果扫描件是反的,可以先rotate(180)转回来。合并多个PDF时还有一个经验:不同来源PDF的页面大小往往不一致,合并后输出页会默认按第一页尺寸统一,内容少的可能会留白边。若要求严格对齐,可以在合并前用page.scale_to(width, height)统一页面尺寸。
2.4 用reportlab从零生成PDF报表
如果你要生成结构化的日报、账单、发票PDF,推荐直接学reportlab。它的核心是"画布绘图"思想:先定义页面尺寸,然后在页面坐标系中绘制文本、矩形、线条和图片。
基础示例:
from reportlab.pdfgen import canvas from reportlab.lib.pagesizes import A4 c = canvas.Canvas("invoice.pdf", pagesize=A4) width, height = A4 # 宽595,高842 c.drawString(72, height - 72, "Invoice") c.drawString(72, height - 120, "Customer: Some Company") c.line(72, height - 130, width - 72, height - 130) c.save()注意,drawString的坐标系原点在左下角,y坐标从下往上增大。刚开始用的人很容易把"从页面顶部往下数100"写成height - 100,结果才画到页面中间附近,这其实是对的;更常见的错误是直接写y=100,结果内容跑到了页面底部。如果要做中文内容,必须注册中文字体,否则输出的是空方块:
from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont from reportlab.lib.pagesizes import A4 pdfmetrics.registerFont(TTFont("NotoSerifCJK", "NotoSerifCJK-Regular.ttc")) c = canvas.Canvas("chinese_demo.pdf", pagesize=A4) c.setFont("NotoSerifCJK", 12) c.drawString(72, 700, "这是中文文本") c.save()这里的本质原因在于,PDF文档内部需要一个合法的字体子集来映射字符。你不注册中文字体,canvas就会用默认字体(Helvetica),而Helvetica的字符集根本不包含中文编码,所以输出结果自然是空白或乱码。
如果要快速生成Excel风格的表格,建议直接用reportlab.platypus.Table,它比手动画线高效得多:
from reportlab.lib.pagesizes import A4 from reportlab.platypus import SimpleDocTemplate, Table, TableStyle from reportlab.lib import colors doc = SimpleDocTemplate("table_report.pdf", pagesize=A4) data = [ ["月份", "销售额", "成本"], ["1月", "120万", "80万"], ["2月", "155万", "95万"], ] table = Table(data) table.setStyle(TableStyle([ ("BACKGROUND", (0, 0), (-1, 0), colors.lightgrey), ("GRID", (0, 0), (-1, -1), 0.5, colors.black), ])) doc.build([table])用TableStyle可以统一设置各单元的样式,(0,0)到(-1,-1)这种坐标就是"从左上角第一个单元格到右下角最后一个单元格"的区域标记。这个方式生成的表格打印无压力,不需要外部渲染组件。
2.5 给PDF加水印、加密和解密
先加一个批量水印的经典方案。思路是先用reportlab生成一个只有"保密/机密"字样的透明水印PDF,再用pypdf把它的页面作为背景层覆盖到目标PDF每个页面上:
from reportlab.pdfgen import canvas from pypdf import PdfReader, PdfWriter # 生成单页水印PDF c = canvas.Canvas("watermark.pdf") c.setFont("Helvetica", 60) c.setFillAlpha(0.15) c.drawString(160, 400, "CONFIDENTIAL") c.save() watermark = PdfReader("watermark.pdf").pages[0] reader = PdfReader("target.pdf") writer = PdfWriter() for page in reader.pages: page.merge_page(watermark) # 水印作为底层合并进去 writer.add_page(page) with open("watermarked.pdf", "wb") as f: writer.write(f)merge_page这个方法我第一次用的时候就迷糊过,它并不是把水印页面叠加在当前页"上面",而是把当前页的内容叠加到水印页面上。如果你希望水印显示在最底层,直接把水印页面作为merge_page的参数即可;如果水印要浮在正文上方,逻辑上要反过来。
接着看加密与解密。PDF加密分为用户口令(打开密码)和所有者口令(权限控制)。pypdf可以设置密码:
from pypdf import PdfReader, PdfWriter reader = PdfReader("target.pdf") writer = PdfWriter() for page in reader.pages: writer.add_page(page) writer.encrypt(user_password="123456", owner_password="owner_secret", permissions_flag=-44) # 允许打印禁止修改等permissions_flag这个参数用的是PDF规范里定义的权限位图,直接记忆比较麻烦。我建议不需要精细控制的时候就别传这个参数,或者传入固定的-44(允许打印、禁止复制和修改)即可,更细节的权限控制可以查阅相关规范或文档来明确每一位的含义。
解密更简单:
from pypdf import PdfReader reader = PdfReader("encrypted.pdf") if reader.is_encrypted: result = reader.decrypt("123456") print("解密状态:", result)这里有个要注意的地方:decrypt()返回的是密码枚举等级,1表示有用户密码(可能只支持受限操作),2表示有所有者密码。只传用户密码时,有些操作可能仍然会被限制。想"打开后能编辑",必须用所有者密码解密。
3. 项目实操流程:从零完成一个PDF批处理脚本
3.1 需求设计与目录规划
讲一个我做过的实际模拟项目:某机构需要批量把指定目录里的多个PDF报告合并成一个总报告,并对其中含"重要"标题的页面加醒目水印,最后统一加密分发。这个需求涵盖了合并、水印、加密三个核心功能,非常适合拿来展示完整处理流程。
项目目录规划如下:
pdf_processor/ ├── input/ # 待处理PDF文件 ├── output/ # 处理后的文件输出 ├── assets/ # 水印模板等中间产物 └── process_reports.py # 主处理脚本不要试图一次性写一个巨型脚本。在代码工程上,我会拆成四个模块:文件遍历与排序、页面筛选、水印生成与合并、加密输出。这样每个模块都可以独立测试,排查问题时定位极快。
3.2 主流程代码实现
先看一下主流程脚本的核心框架:
from pathlib import Path from pypdf import PdfReader, PdfWriter from reportlab.pdfgen import canvas INPUT_DIR = Path("input") OUTPUT_DIR = Path("output") ASSET_DIR = Path("assets") def make_watermark(text="IMPORTANT"): wm_path = ASSET_DIR / "watermark.pdf" c = canvas.Canvas(str(wm_path)) c.setFont("Helvetica-Bold", 52) c.setFillAlpha(0.18) c.setFillColorRGB(0.9, 0.1, 0.1) c.rotate(35) c.drawString(150, 30, text) c.save() return wm_path def collect_files(): pdf_files = list(INPUT_DIR.glob("*.pdf")) assert pdf_files, "input目录下没有PDF文件" return sorted(pdf_files, key=lambda x: x.name) def process_pdf(files): writer = PdfWriter() wm_page = PdfReader(str(make_watermark())).pages[0] for pdf_path in files: reader = PdfReader(str(pdf_path)) for page_num, page in enumerate(reader.pages): text = page.extract_text() or "" if "重要" in text: page.merge_page(wm_page) writer.add_page(page) return writer if __name__ == "__main__": files = collect_files() writer = process_pdf(files) writer.encrypt(user_password="Secret123") OUTPUT_DIR.mkdir(exist_ok=True) with open(OUTPUT_DIR / "merged_protected.pdf", "wb") as f: writer.write(f) print("任务完成,输出文件路径:", OUTPUT_DIR / "merged_protected.pdf")这个流程里有两个值得注意的细节:
一是水印模板随处理脚本在运行期生成,不单独保存在assets里。原因是我把水印文字作为参数传入,方便后续改成日期或者其他文案。但如果你要统一品牌水印,把水印PDF作为静态资源固定下来,运行效率更高。
二是page.extract_text()返回的可能是空字符串。空字符串说明该页是纯图片扫描页,这页就不会命中关键词。如果业务上有需求"对扫描页也判断重要级别",那就必须先接入OCR识别,这个我在前面第2部分已经讲了基本的处理思路。
3.3 运行验证与参数调整
脚本运行后,两个关键验证必不可少:
- 检查输出文件页数:
len(PdfReader(output_path).pages)应等于所有输入PDF页数之和。 - 渲染抽样页为图片,人工核对水印位置和清晰度。可以使用PyMuPDF把页面转成PNG来检查:
import fitz doc = fitz.open(OUTPUT_DIR / "merged_protected.pdf") page = doc[0] pix = page.get_pixmap(dpi=96) pix.save("preview_page_0.png")为什么要渲染成图片检查?因为水印是半透明红色且旋转后的文字,如果透明度设置过高,在屏幕上几乎看不见;如果旋转角度不好,文字可能超出页面可打印区域。直接看最终的位图效果才是最快、最直观的排查方式。
另外,加密后的PDF会不会因为密码被其他程序拒绝打开?实际业务里经常遇到客户用极老的工具打开文件,某些老工具不支持高版本加密算法。如果你遇到兼容性问题,可以回退到较低加密级别。pypdf的encrypt方法里还有一个参数algorithm,默认情况下会采用较高的加密算法,兼容性需要时再显式指定为低版本算法。
4. 常见问题与实战排查心得
4.1 提取文本出现乱码或空白
这个问题出现频次最高。如果是文本型PDF提取出乱码,原因多数是字体编码映射失败。内置字体(非嵌入字体)在PDF里只存放了字符编码,不存放字形信息,解析端需要猜测映射表。解决办法如下:
- 优先用PyMuPDF试试,它的内部渲染引擎对很多收藏类PDF支持更好。
- 检查PDF是否真的文本型:用PyMuPDF渲染页面为图片,如果图片里文字清晰而提取为空,说明是扫描件,必须走OCR。
- 检查PDF是否带保护:先解密再提取,空结果也可能是因为只读了加密文件的外部信息。
4.2 pypdf与PyPDF2的新旧API混淆
很多老教程是2021年左右写的,API是PdfFileReader、PdfFileWriter、PdfFileMerger。如果你安装的是新版pypdf,这些类名已经改名了。看到from PyPDF2 import PdfFileReader报错时,优先检查你装的是不是pypdf的新版本,然后统一改成PdfReader/PdfWriter/PdfMerger。不要同时混装PyPDF2和pypdf,两个包都提供类似类名,Python导入顺序不同会导致诡异报错。
4.3 表格提取错位与漏列
错位最常见的原因是单元格内换行符太多,导致pdfplumber解析时认为已经进入下一行。我的处理方案是:在提取之前,先把表格单元格里的换行符统一替换成空格再清洗,然后基于清洗后的二维数组重建表格。如果你遇到带有合并单元格的表格,pdfplumber支持单页内返回raw text和布局信息,你需要自己根据坐标做二次合并。这种场景很费时间,建议在项目早期确认表格复杂度,评估是否需要对复杂表格投入专门开发。
4.4 处理大文件时的内存问题
PDF几千页甚至上万页时,一次性把所有页面加载进writer会导致内存暴涨。切记要用流式策略:合并时按批处理,比如每200页就写一次临时文件,最后再合并临时文件。或者改用PdfWriter配合文件流时,用追加模式写入,避免保存所有页面对象。这个在高强度批量生产场景是刚需。
4.5 相关问题的实战策略速查表
我把日常遇到的高频问题按"现象-原因-解决路径"整理成一张速查表,方便你直接对照处理:
| 现象 | 根本原因 | 解决路径 |
|---|---|---|
| 提取文本为空 | 扫描件/图片型PDF | 改用OCR流程,预处理图文后识别 |
| 提取文本乱码 | 字体未嵌入或CMap缺失 | 尝试PyMuPDF,设置fontname过滤 |
| 生成的PDF中文空白 | 未注册中文字体 | 注册TTF字体后设定字体名 |
| 合并后页面尺寸不一 | 原页面源不同尺寸 | 统一scale_to()或设置固定页面尺寸 |
| 解密后仍不能编辑 | 未用所有者密码 | 用所有者密码解密或对照权限位设置 |
| 输出PDF内存溢出 | 大文件页对象全在内存 | 流式批量消费与临时文件合并 |
| 读取慢 | 每个页面都要重新读文件 | 一次加载到Reader,按页索引访问 |
这张表不用背,遇到问题时回来看一眼,定位效率比翻源码高得多。
4.6 我的几条避坑经验
做PDF处理踩过几次坑之后,我给自己定了几条规矩:
第一,拿到PDF先执行体检。把页数、是否加密、是否有内嵌字体、页面尺寸分布全部打印出来。很多问题在动手前就能暴露,比如客户发来的所谓"PDF",打开才发现是CAD导出的图片型图纸,这样从一开始就走OCR路线,不浪费时间。
第二,处理前永远保留原始文件副本。PDF是"所见即所得"最典型的代表,处理结果是否符合预期,除了代码层面检查外,视觉上必须抽检。保留原始副本就是让你可以随时回溯对比,不至于把原始数据弄丢。
第三,不要盲目追求"全能"的PDF库。我遇到过有人为了提取表格,同时引入了PyMuPDF、pdfplumber、pypdf、fitz好几个库,最后主流程又长又难维护。每个库只做它最擅长的事,其他交给另一个库,接口边界清晰,这样代码大概率不会失控。
5. 扩展思路:从单文件处理走向PDF自动化工作流
5.1 文件夹监控与定时任务的落地方法
很多读者的实际需求比处理单文件更复杂:每天都有新PDF进来,要自动合并、改名、发送。这种场景不建议写死脚本手动执行,而是把上面的代码改造成服务化工作流。
我常用的是watchdog库监控文件夹变化,配合脚本自动出发处理:
from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler class PdfHandler(FileSystemEventHandler): def on_created(self, event): if event.src_path.endswith(".pdf"): print(f"检测到新PDF:{event.src_path}") process_pdf([event.src_path]) observer = Observer() observer.schedule(PdfHandler(), "input", recursive=False) observer.start()配合任务计划程序(Windows)或Cron(Linux),就能做到定时、实时处理。这类自动化脚本胜在轻量,不需要起一个完整的Web服务。如果业务复杂到要多人使用、需要有操作界面,那还是把核心处理逻辑封装成独立模块,外接调度框架,保持处理逻辑的纯粹性。
5.2 结合OCR服务做全自动电子化归档
对扫描件档案类业务,最完整的自动化链路是:PyMuPDF负责把PDF每页渲染成高分辨率图片,OpenCV做去污、纠偏和增强,PaddleOCR负责文字识别输出结构化文本,pypdf负责将原始扫描件拆分成单页档案,再按识别出的关键字段(如发票号、日期)重命名归档。
这条链路每一步我都在前文中给出了基础代码示例,组合起来就是一个小型文档管理系统。需要注意OCR这个环节的计算开销比较大,制作一个100页的扫描文档可能要等几分钟,可以做异步任务队列,在不影响主业务的情况下在后台跑。
5.3 对项目整体方案的几点复盘
复盘这个PDF批处理项目,有三个决策让我觉得最值回票价:
第一,把"选型"放在"写代码"前面。我最初图省事,尝试一个库弄完全部需求,结果在表格提取上浪费了整整一天。后来回到"专材专用"的选型逻辑,半天就解决了表格问题。
第二,把"校验"放在"输出"前面。早期我以为输出PDF后就大功告成,结果被用户反馈"字体有问题"才回头仔细看。现在不管多赶,都强制加一步视觉抽检,这个动作省下来的沟通成本远超运行成本。
第三,把"处理流程"抽象成"五步框架"。页数统计、内容提取、规则匹配、重组输出、精选检查,这个流程不只适用于PDF处理,后来我在处理Word、Excel批处理办公自动化项目时也套用了它,开发和沟通效率都明显提升。
PDF处理用Python来做,真正难的不是代码本身,而是对PDF文件底层结构和不同库适用边界的理解。希望这篇实操总结,能让你少走点弯路。后面如果再碰到什么特别的PDF怪问题,欢迎交流思路。