跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Python算法

Python 读取图片 EXIF 信息解析拍摄地理位置与时间

介绍如何使用 Python 脚本读取图片的 EXIF 元数据,提取 GPS 经纬度坐标及拍摄时间,并通过百度地图 API 将坐标转换为具体地址。文章涵盖 exifread 库的安装使用、经纬度格式转换逻辑以及 API 调用方法,同时提醒用户注意隐私保护及 API Key 的安全管理。

Qiny01发布于 2025/2/7更新于 2026/10/24101 浏览
Python 读取图片 EXIF 信息解析拍摄地理位置与时间

Python 读取图片 EXIF 信息解析拍摄地理位置与时间

背景介绍

在数字摄影中,大多数相机和手机在保存照片时会自动嵌入 EXIF(Exchangeable Image File Format)数据。这些数据通常包含拍摄时间、相机型号、光圈快门参数,以及最重要的——GPS 经纬度信息。

了解如何提取这些信息对于隐私保护、取证分析或地理标记管理具有重要意义。本文将演示如何使用 Python 脚本读取图片中的 GPS 元数据,并将其转换为具体的物理地址。

环境准备

首先,需要安装必要的 Python 库:

  • exifread:用于解析图片的 EXIF 标签。
  • requests:用于调用地图 API。
  • json:处理返回的 JSON 数据。
  • re:正则表达式匹配。

安装命令如下:

pip install exifread requests

核心原理

1. 读取 EXIF 信息

EXIF 数据存储在图片的二进制文件中。使用 exifread 库可以方便地遍历这些标签。其中,GPS 相关的标签通常以 GPS: 开头,例如 GPS:GPSLatitudeRef(纬度方向)、GPS:GPSLatitude(纬度值)等。

注意:GPS 坐标通常以度分秒(DMS)格式存储,如 [24, 24, 35] 表示 24 度 24 分 35 秒。为了进行地理编码,通常需要将其转换为十进制度数(Decimal Degrees)。

2. 经纬度转换

将 DMS 转换为十进制的公式为:

$$ \text{Decimal} = \text{Degrees} + \frac{\text{Minutes}}{60} + \frac{\text{Seconds}}{3600} $$

3. 地理编码(Geocoding)

获取经纬度后,可以通过第三方地图服务(如百度地图、高德地图)的 API 接口,将坐标反转为具体的街道地址。

代码实现

步骤一:解析图片 GPS 信息

以下函数负责读取图片文件,提取 GPS 坐标及拍摄日期。

import exifread
import re
import json

# 辅助函数:将度分秒转换为十进制
def dms_to_decimal(degrees, minutes, seconds):
    return float(degrees) + (float(minutes) / 60) + (float(seconds.split('/')[0]) / float(seconds.split('/')[-1]) / 60)

# 读取照片的 GPS 经纬度信息
def find_GPS_image(pic_path):
    GPS = {}
    date = ''
    try:
        with open(pic_path, 'rb') as f:
            tags = exifread.process_file(f)
            for tag, value in tags.items():
                # 纬度参考
                if re.match('GPS GPSLatitudeRef', tag):
                    GPS['GPSLatitudeRef'] = str(value)
                # 经度参考
                elif re.match('GPS GPSLongitudeRef', tag):
                    GPS['GPSLongitudeRef'] = str(value)
                # 海拔参考
                elif re.match('GPS GPSAltitudeRef', tag):
                    GPS['GPSAltitudeRef'] = str(value)
                
                # 纬度值
                elif re.match('GPS GPSLatitude', tag):
                    try:
                        # 尝试解析 [deg, min, sec/1] 格式
                        match_result = re.match(r'\[(\w*), (\w*), (\w.*)/(\w.*)\]', str(value))
                        if match_result:
                            groups = match_result.groups()
                            GPS['GPSLatitude'] = dms_to_decimal(groups[0], groups[1], groups[2] + '/' + groups[3])
                        else:
                            # 备用解析逻辑
                            deg, min, sec = [x.replace(' ', '') for x in str(value)[1:-1].split(',')]
                            GPS['GPSLatitude'] = dms_to_decimal(deg, min, sec)
                    except Exception:
                        pass
                
                # 经度值
                elif re.match('GPS GPSLongitude', tag):
                    try:
                        match_result = re.match(r'\[(\w*), (\w*), (\w.*)/(\w.*)\]', str(value))
                        if match_result:
                            groups = match_result.groups()
                            GPS['GPSLongitude'] = dms_to_decimal(groups[0], groups[1], groups[2] + '/' + groups[3])
                        else:
                            deg, min, sec = [x.replace(' ', '') for x in str(value)[1:-1].split(',')]
                            GPS['GPSLongitude'] = dms_to_decimal(deg, min, sec)
                    except Exception:
                        pass
                
                # 拍摄日期
                elif re.match('.*Date.*', tag):
                    date = str(value)
    except FileNotFoundError:
        print("未找到指定图片文件")
        return None
    
    return {'GPS_information': GPS, 'date_information': date}

步骤二:调用地图 API 转换地址

使用百度地图 Geocoding API 将经纬度转换为结构化地址。注意:实际使用时请替换为您自己的 AK 密钥,不要硬编码在代码中。

import requests

