将Python同步图片下载改为异步(aiohttp)的问题排查
解决aiohttp异步获取图片转PNG bytes的报错问题
问题根源
你碰到的TypeError: unhashable type: 'JpegImageFile',多半是两个原因:
- 误把PIL返回的
JpegImageFile对象用作了需要哈希的场景(比如字典键、集合元素) - aiohttp获取响应内容的逻辑和requests不同,直接操作
response.content没正确读取字节数据
修正后的异步实现代码
先看可运行的完整代码,再拆解关键步骤:
import asyncio import aiohttp from io import BytesIO from PIL import Image async def fetch_and_convert_to_png_bytes(url): async with aiohttp.ClientSession() as session: async with session.get(url) as response: # 异步读取完整响应字节(和requests的response.content不同,必须用await) image_bytes = await response.read() # 用BytesIO包装字节流供PIL读取 img = Image.open(BytesIO(image_bytes)) # 转换为PNG格式并写入BytesIO png_buffer = BytesIO() img.save(png_buffer, format='PNG') # 重置缓冲区指针到开头,读取最终的bytes对象 png_buffer.seek(0) return png_buffer.getvalue() async def main(): # 替换成你的图片URL列表 urls = ["https://example.com/test1.jpg", "https://example.com/test2.png"] tasks = [fetch_and_convert_to_png_bytes(url) for url in urls] # 批量异步执行 png_bytes_list = await asyncio.gather(*tasks) # 验证结果 for idx, bytes_data in enumerate(png_bytes_list): print(f"第{idx+1}张图片的PNG字节长度: {len(bytes_data)}") if __name__ == "__main__": asyncio.run(main())
核心修正点
- 正确读取aiohttp响应:aiohttp的
response.content是异步迭代器,必须用await response.read()获取完整字节,不能直接像requests那样直接取response.content - 避免PIL对象误用:不要把
Image.open()返回的图片对象(比如JpegImageFile)放到需要哈希的容器里,比如别写{img: "some_value"}这种代码 - 保持PNG转换逻辑一致:和同步代码逻辑对齐,用
BytesIO做中间容器,完成格式转换后再导出为bytes对象
排查要点
如果还是报错,检查这几点:
- 确认响应状态码是200,避免拿到错误响应的无效字节
- 确保图片URL可正常访问,没有被反爬拦截导致内容异常
- 批量处理时复用
ClientSession,不要在每个请求里重复创建session(代码里已经正确实现了复用)
内容的提问来源于stack exchange,提问作者The Dan
相关产品推荐
相关产品推荐

