当前位置: 首页 > news >正文

用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 exifread

2. 快速提取照片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, lng

4. 集成地图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 = f"http://api.map.baidu.com/reverse_geocoding/v3/?ak={ak}&output=json&coordtype=wgs84ll&location={lat},{lng}" try: response = requests.get(url) result = response.json() if result['status'] == 0: return result['result']['formatted_address'] else: print(f"API错误: {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_file='photo_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', encoding='utf-8') as f: json.dump(results, f, ensure_ascii=False, indent=2) 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 = f"http://api.map.baidu.com/reverse_geocoding/v3/?ak={self.baidu_ak}&output=json&coordtype=wgs84ll&location={lat},{lng}" try: response = requests.get(url, timeout=5) 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. 实际应用场景与注意事项

这个工具在实际中有多种应用场景:

  1. 旅行照片管理:自动为照片添加地理位置标签,方便分类和检索
  2. 照片真实性验证:通过EXIF信息验证照片的拍摄时间和地点
  3. 地理信息分析:分析拍摄地点的分布模式
  4. 安全审计:检查照片中是否包含敏感位置信息

使用时的注意事项:

  • 隐私保护:处理他人照片前确保获得授权
  • 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调用次数
  • 添加进度显示提升用户体验
http://www.jsqmd.com/news/528924/

相关文章:

  • 如何基于 Go-kit 开发 Web 应用:从接口层到业务层再到数据层
  • 提示工程超简单
  • 告别多设备切换?这款开源工具让你的键鼠实现自由流动
  • 如何快速实现批量下载:CyberdropBunkrDownloader开源工具的终极使用指南
  • 嵌入式流体监测库:流量与热能实时计算中间件
  • Dify私有化部署安全架构全景图:从网络隔离、RBAC细粒度鉴权到审计日志全链路加密,一文吃透5大核心防线
  • FireRed-OCR Studio实操手册:日志监控+性能分析+错误追踪配置指南
  • Qwen-Image-2512-Pixel-Art-LoRA完整指南:三档生成模式(10/30/45步)性能实测对比
  • ExDark低光照图像数据集:从技术架构到前沿探索
  • 激光器温控TEC选型实战:从热负荷估算到型号敲定
  • 用docker安装测试crate数据库
  • PRDownloader完整指南:从基础使用到高级配置
  • 揭开存储设备的真实面目:存储设备验真与容量欺诈识别指南
  • ROS Noetic/Foxy安装避坑实录:手把手教你修改Python源码搞定rosdep初始化与更新
  • 实测LongCat编辑效果:从简单装饰到物种变身,效果对比展示
  • ESP32S3 变身网络魔术师:从零打造你的专属 Wi-Fi 共享神器
  • 【紧急预警】Python项目即将上线却未通过等保2.0 SM9算法测评?3小时完成国密改造并获取检测报告模板
  • 利用Granite模型进行网络安全流量异常预测:主动防御新思路
  • 企业安全自查:你的金蝶云星空私有云版本是否受这个上传漏洞影响?
  • DeepSeek-R1-Distill-Qwen-1.5B保姆级教程:环境配置到服务启动全流程
  • 零基础搭建Qwen3-Embedding-4B向量服务:SGlang部署实战指南
  • Docker Swarm Visualizer源码深度解析:EventEmitter与轮询机制如何实现实时集群监控
  • 图像加密避坑指南:Arnold变换的周期性陷阱与MATLAB优化方案
  • 轻量级全场景效率工具:GHelper华硕笔记本性能优化指南
  • 5步容器化部署Stalwart邮件服务器:现代化邮件服务配置实战指南
  • RexUniNLU开源大模型部署实操:阿里云GPU实例上一键拉起NLP分析服务
  • 微信小程序二进制包逆向工程工具unwxapkg架构深度解析
  • Qwen3-VL-4B Pro实战案例:跨境电商多语言商品图→本地化文案生成
  • OpenClaw多模型切换:Qwen3.5-4B-Claude与本地小模型协同工作方案
  • 量子禅修师:不观测时bug自动消失——软件测试中的海森堡现象与专业应对策略