def find_address_from_GPS(GPS_info):
    """
    使用百度地图 API 将经纬度坐标转换为结构化地址。
    :param GPS_info: 包含 GPS 信息的字典
    :return: 地址详情元组
    """
    if not GPS_info or not GPS_info.get('GPS_information'):
        return '该照片无 GPS 信息'
    
    gps_data = GPS_info['GPS_information']
    
    # 确保有有效的经纬度
    if 'GPSLatitude' not in gps_data or 'GPSLongitude' not in gps_data:
        return '经纬度数据缺失'
    
    lat = gps_data['GPSLatitude']
    lng = gps_data['GPSLongitude']
    
    # 构建请求 URL
    # 警告:请勿将您的 Secret Key 直接提交到公共代码仓库
    secret_key = 'YOUR_BAIDU_API_SECRET_KEY'
    ak = 'YOUR_BAIDU_MAP_AK'
    
    baidu_map_api = f"http://api.map.baidu.com/geocoder/v2/?ak={ak}&callback=renderReverse&location={lat},{lng}&output=json&pois=0"
    
    try:
        response = requests.get(baidu_map_api, timeout=10)
        content = response.text
        
        # 处理回调格式
        if 'renderReverse' in content:
            content = content.replace("renderReverse(", "").rstrip(";")
        
        baidu_map_address = json.loads(content)
        
        if baidu_map_address.get('status') != 0:
            return 'API 查询失败'
            
        result = baidu_map_address.get('result', {})
        formatted_address = result.get('formatted_address', '')
        province = result.get('addressComponent', {}).get('province', '')
        city = result.get('addressComponent', {}).get('city', '')
        district = result.get('addressComponent', {}).get('district', '')
        location_desc = result.get('sematic_description', '')
        
        return formatted_address, province, city, district, location_desc
        
    except Exception as e:
        return f'网络请求错误:{str(e)}'

步骤三:完整流程整合

if __name__ == '__main__':
    # 替换为你的图片路径
    pic_path = 'C:/Users/pacer/desktop/img/photo.jpg'
    
    # 1. 读取 EXIF
    GPS_info = find_GPS_image(pic_path=pic_path)
    
    if GPS_info:
        print(f"拍摄时间:{GPS_info.get('date_information')}")
        
        # 2. 转换地址
        address = find_address_from_GPS(GPS_info)
        print(f'照片拍摄地址:{address}')
    else:
        print('无法读取图片信息')

注意事项

  1. 隐私安全:上传至社交媒体的图片若未关闭定位权限,可能泄露精确位置。建议在发布前使用工具抹除 EXIF 信息。
  2. API 配额:免费地图 API 通常有每日调用次数限制,生产环境建议申请企业级账号。
  3. 代码安全:切勿将真实的 API Key 硬编码在代码中,应通过环境变量或配置文件管理。
  4. 异常处理:实际应用中需增加更完善的异常捕获机制,防止因图片损坏或网络波动导致程序崩溃。

总结

通过本文的方法,我们可以快速验证图片的来源地信息。这对于理解数字取证的基础流程以及提升个人隐私保护意识具有实用价值。在实际开发中,建议结合更多元的数据源以提高准确性。

目录

  1. Python 读取图片 EXIF 信息解析拍摄地理位置与时间
  2. 背景介绍
  3. 环境准备
  4. 核心原理
  5. 1. 读取 EXIF 信息
  6. 2. 经纬度转换
  7. 3. 地理编码(Geocoding)
  8. 代码实现
  9. 步骤一:解析图片 GPS 信息
  10. 辅助函数:将度分秒转换为十进制
  11. 读取照片的 GPS 经纬度信息
  12. 步骤二:调用地图 API 转换地址
  13. 步骤三:完整流程整合
  14. 注意事项
  15. 总结

更多推荐文章

查看全部
  • DeepSeek 团队揭秘:清北应届生撑起大模型技术
  • 现代前端 UI 工作流:利用 AI 技能提升设计落地效率
  • UI UX Pro Max:AI 驱动的现代前端 UI 工作流实战
  • 大模型 RAG 架构落地的十大核心挑战
  • 基于 OpenClaw 与飞书构建 AI 新闻推送机器人
  • 基于本地大语言模型与 LangChain 构建免费 AI 搜索问答助手
  • 基于 Jetson 与 OpenClaw 搭建飞书远程交互系统
  • C# WinForm 调用 HeyGem 核心算法 DLL 封装实践
  • C++ 二叉搜索树:原理与增删查实现详解
  • Llama-Factory 集成 Wandb 监控:跨团队训练协作
  • 基于 Spring Boot 3 与 Vue 3 的汽车租赁系统
  • Web 虚拟卡销售平台实现方案
  • SenseVoiceSmall 语音识别 WebUI 快速部署与使用指南
  • MicroG 在 HarmonyOS 上的签名兼容方案与配置指南
  • Processing.py Python 创意编程入门指南
  • JavaEE 深度解析:从 Jakarta EE 演进、B/S 架构到 SSM 框架
  • 通义万相 2.1 结合计算平台实现 AIGC 内容生成
  • 基于 DeepFace 与 OpenCV 的实时情绪分析器实战
  • Linux 进程优先级详解:调度规则与调整方法
  • 基于 Vue 3 构建 imToken 风格钱包交互原型及核心流程验证

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online