【原创】某少儿不宜网站图片拍摄位置分析 (python批量读取图片GPS位置信息)

1. python读取图片exif属性中的GPS信息

  • 智能手机或平板如果在拍照时开启定位服务,照片中就会记录拍照位置信息和拍摄时间。如果将原始照片直接发送发布到网上,无意中就泄漏了自己的位置信息,有恶意企图的人可能会通过照片分析出你的家庭住址和工作单位。
  • python有很多工具库可以解析图片的exif元数据信息,笔者喜欢使用exifread这个库。

2. 完整代码(需要使用python3)

  • 图片exif属性中的经纬度是“度,分,秒”的形式,如:[25, 34, 5927/100],需要转换成类似“16.439683,39.941649999”这样的形式。
  • 除经纬度外,还要读取GPS元数据中的东西半球和南北半球标识。
  • 经纬度转换成地理位置需要调用地图服务接口,本程序代码采用百度的逆地理编码服务API接口,调用接口需要申请服务密钥。
  • 笔者自己利用手机拍照测试,程序分析后得到的位置信息基本没有误差。
#!/bin/python
#coding:utf8
import os
import exifread
import re
import sys
import requests
import json

__author__ = 'DaDaLuLa'


#************************************************************************
#代码功能:                                                               #
#   1.读取所有图片文件的exif信息                                            #
#   2.提取图片中的经纬度,将度、分、秒转换为小数形式                           #
#   3.利用百度地图API接口将经纬度转换成地址形式                               #    
#************************************************************************


#遍历文件夹及子文件夹中的所有图片,逐个文件读取exif信息
def get_pic_GPS(pic_dir): 
    items = os.listdir(pic_dir) 
    for item in items: 
        path = os.path.join(pic_dir, item) 
        if os.path.isdir(path): 
            get_pic_GPS(path) 
        else: 
            imageread(path)

#将经纬度转换为小数形式 
def convert_to_decimal(*gps):
    #度
    if '/' in gps[0]:
        deg = gps[0].split('/')
        if deg[0] == '0' or deg[1] == '0':
            gps_d = 0
        else:
            gps_d = float(deg[0]) / float(deg[1])
    else:
        gps_d = float(gps[0])
    #分
    if '/' in gps[1]:
        minu = gps[1].split('/')
        if minu[0] == '0' or minu[1] == '0':
            gps_m = 0
        else:
            gps_m = (float(minu[0]) / float(minu[1])) / 60
    else:
        gps_m = float(gps[1]) / 60
    #秒
    if '/' in gps[2]:
        sec = gps[2].split('/')
        if sec[0] == '0' or sec[1] == '0':
            gps_s = 0
        else:
            gps_s = (float(sec[0]) / float(sec[1])) / 3600
    else:
        gps_s = float(gps[2]) / 3600

    decimal_gps = gps_d + gps_m + gps_s
    #如果是南半球或是西半球
    if gps[3] == 'W' or gps[3] == 'S' or gps[3] == "83" or gps[3] == "87":
        return str(decimal_gps * -1)
    else:
        return str(decimal_gps)


