使用urllib.request调用Google Maps API无法正常保存JPG文件
报错里提到的文件起始标识0x89 0x50是PNG格式的标准文件头,说明你拿到的文件本质就是PNG格式。urllib.request.urlretrieve只会把接口返回的二进制数据原封不动写入本地文件,不会根据你填写的文件后缀自动做格式转码。你之前只是把保存路径的后缀从.png改成了.jpg,文件实际编码格式没有任何变化,图片查看器/处理工具识别到后缀和实际格式不匹配就会抛错,手动改回PNG后缀能打开也印证了这一点。
Google Maps静态地图API默认返回PNG格式图片,你没有在请求参数里指定返回格式的话,不管你本地文件起什么后缀名,拿到的都是PNG数据。
方案1:直接指定API返回JPG格式(推荐,改动最小)
Google Maps静态地图API支持通过format参数指定返回图片格式,直接在请求URL里追加参数即可拿到原生JPG数据,不需要本地转码,完全适配现有业务流水线。
可选的JPG相关参数值:
format=jpg:返回渐进式JPG,加载时会从模糊到清晰逐步渲染format=jpg-baseline:返回标准基线JPG,兼容性最好,适合老旧的图片处理组件、嵌入式设备等场景
修改后的代码示例:
import urllib.request # 新增format=jpg-baseline参数指定返回标准JPG url = 'http://maps.googleapis.com/maps/api/staticmap?format=jpg-baseline&scale=2¢er=46.257632,6.108669&zoom=12&size=400x400&maptype=satellite&key=xxxxx' urllib.request.urlretrieve(url, 'map_46_6.jpg')
注意:卫星图场景下JPG压缩率比PNG高很多,文件体积会明显变小,如果对画质有要求可以不用改其他参数,API默认的JPG画质足够常规业务使用。
方案2:本地转码(适用于无法修改API请求参数的场景)
如果因为业务限制不能调整API请求参数,可以先把接口返回的PNG数据读取到内存,通过图片处理库转码为JPG后再保存,不需要落地临时PNG文件。
先安装依赖:pip install pillow
转码代码示例:
import urllib.request from PIL import Image import io url = 'http://maps.googleapis.com/maps/api/staticmap?scale=2¢er=46.257632,6.108669&zoom=12&size=400x400&maptype=satellite&key=xxxxx' with urllib.request.urlopen(url) as resp: # 读取接口返回的二进制数据为图片对象 img = Image.open(io.BytesIO(resp.read())) # PNG带Alpha透明通道,JPG不支持透明,需先转RGB模式 img = img.convert('RGB') # 按指定画质保存为JPG,quality参数范围0-100,数值越高画质越好 img.save('map_46_6.jpg', 'JPEG', quality=95)
转码注意:如果原PNG图存在透明区域,直接转RGB会默认把透明部分填充为黑色,需要自定义填充底色(比如白色)的话,可以先生成对应底色的RGB画布,再把PNG图粘贴到画布上再保存。
内容的提问来源于stack exchange,提问作者Ruthger Righart

