用Python的exifread库,5分钟搞定照片GPS定位与地址反查(附完整代码) 用Python的exifread库5分钟实现照片GPS解析与地址反查每次旅行归来手机相册里总躺着几百张照片。你有没有想过这些照片除了记录美好瞬间还隐藏着精确的地理位置信息今天我们就用Python的exifread库5分钟搞定照片GPS定位与地址反查让你的照片管理更智能。1. 准备工作与环境搭建在开始之前我们需要确保开发环境准备就绪。Python 3.6版本是必须的同时需要安装exifread库来处理照片的EXIF信息。pip install exifread为什么选择exifread库相比其他EXIF处理库exifread有以下优势轻量级无额外依赖支持JPEG和TIFF格式能够处理GPS坐标信息API简单直观提示如果你使用的是Anaconda环境也可以用conda安装conda install -c conda-forge exifread2. 快速提取照片EXIF信息让我们从一个简单的例子开始提取照片的基本EXIF信息import exifread def get_exif_data(image_path): with open(image_path, rb) as f: tags exifread.process_file(f) return tags # 使用示例 image_path sample.jpg exif_data get_exif_data(image_path) # 打印所有EXIF标签 for tag in exif_data.keys(): if tag not in (JPEGThumbnail, TIFFThumbnail, Filename, EXIF MakerNote): print(f{tag:25}: {exif_data[tag]})这段代码会输出照片的所有EXIF信息包括但不限于拍摄时间 (EXIF DateTimeOriginal)相机型号 (Image Model)光圈值 (EXIF FNumber)ISO感光度 (EXIF ISOSpeedRatings)焦距 (EXIF FocalLength)3. 解析GPS坐标信息GPS信息在EXIF中存储为度分秒格式我们需要将其转换为十进制小数形式才能用于地图API。def dms_to_decimal(dms, ref): 将度分秒格式转换为十进制小数 :param dms: 度分秒元组例如 ((31, 1), (13, 1), (4939, 100)) :param ref: 参考方向N, S, E 或 W :return: 十进制小数表示的经纬度 degrees float(dms.values[0].num) / float(dms.values[0].den) minutes float(dms.values[1].num) / float(dms.values[1].den) seconds float(dms.values[2].num) / float(dms.values[2].den) decimal degrees (minutes / 60.0) (seconds / 3600.0) if ref in [S, W]: decimal -decimal return decimal def get_gps_coordinates(exif_data): 从EXIF数据中提取GPS坐标 :param exif_data: exifread返回的标签字典 :return: 纬度, 经度 (十进制小数) gps_latitude exif_data.get(GPS GPSLatitude) gps_latitude_ref exif_data.get(GPS GPSLatitudeRef) gps_longitude exif_data.get(GPS GPSLongitude) gps_longitude_ref exif_data.get(GPS GPSLongitudeRef) if not all([gps_latitude, gps_latitude_ref, gps_longitude, gps_longitude_ref]): return None, None lat dms_to_decimal(gps_latitude, gps_latitude_ref.printable) lng dms_to_decimal(gps_longitude, gps_longitude_ref.printable) return lat, lng4. 集成地图API进行地址反查获取到经纬度后我们可以使用地图API将其转换为可读的地址。这里以百度地图API为例import requests import json def get_address_from_coordinates(ak, lat, lng): 使用百度地图API根据经纬度获取地址 :param ak: 百度地图API的AK :param lat: 纬度 :param lng: 经度 :return: 格式化地址 url fhttp://api.map.baidu.com/reverse_geocoding/v3/?ak{ak}outputjsoncoordtypewgs84lllocation{lat},{lng} try: response requests.get(url) result response.json() if result[status] 0: return result[result][formatted_address] else: print(fAPI错误: {result.get(message, 未知错误)}) return None except Exception as e: print(f请求失败: {str(e)}) return None # 使用示例 baidu_ak 你的百度地图AK # 替换为你的AK lat, lng get_gps_coordinates(exif_data) if lat and lng: address get_address_from_coordinates(baidu_ak, lat, lng) print(f拍摄地点: {address})注意使用百度地图API需要先申请开发者AK可以在百度地图开放平台免费申请基础版服务。5. 完整代码示例与批量处理现在我们把所有功能整合起来并添加批量处理能力import os import exifread import requests from concurrent.futures import ThreadPoolExecutor class PhotoGeoParser: def __init__(self, baidu_ak): self.baidu_ak baidu_ak def parse_single_photo(self, photo_path): 解析单张照片的地理信息 try: with open(photo_path, rb) as f: tags exifread.process_file(f) # 获取拍摄时间 date_time tags.get(EXIF DateTimeOriginal) date_time str(date_time) if date_time else 未知 # 获取GPS坐标 lat, lng self._get_gps_coordinates(tags) if not lat or not lng: return { filename: os.path.basename(photo_path), error: 无GPS信息 } # 获取地址 address self._get_address_from_coordinates(lat, lng) return { filename: os.path.basename(photo_path), date_time: date_time, latitude: lat, longitude: lng, address: address } except Exception as e: return { filename: os.path.basename(photo_path), error: str(e) } def batch_parse(self, photo_dir, output_filephoto_geo_info.json): 批量解析目录下的照片 photo_paths [ os.path.join(photo_dir, f) for f in os.listdir(photo_dir) if f.lower().endswith((.jpg, .jpeg, .png)) ] results [] with ThreadPoolExecutor() as executor: results list(executor.map(self.parse_single_photo, photo_paths)) # 保存结果到JSON文件 with open(output_file, w, encodingutf-8) as f: json.dump(results, f, ensure_asciiFalse, indent2) return results def _get_gps_coordinates(self, tags): 内部方法从EXIF标签获取GPS坐标 gps_latitude tags.get(GPS GPSLatitude) gps_latitude_ref tags.get(GPS GPSLatitudeRef) gps_longitude tags.get(GPS GPSLongitude) gps_longitude_ref tags.get(GPS GPSLongitudeRef) if not all([gps_latitude, gps_latitude_ref, gps_longitude, gps_longitude_ref]): return None, None lat self._dms_to_decimal(gps_latitude, gps_latitude_ref.printable) lng self._dms_to_decimal(gps_longitude, gps_longitude_ref.printable) return lat, lng def _dms_to_decimal(self, dms, ref): 内部方法度分秒转十进制 degrees float(dms.values[0].num) / float(dms.values[0].den) minutes float(dms.values[1].num) / float(dms.values[1].den) seconds float(dms.values[2].num) / float(dms.values[2].den) decimal degrees (minutes / 60.0) (seconds / 3600.0) if ref in [S, W]: decimal -decimal return decimal def _get_address_from_coordinates(self, lat, lng): 内部方法调用百度地图API获取地址 url fhttp://api.map.baidu.com/reverse_geocoding/v3/?ak{self.baidu_ak}outputjsoncoordtypewgs84lllocation{lat},{lng} try: response requests.get(url, timeout5) result response.json() if result[status] 0: return result[result][formatted_address] return None except: return None # 使用示例 if __name__ __main__: parser PhotoGeoParser(baidu_ak你的百度地图AK) # 解析单张照片 single_result parser.parse_single_photo(test.jpg) print(single_result) # 批量解析目录下的照片 batch_results parser.batch_parse(photo_directory) print(f成功解析了{len([r for r in batch_results if error not in r])}张照片的地理信息)这个完整实现提供了以下功能单张照片解析批量照片处理支持多线程结果保存为JSON文件错误处理机制6. 实际应用场景与注意事项这个工具在实际中有多种应用场景旅行照片管理自动为照片添加地理位置标签方便分类和检索照片真实性验证通过EXIF信息验证照片的拍摄时间和地点地理信息分析分析拍摄地点的分布模式安全审计检查照片中是否包含敏感位置信息使用时的注意事项隐私保护处理他人照片前确保获得授权API限制百度地图API有调用频率限制批量处理时可能需要控制速度照片格式不同设备拍摄的照片EXIF信息可能有所不同错误处理不是所有照片都包含GPS信息代码需要做好兼容处理# 错误处理示例 result parser.parse_single_photo(no_gps.jpg) if error in result: print(f处理{result[filename]}时出错: {result[error]}) else: print(f拍摄地点: {result[address]})对于需要更高性能的场景可以考虑以下优化使用异步IO提高API调用效率实现结果缓存减少API调用次数添加进度显示提升用户体验