#读取图片的经纬度和拍摄时间
def imageread(path):
    f = open(path,'rb')
    GPS = {}
    Data = ""
    try:
        tags = exifread.process_file(f)
    except:
        return 
    '''
    for tag in tags:               
        print(tag,":",tags[tag])
    '''
        
        
    #南北半球标识
    if 'GPS GPSLatitudeRef' in tags:
        GPS['GPSLatitudeRef'] = str(tags['GPS GPSLatitudeRef'])
    else:
        GPS['GPSLatitudeRef'] = 'N' #缺省设置为北半球

    
    #东西半球标识
    if 'GPS GPSLongitudeRef'in tags:
        GPS['GPSLongitudeRef'] = str(tags['GPS GPSLongitudeRef'])
    else:
        GPS['GPSLongitudeRef'] = 'E' #缺省设置为东半球
    
    #海拔高度标识
    if 'GPS GPSAltitudeRef' in tags:
        GPS['GPSAltitudeRef'] =  str(tags['GPS GPSAltitudeRef'])
    
    #获取纬度
    if 'GPS GPSLatitude' in tags:
        lat = str(tags['GPS GPSLatitude'])
        #处理无效值
        if lat == '[0, 0, 0]' or lat == '[0/0, 0/0, 0/0]':
            return
            
        deg, minu, sec = [x.replace(' ', '') for x in lat[1:-1].split(',')]
        #将纬度转换为小数形式
        GPS['GPSLatitude'] = convert_to_decimal(deg, minu, sec,GPS['GPSLatitudeRef'])
    
    #获取经度
    if 'GPS GPSLongitude' in tags:
        lng = str(tags['GPS GPSLongitude'])
    
        #处理无效值
        if lng == '[0, 0, 0]' or lng == '[0/0, 0/0, 0/0]':
            return
            
        deg, minu, sec = [x.replace(' ', '') for x in lng[1:-1].split(',')]
        #将经度转换为小数形式
        GPS['GPSLongitude'] = convert_to_decimal(deg, minu, sec,GPS['GPSLongitudeRef'])#对特殊的经纬度格式进行处理

    #获取海拔高度
    if 'GPS GPSAltitude' in tags:
        GPS['GPSAltitude'] = str(tags["GPS GPSAltitude"])

    #获取图片拍摄时间
    if 'Image DateTime' in tags:
        GPS["DateTime"] = str(tags["Image DateTime"])
    elif "EXIF DateTimeOriginal" in tags:
        GPS["DateTime"] = str(tags["EXIF DateTimeOriginal"])

    if 'GPSLatitude' in GPS:
        #将经纬度转换为地址
        convert_gps_to_address(GPS)

#利用百度全球逆地理编码服务(Geocoder)Web API接口服务将经纬转换为位置信息
def convert_gps_to_address(GPS):
    secret_key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'#百度密钥
    lat, lng = GPS['GPSLatitude'], GPS['GPSLongitude']
    #注意coordtype为wgs84ll(GPS经纬度),否则定位会出现偏差
    baidu_map_api = "http://api.map.baidu.com/geocoder/v2/?coordtype=wgs84ll&location={0},{1}&output=json&pois=0&ak={2}".format(lat,lng,secret_key)
    content = requests.get(baidu_map_api).text
    gps_address = json.loads(content)
    #结构化的地址
    formatted_address = gps_address["result"]["formatted_address"]
    #国家(若需访问境外POI,需申请逆地理编码境外POI服务权限)
    country = gps_address["result"]["addressComponent"]["country"]
    #省
    province = gps_address["result"]["addressComponent"]["province"]
    #城市
    city = gps_address["result"]["addressComponent"]["city"]
    #区
    district = gps_address["result"]["addressComponent"]["district"]
    #语义化地址描述
    sematic_description = gps_address["result"]["sematic_description"]
    #将转换后的信息写入文件 
    with open("gps_address.csv","a+") as csv:
            csv.write(GPS["DateTime"] + "|" + formatted_address + "|" + country + "|" + province + "|" + city + "|" + district + "|" + sematic_description + "\n")            

if __name__ == "__main__":
    get_pic_GPS("./photo/")
  • 从网站下载多张照片,测试运行结果如下:
    2017:07:27 22:02:52|天津市河东区长征路8号|中国|天津市|天津市|河东区|水利家园东79米
    2018:10:28 17:53:32|广东省广州市天河区员村二横路|中国|广东省|广州市|天河区|程界子富新村内
    2017:08:30 05:52:54|山东省济南市济阳县经四路|中国|山东省|济南市|济阳县|嘉景苑南

