为何PDF无保存错误却处理异常?PyPDF2读取报错排查
你遇到的问题其实很常见——用requests直接下载的PDF虽然能被Adobe打开,但因为文件末尾附带了额外的非PDF数据,导致严格遵循PDF规范的PyPDF2解析时找不到正确的%%EOF结束标记,从而报错。
为什么会出现这种情况?
很多动态生成文件的服务器(比如你这个用ImageHandler.ashx返回PDF的服务),在返回响应时可能会额外附加一些日志信息、调试内容或者响应尾部的冗余字节。浏览器打开PDF时会自动忽略这些非标准内容,但像PyPDF2这类解析库是严格按照PDF格式校验的,所以会触发PdfReadError。
你可以验证一下:用Sublime这类编辑器打开你下载的pdf_file.pdf,拉到文件最底部,肯定能看到一堆不属于PDF内容的字符(比如HTML片段、空白行、服务器日志之类的)。
几种可行的解决方案
1. 手动截断到PDF的结束标记
PDF规范里的结束标记是%%EOF,我们可以找到这个标记在下载内容里的最后出现位置,然后只保留到该位置的内容,就能得到干净的PDF文件:
import requests url = "https://icms.bombinoexp.in/ImageHandler.ashx?ID=37997&Type=BOE&FileExt=.pdf" response = requests.get(url) # 定位PDF的结束标记(二进制形式) eof_marker = b'%%EOF' # 找最后一次出现的位置,避免前面有注释里的类似字符串 eof_position = response.content.rfind(eof_marker) if eof_position != -1: # 保留到标记的末尾(%%EOF共5个字节) cleaned_content = response.content[:eof_position + len(eof_marker)] with open('clean_pdf.pdf', 'wb') as f: f.write(cleaned_content) print("清理后的PDF已保存,现在可以用PyPDF2正常处理了") else: print("未找到PDF结束标记,可能需要尝试其他方法")
2. 用兼容性更强的PDF库修复
如果手动截断嫌麻烦,推荐用**PyMuPDF(fitz)**这个库——它对不规范、损坏的PDF兼容性极强,能自动清理冗余数据并重新保存:
import fitz # 先安装:pip install pymupdf # 打开有问题的PDF doc = fitz.open("pdf_file.pdf") # 保存时自动清理垃圾数据(garbage=4表示彻底清理) doc.save("fixed_pdf.pdf", garbage=4) doc.close() print("修复后的PDF已保存")
这个方法几乎能解决绝大多数PDF解析异常的问题,比PyPDF2容错性好很多。
3. 模拟浏览器请求头(可选)
有些服务器会根据请求的User-Agent判断是爬虫还是浏览器,返回不同的内容。你可以加上浏览器的请求头,让服务器返回标准的PDF:
import requests headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36' } url = "https://icms.bombinoexp.in/ImageHandler.ashx?ID=37997&Type=BOE&FileExt=.pdf" response = requests.get(url, headers=headers) with open('pdf_file.pdf', 'wb') as f: f.write(response.content)
这个方法不一定100%有效,但可以作为补充尝试。
总结
核心问题就是下载的PDF文件被附加了冗余数据,导致PyPDF2解析失败。优先试试方案1或方案2,尤其是方案2的PyMuPDF,操作简单且兼容性强。
内容的提问来源于stack exchange,提问作者Saad Saadi

