Python入門系列20 - 5分鐘教你用圖片定位具體地址!
Python入門系列20

5分鐘教你用圖片定位具體地址!
本篇文字約為1000字,閱讀時(shí)間約為5分鐘。
1
前言
今天像上次一樣來(lái)點(diǎn)實(shí)戰(zhàn)干貨,有了面向?qū)ο笠约爸八榻B的知識(shí),即可以實(shí)現(xiàn)本章內(nèi)容。為了復(fù)習(xí)下面向?qū)ο蟮氖褂梅椒?,特意?xiě)了一篇實(shí)戰(zhàn),若有不懂得的地方,請(qǐng)回顧Python入門系列1-19。
在我們的日常生活中,經(jīng)常離不開(kāi)照相,尤其是在中國(guó)這么網(wǎng)絡(luò)發(fā)達(dá)的國(guó)家,出去旅個(gè)游或者曬個(gè)娃都會(huì)通過(guò)互聯(lián)網(wǎng)的形式將照片發(fā)到朋友圈留個(gè)紀(jì)念。那么今天的主題就是5分鐘教會(huì)你用python如何通過(guò)圖片來(lái)定位到圖片擁有者的地理信息!由于本章內(nèi)容涉及到個(gè)人隱私問(wèn)題,所以請(qǐng)遵守以下聲明。
聲明:本章內(nèi)容僅供學(xué)習(xí)記錄使用,請(qǐng)勿用于商業(yè)以及非法用途!
2
圖片定位的實(shí)現(xiàn)思路
在正式講解思路之前,先來(lái)補(bǔ)充一個(gè)知識(shí)點(diǎn),描述數(shù)據(jù)的數(shù)據(jù)信息,我們稱之為元數(shù)據(jù)!簡(jiǎn)單地舉個(gè)栗子來(lái)說(shuō):比如,有一條學(xué)生信息記錄,其中包括字段姓名(name)、年齡(age)、性別(male)、班級(jí)(class)等,那么name、age、male、class就是元數(shù)據(jù)。通過(guò)它們的描述,一條關(guān)于學(xué)生信息的數(shù)據(jù)記錄就產(chǎn)生。
相對(duì)的,圖片信息也是存在元數(shù)據(jù)的,網(wǎng)上官方稱之為exif(exchange image file format),中文意思是交換圖像文件格式。要注意的是有些圖片是沒(méi)有元數(shù)據(jù)的,比如壓縮過(guò)的圖片,元數(shù)據(jù)被破壞,無(wú)法探測(cè),所以圖片一定要是原圖。如果你想用微信朋友圈,微博的圖片來(lái)測(cè)試這次的代碼,怕是沒(méi)有希望了,因?yàn)槎际潜粔嚎s過(guò)的圖片。后續(xù)會(huì)提供一些額外思路,通過(guò)圖片來(lái)定位物理信息在網(wǎng)絡(luò)安全中還是非常有用途的。
這次用到的是python第三方庫(kù)exifread,通過(guò)此庫(kù)可以直接對(duì)圖片進(jìn)行元數(shù)據(jù)的讀取。讀取后其中有4項(xiàng)是關(guān)于GPS的經(jīng)緯度坐標(biāo),將其清洗轉(zhuǎn)化為gps在線網(wǎng)頁(yè)(http://www.gpsspg.com/maps.htm)查詢的經(jīng)緯度格式。
3
定位的演示效果以及講解
這里以我前一陣去順義為例吧,途中路過(guò)孫河附近,當(dāng)時(shí)覺(jué)得天氣還不錯(cuò),借著太陽(yáng)錯(cuò)位發(fā)出的光給路燈隨手拍了一張,圖片如下:

上面這張圖片原圖,接下來(lái)我把它放到一個(gè)文件夾下,通過(guò)命令行調(diào)用python腳本來(lái)得到它的經(jīng)緯度位置。

隨后將得到的經(jīng)緯度復(fù)制到在線查看的網(wǎng)頁(yè)上:

講解:其實(shí)明白了背后的原理,實(shí)現(xiàn)起來(lái)并不困難,來(lái)看下下面的圖,是我在手機(jī)上查看圖片的詳情信息得到的:

原理就是通過(guò)python獲取照片的這類信息!一般手機(jī)照相功能默認(rèn)是開(kāi)啟定位顯示的,所以當(dāng)你手機(jī)開(kāi)著網(wǎng)時(shí),相機(jī)會(huì)基于網(wǎng)絡(luò)對(duì)你所在的位置進(jìn)行定位,然后寫(xiě)入到照片的屬性中去,也就是上文所說(shuō)的元數(shù)據(jù)!
4
代碼的實(shí)現(xiàn)講解
1. 整體代碼:
放出整體代碼是為了復(fù)習(xí)上一章節(jié)所講到的類,也就是面向?qū)ο蟮母拍睢?/span>
#?-*-?coding:?utf-8?-*-
"""
@author:?sy
@file:?meta_picture.py
@time:?2018年12月08日21:32:49
@desc:?讀取圖片,解析其中的元數(shù)據(jù)小腳本
???????在線GPS定位網(wǎng)站:http://www.gpsspg.com/maps.htm
"""
import?os
import?exifread
class?MetaPicture(object):
????#?類變量,圖片文件夾的絕對(duì)路徑
????picture_paths?=?os.path.join(os.path.dirname(os.path.abspath(__file__)),?'picture')
????def?read_picture(self):
????????"""?讀取圖片,并調(diào)用自身提取元數(shù)據(jù)方法?"""
????????pictures?=?os.listdir(self.__class__.picture_paths)
????????for?picture?in?pictures:
????????????picture_path?=?os.path.join(self.picture_paths,?picture)
????????????self.get_picture_exif(picture_path)
????def?get_picture_exif(self,?picture_name):
????????"""?提取圖片元數(shù)據(jù)?"""
????????img_file?=?open(picture_name,?'rb')
????????picture_info?=?exifread.process_file(img_file)
????????if?picture_info:
????????????for?tag,?value?in?picture_info.items():
????????????????print(f'{tag}:{value}')
????????????print('*'?*?150)
????????????GPSLatitude?=?picture_info['GPS?GPSLatitude']??#?緯度數(shù)
????????????GPSLatitudeRef?=?picture_info['GPS?GPSLatitudeRef']??#?N,S?南北緯
????????????GPSLongitude?=?picture_info['GPS?GPSLongitude']??#?經(jīng)度數(shù)
????????????GPSLongitudeRef?=?picture_info['GPS?GPSLongitudeRef']??#?E,W?東西經(jīng)
????????????GPSDate?=?picture_info['EXIF?DateTimeOriginal']??#?拍攝時(shí)間
????????????if?GPSLatitude?and?GPSLongitude?and?GPSDate:
????????????????print(f'緯度:{GPSLatitudeRef}{GPSLatitude}\n精度:{GPSLongitudeRef}{GPSLongitude}\n拍攝時(shí)間:{GPSDate}\n')
????????????????latitude?=?self.deal_data_format(GPSLatitude)
????????????????longitude?=?self.deal_data_format(GPSLongitude)
????????????????print(f'處理后的經(jīng)緯度:【{GPSLatitudeRef}{latitude},{GPSLongitudeRef}{longitude}】')
????????else:
????????????print('請(qǐng)檢查提取的圖片是否為原圖,若為原圖,則說(shuō)明無(wú)相關(guān)元數(shù)據(jù)!')
????def?deal_data_format(self,?data):
????????"""?處理數(shù)據(jù),清洗格式生成對(duì)應(yīng)內(nèi)容?"""
????????data_list_tmp?=?str(data).replace('[',?'').replace(']',?'').split(',')
????????data_list?=?[data.strip()?for?data?in?data_list_tmp]
????????data_tmp?=?data_list[-1].split('/')
????????data_list[-1]?=?str(int(data_tmp[0])?/?int(data_tmp[1]))
????????#?為了適配gps定位網(wǎng)站的規(guī)格輸出,將列表做成度分時(shí)的狀態(tài)
????????data_list.insert(1,'°')
????????data_list.insert(3,'′′')
????????data_str?=?''.join(data_list)
????????return?data_str
def?main():
????metaPicture?=?MetaPicture()
????metaPicture.read_picture()
main()
2. 讀取圖片,獲取元數(shù)據(jù):
整體代碼有興趣可以看下,核心代碼利用了python第三方庫(kù)exifread,可以通過(guò)pip install exifread來(lái)進(jìn)行安裝(不詳細(xì)介紹了,zip爆破章節(jié)講過(guò)pip命令)。
利用python自帶的open對(duì)圖片進(jìn)行讀取,將二進(jìn)制流傳入第三方庫(kù)的方法中進(jìn)行處理,可以得到圖片信息的字典,可以看上面的gif演示中,有許多屬性,而我們關(guān)心的只是gps,所以可以取出gps相關(guān)value進(jìn)行處理打印。
def?get_picture_exif(self,?picture_name):
????????"""?提取圖片元數(shù)據(jù)?"""
????????img_file?=?open(picture_name,?'rb')
????????picture_info?=?exifread.process_file(img_file)
????????if?picture_info:
????????????for?tag,?value?in?picture_info.items():
????????????????print(f'{tag}:{value}')
????????????print('*'?*?150)
????????????GPSLatitude?=?picture_info['GPS?GPSLatitude']??#?緯度數(shù)
????????????GPSLatitudeRef?=?picture_info['GPS?GPSLatitudeRef']??#?N,S?南北緯
????????????GPSLongitude?=?picture_info['GPS?GPSLongitude']??#?經(jīng)度數(shù)
????????????GPSLongitudeRef?=?picture_info['GPS?GPSLongitudeRef']??#?E,W?東西經(jīng)
????????????GPSDate?=?picture_info['EXIF?DateTimeOriginal']??#?拍攝時(shí)間
????????????if?GPSLatitude?and?GPSLongitude?and?GPSDate:
????????????????print(f'緯度:{GPSLatitudeRef}{GPSLatitude}\n精度:{GPSLongitudeRef}{GPSLongitude}\n拍攝時(shí)間:{GPSDate}\n')
????????????????latitude?=?self.deal_data_format(GPSLatitude)
????????????????longitude?=?self.deal_data_format(GPSLongitude)
????????????????print(f'處理后的經(jīng)緯度:【{GPSLatitudeRef}{latitude},{GPSLongitudeRef}{longitude}】')
????????else:
????????????print('請(qǐng)檢查提取的圖片是否為原圖,若為原圖,則說(shuō)明無(wú)相關(guān)元數(shù)據(jù)!')
3. 清洗gps元數(shù)據(jù):
由于exifread庫(kù)取出的數(shù)據(jù)并不符合gps在線網(wǎng)站的經(jīng)緯度定位樣式,所以特意數(shù)據(jù)清理一番,將原有數(shù)據(jù)改為在線網(wǎng)站所需的格式即可。在線網(wǎng)站需要的格式為:【N40°2′′19.356,E116°31′′36.7031】,花括號(hào)逗號(hào)前面是緯度,后面是經(jīng)度。
def?deal_data_format(self,?data):
????????"""?處理數(shù)據(jù),清洗格式生成對(duì)應(yīng)內(nèi)容?"""
????????data_list_tmp?=?str(data).replace('[',?'').replace(']',?'').split(',')
????????data_list?=?[data.strip()?for?data?in?data_list_tmp]
????????data_tmp?=?data_list[-1].split('/')
????????data_list[-1]?=?str(int(data_tmp[0])?/?int(data_tmp[1]))
????????#?為了適配gps定位網(wǎng)站的規(guī)格輸出,將列表做成度分時(shí)的狀態(tài)
????????data_list.insert(1,'°')
????????data_list.insert(3,'′′')
????????data_str?=?''.join(data_list)
????????return?data_str
5
代碼中的知識(shí)點(diǎn)復(fù)習(xí)與額外知識(shí)點(diǎn)
1. 獲取圖片文件夾的絕對(duì)路徑
在《Python入門系列15 - 史上最詳細(xì)的包和模塊import講解篇》,曾經(jīng)介紹過(guò)這樣的用法,不熟悉的可以去看下,不做多余講解了。
#?類變量,圖片文件夾的絕對(duì)路徑
picture_paths?=?os.path.join(os.path.dirname(os.path.abspath(__file__)),?'picture')
2. 類變量以及實(shí)例變量的引用
上一章節(jié)講過(guò)的面向?qū)ο笾兄v過(guò),不熟悉請(qǐng)看《python小課堂19 - 面向?qū)ο笃ǘ?/span>》。
?def?read_picture(self):
????????"""?讀取圖片,并調(diào)用自身提取元數(shù)據(jù)方法?"""
????????pictures?=?os.listdir(self.__class__.picture_paths)
????????for?picture?in?pictures:
????????????picture_path?=?os.path.join(self.picture_paths,?picture)
????????????self.get_picture_exif(picture_path)
3. list內(nèi)容轉(zhuǎn)為str類型
將list中的每個(gè)元素,進(jìn)行無(wú)符號(hào)拼接,最終成為字符串,如果想讓每個(gè)元素以逗號(hào)進(jìn)行拼接,將''.join改為','.join即可。
data_str?=?''.join(data_list)
6
拓展思維使用場(chǎng)景
如果意識(shí)到元數(shù)據(jù)的重要性,其實(shí)玩出的花樣非常多,這也是網(wǎng)絡(luò)安全中取證調(diào)查的常用手法。舉兩個(gè)使用場(chǎng)景,就意識(shí)到其實(shí)這個(gè)手法很有效。
1. 假設(shè)一個(gè)士兵把他的一些帶有元數(shù)據(jù)的照片放在他的博客或者web網(wǎng)站上,那么敵軍就可以下載照片,并且在很短的時(shí)間內(nèi)掌握這名士兵的活動(dòng)軌跡。
2. 如果你想定位你女朋友的具體位置,只需要讓她發(fā)你微信一張剛拍好的原圖照片即可!前提是她手機(jī)相機(jī)打開(kāi)了“保存照片的地理位置(明白人都懂....不多說(shuō)了!
)
7
隱私的安全性
所以呢,通過(guò)本章了解到,其實(shí)我們?nèi)粘I钪械恼掌碾[私信息量是非常有利的,為了個(gè)人的隱私安全,一般不要輕易在各大網(wǎng)站上或者微信上上傳原圖(畢竟現(xiàn)在手機(jī)都聯(lián)網(wǎng),沒(méi)人會(huì)在意相機(jī)是否開(kāi)啟了保留地理位置),否則你的地理信息位置很輕易的就會(huì)暴露給一個(gè)懂元數(shù)據(jù)的人喲!
8
代碼的開(kāi)源地址
附上github開(kāi)源地址:
https://github.com/unlimitbladeworks/python-tools/tree/master/hack/meta
以上就是本章所有內(nèi)容了!至此完!