3. 某色情网站照片GPS批量提取

  • 笔者无意中发现,互联网上传播的一些色情图片保留了完整的GPS等元数据信息。为了分析色情图片的拍摄位置,从某网站爬取了34万张少儿不宜的图片。利用上文中的程序进行分析,共获取了3000余张图片包含的经纬度信息,利用百度地图API接口转换成对应的精确地理位置信息
    photo.jpg
  • 经程序分析后获取的部分数据如下(多数位置信息能够精确所在的小区和酒店,以下结果对具体地址加*号处理):
    2017:10:16 15:13:43|四川省成都市双流县华阳大道4段-200|中国|四川省|成都市|双流县|成都栢顿*大酒店内
    2017:10:16 15:43:39|四川省成都市双流县华阳大道4段-200|中国|四川省|成都市|双流县|泊尔*酒店
    2017:10:24 19:41:37|四川省成都市双流县华阳大道4段-200|中国|四川省|成都市|双流县|泊尔*酒店
    2017:08:31 22:04:58|山东省潍坊市诸城市|中国|山东省|潍坊市|诸城市|万*家园内
    2017:08:31 22:05:20|山东省潍坊市诸城市|中国|山东省|潍坊市|诸城市|万*家园内
    2017:08:31 22:04:29|山东省潍坊市诸城市|中国|山东省|潍坊市|诸城市|万*家园内
    2017:08:31 22:07:36|山东省潍坊市诸城市|中国|山东省|潍坊市|诸城市|万*家园内
    2018:01:24 23:21:23|河南省许昌市长葛市铁东路|中国|河南省|许昌市|长葛市|幸*小区
    2018:01:24 23:21:20|河南省许昌市长葛市铁东路|中国|河南省|许昌市|长葛市|幸*小区
    018:08:31 22:56:52|广东省深圳市宝安区航城大道|中国|广东省|深圳市|宝安区|南航明*花园内
    2018:11:14 21:43:42|广东省深圳市宝安区航城大道|中国|广东省|深圳市|宝安区|南航明*花园内
    2018:06:21 21:43:45|辽宁省沈阳市于洪区中央大街|中国|辽宁省|沈阳市|于洪区|沈阳世纪高尔*俱乐部内
    2018:08:23 19:04:38|辽宁省沈阳市大东区滂江街|中国|辽宁省|沈阳市|大东区|龙之*畅园内
    2018:10:24 23:16:13|辽宁省沈阳市于洪区十一号街|中国|辽宁省|沈阳市|于洪区|水墨*青内
    2018:10:24 23:12:40|辽宁省沈阳市于洪区十一号街|中国|辽宁省|沈阳市|于洪区|水墨*青内
    2018:10:24 23:34:06|辽宁省沈阳市于洪区十一号街|中国|辽宁省|沈阳市|于洪区|水墨*青内
    2018:10:24 23:12:30|辽宁省沈阳市于洪区十一号街|中国|辽宁省|沈阳市|于洪区|水墨*青内
    2017:10:10 07:37:31|广东省深圳市宝安区澳桂路|中国|广东省|深圳市|宝安区|尚品*居内
    2017:10:10 07:37:36|广东省深圳市宝安区澳桂路|中国|广东省|深圳市|宝安区|尚品*居内
    2017:10:21 20:28:06|江苏省徐州市云龙区大工巷1号楼2楼|中国|江苏省|徐州市|云龙区|建*小区内
    2017:10:21 20:22:25|江苏省徐州市云龙区大工巷1号楼2楼|中国|江苏省|徐州市|云龙区|建*小区内
    2017:11:07 16:02:51|广东省深圳市宝安区澳桂路|中国|广东省|深圳市|宝安区|尚品*居内
    2017:11:07 16:02:50|广东省深圳市宝安区澳桂路|中国|广东省|深圳市|宝安区|尚品*居内
    2018:04:04 15:40:21|上海市宝山区春雷路372号|中国|上海市|上海市|宝山区|上海中*医院内
    2018:04:04 15:40:14|上海市宝山区春雷路372号|中国|上海市|上海市|宝山区|上海中*医院内
    2017:10:16 22:42:54|浙江省温州市永嘉县|中国|浙江省|温州市|永嘉县|西后村附近37米
    2018:11:14 21:44:13|广东省深圳市宝安区宝安大道5005-9|中国|广东省|深圳市|宝安区|汇*居内
    2013:05:13 00:20:17|四川省成都市成华区建设北路2段-4号|中国|四川省|成都市|成华区|电子科技大学(沙河校区)内
    2017:10:30 11:47:33|广东省汕头市龙湖区长平路东段|中国|广东省|汕头市|龙湖区|汕头龙光喜来*酒店内,龙光世纪大厦南59米
    2017:10:30 11:35:11|广东省汕头市龙湖区金环南路|中国|广东省|汕头市|龙湖区|汕头龙光喜来*酒店内,龙光世纪大厦内0米
    2017:07:28 23:11:27|山东省济南市济阳县纬一路|中国|山东省|济南市|济阳县|雅*园-四区内
    2017:07:29 05:20:51|山东省济南市济阳县纬一路|中国|山东省|济南市|济阳县|雅*园-四区内
    2017:07:28 23:12:03|山东省济南市济阳县纬一路|中国|山东省|济南市|济阳县|雅*园-四区内
    2018:01:09 13:14:27|河北省沧州市任丘市燕山南道|中国|河北省|沧州市|任丘市|源*美璟商业广场内,源*大酒店内
    2017:12:31 11:28:04|浙江省杭州市江干区秋涛北路38号|中国|浙江省|杭州市|江干区|浙江大学医学院附属邵逸夫医院(庆春院区)西南298米
    2017:12:31 11:24:09|浙江省杭州市江干区秋涛北路52号3023|中国|浙江省|杭州市|江干区|杭州钱*精品酒店内
    2018:01:13 10:47:13|浙江省杭州市上城区浣纱路18号|中国|浙江省|杭州市|上城区|浙江烟草大楼东54米
    2017:12:31 11:30:59|浙江省杭州市江干区秋涛北路52号3023|中国|浙江省|杭州市|江干区|杭州钱*精品酒店内
    2018:06:21 21:43:45|辽宁省沈阳市于洪区中央大街|中国|辽宁省|沈阳市|于洪区|沈阳世纪高*俱乐部内
    2018:08:23 19:04:38|辽宁省沈阳市大东区滂江街|中国|辽宁省|沈阳市|大东区|龙之*畅园内
    2018:10:24 23:16:13|辽宁省沈阳市于洪区十一号街|中国|辽宁省|沈阳市|于洪区|水*丹青内
    2018:10:24 23:12:40|辽宁省沈阳市于洪区十一号街|中国|辽宁省|沈阳市|于洪区|水*丹青内
    2018:10:24 23:34:06|辽宁省沈阳市于洪区十一号街|中国|辽宁省|沈阳市|于洪区|水*丹青内
    2018:10:24 23:12:30|辽宁省沈阳市于洪区十一号街|中国|辽宁省|沈阳市|于洪区|水*丹青内
    2017:08:07 21:47:14|江苏省苏州市张家港市小河坝西路|中国|江苏省|苏州市|张家港市|尚*国际内
    2017:05:01 23:17:23|江苏省苏州市张家港市小河坝西路|中国|江苏省|苏州市|张家港市|尚*国际内
    2017:05:01 22:56:49|江苏省苏州市张家港市小河坝西路|中国|江苏省|苏州市|张家港市|尚*国际内
    2017:05:01 23:13:07|江苏省苏州市张家港市小河坝西路|中国|江苏省|苏州市|张家港市|尚*国际内
    2017:09:15 09:29:44|陕西省西安市未央区浐灞二路|中国|陕西省|西安市|未央区|滹*小区西69米
    2017:09:15 09:25:36|陕西省西安市未央区浐灞二路|中国|陕西省|西安市|未央区|泘*小学内
    2017:09:15 09:27:12|陕西省西安市未央区浐灞二路|中国|陕西省|西安市|未央区|滹*锦绣-北区内
    2017:09:15 09:25:38|陕西省西安市未央区浐灞二路|中国|陕西省|西安市|未央区|泘沱小学内
    2018:08:23 23:06:26|福建省南平市建阳市黄溪路93号|中国|福建省|南平市|建阳市|金*大酒店(上水南路店)内
    2017:09:15 21:53:39|四川省成都市成华区和锦路|中国|四川省|成都市|成华区|成都*畔生活酒店(成都火车东站魅力店)
    2017:06:30 23:22:51|福建省福州市闽侯县广贤路|中国|福建省|福州市|闽侯县|福建华南*职业学院(旗山校区)内
    2017:12:04 00:32:06|福建省福州市仓山区永南路|中国|福建省|福州市|仓山区|领*新城内
    2017:08:06 10:07:41|四川省成都市新都区詹家湾路|中国|四川省|成都市|新都区|润*花园内

