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

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

在数字摄影中,大多数相机和手机在保存照片时会自动嵌入 EXIF(Exchangeable Image File Format)数据。这些数据通常包含拍摄时间、相机型号、光圈快门参数,以及最重要的——GPS 经纬度信息。
了解如何提取这些信息对于隐私保护、取证分析或地理标记管理具有重要意义。本文将演示如何使用 Python 脚本读取图片中的 GPS 元数据,并将其转换为具体的物理地址。
首先,需要安装必要的 Python 库:
exifread:用于解析图片的 EXIF 标签。requests:用于调用地图 API。json:处理返回的 JSON 数据。re:正则表达式匹配。安装命令如下:
pip install exifread requests
EXIF 数据存储在图片的二进制文件中。使用 exifread 库可以方便地遍历这些标签。其中,GPS 相关的标签通常以 GPS: 开头,例如 GPS:GPSLatitudeRef(纬度方向)、GPS:GPSLatitude(纬度值)等。
注意:GPS 坐标通常以度分秒(DMS)格式存储,如 [24, 24, 35] 表示 24 度 24 分 35 秒。为了进行地理编码,通常需要将其转换为十进制度数(Decimal Degrees)。
将 DMS 转换为十进制的公式为:
$$ \text{Decimal} = \text{Degrees} + \frac{\text{Minutes}}{60} + \frac{\text{Seconds}}{3600} $$
获取经纬度后,可以通过第三方地图服务(如百度地图、高德地图)的 API 接口,将坐标反转为具体的街道地址。
以下函数负责读取图片文件,提取 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}
使用百度地图 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('无法读取图片信息')
通过本文的方法,我们可以快速验证图片的来源地信息。这对于理解数字取证的基础流程以及提升个人隐私保护意识具有实用价值。在实际开发中,建议结合更多元的数据源以提高准确性。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online