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

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

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

Qiny01发布于 2025/2/7更新于 2026/6/622 浏览
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. 总结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Rokid JSAR 开发实战:Web 技术栈 AR 环境搭建与 3D 时钟项目解析
  • 单链表综合练习:删除指定节点、反转链表与查找中间节点
  • RAG 入门教程:LangChain 框架 v0.2 介绍
  • 二分查找算法详解:基础查找与边界定位
  • 解析前端反爬日志:精准补全 Window 对象缺失属性
  • 基于 C++ 手写 HTTP 服务器:从请求解析到响应构建
  • Python+AI 学习路线:从基础到实战
  • 基于 SSM 框架的医院医药药品管理系统设计与实现
  • 神经网络 Embedding 原理及作用详解
  • 红黑树从概念到手撕实现:平衡树的折中智慧
  • Python pip 包管理工具全面使用教程
  • 谷歌 SEO 为何离不开高质量内容创作
  • AIGC 实战:优化图文生成 20 秒与 30 秒视频的成本差异
  • AI 辅助构建高可用电商系统核心架构实战
  • Qt Creator 配置 GitHub Copilot AI 编程插件指南
  • Effective Modern C++ 条款 35:基于任务与基于线程编程的对比与实践
  • Meta-Llama-3-8B-Instruct 多轮对话实测与本地部署
  • AI 大模型提示工程技术指南:从基础概念到思维链应用
  • 字节跳动前端开发工程师面试指南与高频考点
  • C++ set 与 multiset 容器详解

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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