)
用 Python 提取图片元数据EXIF、文件作者与 GPS 定位全解析Get_meta_information_of_images 实战【免费下载链接】python-mini-projectsA collection of simple python mini projects to enhance your python skills项目地址: https://gitcode.com/gh_mirrors/py/python-mini-projects本文围绕 python-mini-projects 仓库中的 Get_meta_information_of_images 小项目系统讲解如何用 Python 从一张图片中提取完整元数据——包括基础信息、EXIF 拍摄参数、文件创建时间、Windows 文件所有者作者以及基于 GPS 坐标的反向地理定位。读完本文你将掌握该工具的全部运行方式、每个输出字段背后的源码实现原理以及其中的平台依赖与使用限制可直接迁移到自己的图片管理、取证分析或自动化归档场景中。项目定位一张图片能读出多少信息这个迷你项目在命令行接收一个图片文件路径一次性输出 9 类信息文件名、像素尺寸、文件扩展名、EXIF 图片宽高、原始拍摄时间、文件创建时间、作者Windows 文件所有者以及拍摄地点GPS 反向地理编码。整个项目只有 4 个文件职责非常清晰文件职责get_meta_from_pic.py程序入口组织所有元数据的读取与打印输出author_utils.py基于 Windows 安全描述符Security Descriptor提取文件所有者作为作者gps_utils.py解析 EXIF 中的 GPS 坐标并反向地理编码为地址requirements.txt声明项目依赖PIL、ExifRead、geopy主程序 get_meta_from_pic.py 直接import了 author_utils 的get_author与 gps_utils 的get_location三个模块各自独立、通过命令行参数sys.argv[1]共享同一个图片路径结构简单非常适合作为学习图片元数据处理的入门范例。环境准备与快速上手安装依赖依赖声明在 requirements.txt 中共 3 个包PIL1.1.6 ExifRead2.3.1 geopy2.0.0安装命令pip install -r requirements.txt说明PIL用于读取 EXIF 基础信息ExifRead包名exifread用于读取 GPS 坐标原始值geopy用于调用 Nominatim 服务完成反向地理编码。运行方式按 README.md 中的用法在项目目录下执行python get_meta_from_pic.py image_file其中image_file为待分析的图片路径。从源码看主程序通过Image.open(sys.argv[1])打开图片因此运行目录不要求固定只要sys.argv[1]指向真实存在的图片即可。输出字段一览程序通过一系列print输出结果字段与含义对应如下输出字段含义数据来源ImageName图片文件名im.filenamesize像素尺寸宽×高im.sizeFileExtension文件扩展名os.path.splitextImageWidth/ImageHeightEXIF 记录的宽高EXIF 标签DateTimeOriginal原始拍摄时间EXIF 标签CreateDate文件创建时间os.path.getctimeAuthor文件所有者作者Windows 安全描述符Location拍摄地点地址GPS 反向地理编码基于 get_meta_from_pic.py 的打印逻辑一次典型运行的效果示意具体值取决于输入图片如下ImageName: sample.jpg size: 4032x3024 FileExtension: .jpg ImageWidth: 4032 ImageHeight: 3024 DateTimeOriginal: 2021:07:11 15:26:33 CreateDate: 2021-07-11 15:30:02 Author: DESKTOP-ABC\owner Location: ...核心源码解析EXIF 提取链路从_getexif()到可读标签主程序对 EXIF 的处理集中在两个函数中get_meta_from_pic.pydef get_exif(image): image.verify() return image._getexif() def get_labeled_exif(exif): labeled {} for (key, val) in exif.items(): labeled[TAGS.get(key)] val return labeledget_exif先调用image.verify()对图片文件做完整性校验再通过 Pillow 的_getexif()拿到原始 EXIF 字典。由于 EXIF 的键是数字 ID如0x9003对应拍摄时间get_labeled_exif使用PIL.ExifTags.TAGS这个 ID→标签名的映射表把数字键翻译成DateTimeOriginal、ExifImageWidth这类可读字段名再存入labeled字典供后续取值。缺失字段的容错处理并非所有图片都带 EXIF 数据。程序对宽高、拍摄时间采用了显式容错只有当ExifImageWidth、ExifImageHeight、DateTimeOriginal出现在labeled字典中时才输出真实值否则打印No ImageWidth/No ImageHeight/No DateTimeOriginal见 get_meta_from_pic.py。这意味着经过压缩、转发或截图工具处理过的图片EXIF 常被剥离仍能正常运行只是相关字段显示为缺失标记——这一点对实际使用非常重要。基础信息与创建时间图片名、尺寸、扩展名分别来自im.filename、im.size和os.path.splitext(sys.argv[1])创建时间则调用os.path.getctime(sys.argv[1])拿到时间戳再用datetime.fromtimestamp(...).strftime(%Y-%m-%d %H:%M:%S)格式化为可读字符串get_meta_from_pic.py。需要留意的是在 Linux 上getctime实际返回的是 inode 变更时间而非严格的创建时间在 Windows 上才是真正的文件创建时间这也是后续限制小节要讨论的平台差异之一。作者信息提取Windows 文件安全描述符Author 字段的实现非常有特色——它不是从图片内嵌元数据读取的而是通过 Windows 的文件系统安全机制拿到文件的所有者账户名。相关代码全部位于 author_utils.py。ctypes 调用 Win32 API该模块用ctypes.WinDLL加载了kernel32与advapi32两个系统 DLL并通过声明参数类型argtypes/restype/errcheck逐一定义了ConvertSidToStringSidW、LookupAccountSidW、GetNamedSecurityInfoW等 API 的调用契约见 author_utils.py。其中SE_FILE_OBJECT 1表示查询对象是文件_DEFAULT_SECURITY_INFORMATION组合了 OWNER、GROUP、DACL、LABEL 等安全信息位用于告诉系统需要取哪些数据。完整调用链get_authorauthor_utils.py的调用链可以概括为四步处理文件名编码若传入的是bytes优先用os.fsdecode解码否则回退到sys.getfilesystemencoding()调用get_file_security(filename)author_utils.py内部通过GetNamedSecurityInfoW查询文件安全描述符返回封装好的PSECURITY_DESCRIPTOR对象通过pSD.get_owner()得到所有者的 SID再调用look_up_account_sidauthor_utils.py用LookupAccountSidW把 SID 反查为账户名与域名如果存在域信息则把输出拼接为域\账户名的形式owner_domain \\ owner_name最后返回。其中PSID对象重载了__str__用ConvertSidToStringSidW把二进制 SID 转成S-1-5-21-...的字符串形式方便调试与展示PLOCAL类则负责在对象销毁时调用kernel32.LocalFree释放GetNamedSecurityInfoW分配的内存author_utils.py避免句柄与内存泄漏。平台限制从代码结构可以明确看出author_utils.py完全依赖ctypes.WinDLL(kernel32)与advapi32只能在 Windows 上运行。在 Linux/macOS 上执行会因找不到对应的 Win32 DLL 而失败同时在部分文件系统上GetNamedSecurityInfoW本身也可能返回ERROR_INVALID_FUNCTION代码注释中也做了此提示。因此该字段本质上是 Windows 平台专属能力。GPS 定位从经纬度到地理地址Location 字段是本项目另一个亮点实现于 gps_utils.py。读取 EXIF 中的 GPS 数据def get_location(filename): imgexifread.process_file(open(filename,rb)) latitudeformat_lati_long(str(img[GPS GPSLatitude])) longitudeformat_lati_long(str(img[GPS GPSLongitude])) geolocator Nominatim(user_agent your email) position geolocator.reverse(str(latitude) , str(longitude)) return position.addressexifread.process_file读取原始 EXIF 数据其中GPS GPSLatitude与GPS GPSLongitude分别对应 EXIF GPS 标签中的纬度和经度。度分秒DMS转十进制EXIF 中 GPS 坐标以度/分/秒三元组形式存储format_lati_longgps_utils.py负责把它换算成十进制度数def format_lati_long(data): list_tmpstr(data).replace([, ).replace(], ).split(,) list[ele.strip() for ele in list_tmp] if (list[-1].find(/) ! -1): data_sec int(list[-1].split(/)[0]) /(int(list[-1].split(/)[1])*3600) else: data_sec int(list[-1])/3600 data_minute int(list[1])/60 data_degree int(list[0]) resultdata_degree data_minute data_sec return result其换算逻辑为把元组字符串去掉方括号后按逗号切分取出度、分、秒三个分量秒值若是分数形式含/即 EXIF 常见的 Rational 表示先做分子/分母运算再除以 3600最终按十进制度 度 分/60 秒/3600计算。这也解释了为什么 EXIF 中的45°3015会被正确输出为45.50417这样的十进制坐标。反向地理编码与 user_agent 配置拿到十进制经纬度后程序用geopy的Nominatim执行反向地理编码reverse geocoding把坐标反查为可读地址例如某街道、某城市。这里有一个 README 中明确要求配置的要点you need fill in your email address to use the function in gps_utils.py:geolocator Nominatim(user_agent your email)即 gps_utils.py 中Nominatim(user_agent your email)的user_agent参数需要替换成你自己的邮箱地址。这是 Nominatim 开放服务的规范要求——它依赖user_agent标识调用方以便联系与限流管理。不改动该参数直接运行反向地理编码请求可能被服务方拒绝或无法正常返回地址这是该项目使用中最容易踩的坑。使用注意事项与已知限制综合 README 说明与源码实现实际使用中需要关注以下几点图片必须包含位置信息README 明确提示Make sure the picture contains location information, otherwise the location cannot be obtained。从源码看若图片无 GPS 标签img[GPS GPSLatitude]会触发KeyError因此对无定位信息的图片Location字段无法获取——建议先用带 GPS 的手机原图测试。记得配置 user_agent如上一节所述必须将 gps_utils.py 中的user_agent替换为你的邮箱否则地理编码可能失败。Author 字段仅限 Windows作者信息依赖 Win32 APIkernel32/advapi32在 Linux/macOS 上不可用。EXIF 并非总是存在程序对宽高、拍摄时间做了No ...容错输出不会因 EXIF 缺失而崩溃但 GPS 字段没有同级容错这是源码层面的一个可留意差异。创建时间的平台语义差异os.path.getctime在 Windows 上是文件创建时间在 Linux 上则代表 inode 变更时间。隐私提示EXIF 中的 GPS 坐标可精确到街道级别分享照片前如不希望暴露拍摄位置应先剥离 EXIF 元数据。扩展思路在理解上述实现后可以基于现有结构做进一步扩展例如为 GPS 缺失增加 try/except 容错、把sys.argv[1]改为argparse支持批量目录扫描、或将 EXIF 标签完整导出为 JSON/CSV 便于归档。项目的模块化拆分EXIF 解析、作者提取、GPS 定位各自独立成文件也为复用提供了很好的基础——这三个utils模块可以直接被其他脚本 import单独提取某类元数据而无需运行完整主程序。【免费下载链接】python-mini-projectsA collection of simple python mini projects to enhance your python skills项目地址: https://gitcode.com/gh_mirrors/py/python-mini-projects创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考