使用requests拉取PNG图片时遇UnidentifiedImageError,求有效解决方法
解决requests拉取PNG时的UnidentifiedImageError问题
问题重现
使用requests拉取网页PNG图片时触发UnidentifiedImageError,无法识别损坏、截断类异常图片,且不想通过try语句直接跳过问题文件。
报错详情
UnidentifiedImageError: cannot identify image file <_io.BytesIO object at 0x7fa8aca13ef0>
报错栈:
Traceback: File "/home/user/.conda/envs/my_env/lib/python3.8/site-packages/streamlit/script_runner.py", line 333, in _run_script exec(code, module.__dict__) File "/home/user/app/app.py", line 463, in <module> img_details, message, title, invalid_file_paths, dates_df = pull_imgs(url=url, user=user, password=password, input_dir=input_dir) File "/home/user/app/app.py", line 229, in pull_imgs img = Image.open(img_bytes) File "/home/user/.conda/envs/my_env/lib/python3.8/site-packages/PIL/Image.py", line 3023, in open raise UnidentifiedImageError(
问题代码片段
for photo in valid_file_paths: # PULL PHOTO response = requests.get(photo[0], auth=(user, password), stream=True) # FILENAME file = extract_file(photo[0])[1] ext = photo[1] filename = file + '.' + ext img_bytes = io.BytesIO(response.content) img = Image.open(img_bytes) img.save(os.path.join(input_dir, filename), format=ext, optimize=True)
解决方案
1. 开启PIL的截断图片加载支持
PIL默认拒绝加载截断图片,可通过全局参数强制开启兼容:
from PIL import ImageFile ImageFile.LOAD_TRUNCATED_IMAGES = True # 之后再执行原代码的Image.open操作
2. 验证响应完整性,避免拉取不完整图片
在处理响应前,先校验请求状态和内容长度,排除因网络问题导致的截断:
response = requests.get(photo[0], auth=(user, password), stream=True) response.raise_for_status() # 确保请求返回成功状态码(200-399) # 校验内容长度匹配 if 'Content-Length' in response.headers: expected_len = int(response.headers['Content-Length']) actual_len = len(response.content) if actual_len != expected_len: print(f"图片 {photo[0]} 拉取不完整:预期{expected_len}字节,实际{actual_len}字节") # 可在此添加重新请求逻辑,或标记后手动处理
3. 使用更健壮的图片解码库
用pyspng兼容损坏PNG
pyspng对损坏、截断的PNG文件兼容性更强,安装后替换解码逻辑:
pip install pyspng
import pyspng import numpy as np from PIL import Image # 直接解码响应字节 img_data = pyspng.load(response.content) # 转换为PIL Image对象 img = Image.fromarray(np.array(img_data))
用imageio尝试多后端解码
imageio会自动调用多种解码后端,提升异常图片的识别率:
pip install imageio
import imageio from PIL import Image # 读取图片并转换为PIL格式 img_array = imageio.imread(response.content) img = Image.fromarray(img_array)
4. 手动修复PNG文件头
如果是文件头损坏,可补全标准PNG文件头后再尝试解码:
import io from PIL import Image standard_png_header = b'\x89PNG\r\n\x1a\n' img_content = response.content if not img_content.startswith(standard_png_header): # 补全标准文件头 fixed_content = standard_png_header + img_content img_bytes = io.BytesIO(fixed_content) img = Image.open(img_bytes)
5. 借助命令行工具修复复杂损坏
使用pngfix工具修复严重损坏的PNG文件,适合处理复杂异常:
# Ubuntu/Debian安装pngfix sudo apt install pngfix
import subprocess import tempfile import os # 保存原始字节到临时文件 with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp_file: tmp_file.write(response.content) tmp_path = tmp_file.name # 执行修复命令 subprocess.run(['pngfix', tmp_path], check=True) # 读取修复后的文件 img = Image.open(tmp_path) # 保存到目标路径后清理临时文件 img.save(os.path.join(input_dir, filename), format=ext, optimize=True) os.unlink(tmp_path)
内容的提问来源于stack exchange,提问作者sar
相关产品推荐
相关产品推荐