4. 拍摄位置分析

  • 拍摄地分析

  1. 利用python的科学库numpy、数据分析库panda、绘图库matplotlib和数据可视化库pyecharts,对获取的不良图片拍摄位置数据进行处理,分析不良图片拍摄地分布情况。
  2. 本文进对数据进行粗略分析,展示结果仅作一般性观察分析,不代表笔者任何观点和倾向。
#代码在notebook中执行
%matplotlib inline
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['Simhei']
df = pd.read_csv('gps_address.csv',sep='|')
fig = plt.figure(figsize=(15,20))
data = df["province"].value_counts()
num_of_province = data.values
plt.barh(range(len(num_of_province),0,-1),num_of_province,height=0.7,color='steelblue',alpha=0.8)
plt.title("各省分布情况",fontsize=20)
plt.xlabel("数量",fontsize=15)
plt.ylim(0,len(num_of_province)+1)
plt.yticks(range(len(num_of_province),0,-1),data.index,fontsize=15)
for x,y in enumerate(np.sort(num_of_province)):
    plt.text(y + 0.5,x + 0.9,'%s' % y,fontsize=18 )
plt.show()
province.png
fig = plt.figure(figsize=(15,40))
data = df["city"].value_counts()
num_of_city = data.values
plt.barh(range(len(num_of_city),0,-1),num_of_city,height=0.8,color='steelblue',alpha=0.8)
plt.title("城市分布情况",fontsize=20)
plt.xlabel("数量",fontsize=15)
plt.yticks(range(len(num_of_city),0,-1),data.index,fontsize=15)
plt.ylim(0,len(num_of_city)+2)
for x,y in enumerate(np.sort(num_of_city)):
    plt.text(y +0.2,x + 0.8,'%s' % y,fontsize=18 )
plt.show()
city.png
  • 各省分布情况地图展示

from pyecharts import Map, Geo
import re
provinces = list(df["province"].value_counts().index)
provinces = [re.sub("壮族自治区|自治区|省|市","",x) for x in provinces]
print(provinces)

pro_values = list(df["province"].value_counts().values)
city = list(df["province"].value_counts().index)
city_values = list(df["province"].value_counts().values)

geo = Geo("各省分布情况", "", title_color="#fff",
          title_pos="center", width=1000,
          height=600, background_color='#404a59')
geo.add("", provinces, pro_values, visual_range=[0, 200], maptype='china',visual_text_color="#fff",
        symbol_size=10, is_visualmap=True)

geo
各省分布情况.png
  • 城市分布情况地图展示

city = list(df["city"].value_counts().index)
city= [re.sub("白族自治州|市","",x) for x in city]
city_values = list(df["city"].value_counts().values)
geo = Geo("城市分布情况", "", title_color="#fff",title_pos="center", width=1000,height=600, background_color='#404a59')
geo.add("", city, city_values, visual_range=[0, 200], maptype='china',visual_text_color="#fff", symbol_size=10, is_visualmap=True)
geo
城市分布情况.png

[原创文章,转载文章内容和程序代码,请注明本文链接]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容