① 基於python快速整理微信視頻與圖片
作為超級APP,微信已經成了移動互聯網的入口。而我們也早已習慣通過微信收發圖片與視頻等多媒體文件。身為一名屌絲通信狗,經常出差海外,跟家人的溝通方式主要就靠微信了,每當累了或抑鬱了,銀早打開微信看看媳婦兒和孩子的視頻照片,就覺得開心多了。最終,微信變得越來越臃腫,視頻與圖片也必須早點保存到手機或鋒亮雀者電腦。但是最終導出的文件命名是讓人相當崩潰的。。。
是能忍 孰不能忍 ,作為一名有(xia)抱(zhe)負(teng)的屌絲,是絕不能容忍這么雜亂的文件命名的,也絕不可能因此就對這些文件逐一手動重命名的。
作為批量處理文件的利器,當然非Python莫屬了,而且有一堆優質庫可供調用,基本搜索一下能解決90%需求。對於這個需求初始想法是直接通過os庫獲取文件的創始時間,依此來重新命名文件,可是最終發現os庫獲取的是文件第一次拷貝到系統的時間,並非文件的初始創建時間。比如,2016-09-10日19:00:23拷貝2016-06-16日10:00:00拍攝的視頻文件到系統硬碟,系鍵拆統就記錄其時間為2016-09-10日19:00:23,並非6月16日。
上網搜了很久,發現使用exif與ffmpeg可以分別對照片與視頻提取初始創建時間:
Exif是用來存儲數碼照片的屬性信息與拍攝數據的,可以附加於JPEG、TIFF、RIFF等文件之中,為其增加有關數碼相機拍攝信息的內容和索引圖或圖像處理軟體的版本信息 。
使用exif工具查詢當前目錄下的pic1.png文件可以得到如下信息:
MacBook:BaiYun meixuhong$ exif -i pic1.png
顯然獲取到照片拍攝時間為2016:03:08 21:22:46。
ffmpeg自然不用多說,一款開源的牛逼哄哄的絕大多數開發者與公司都在用的音頻解碼軟體。使用它解析視頻文件自然也不在話下。如使用它解析1.mov文件,則會輸出如下信息:
MacBook:BaiYun meixuhong$ ffmpeg -i 1.mov
獲取到視頻文件拍攝於2016-07-10 09:36:54。
這倆工具當然無懈可擊,可是要想用python解析的話就得使用進程調用它們,而且只能將輸出結果保存在文本中,那如果有300個文件就得要輸出300個文本文件,很復雜,顯然不是我要的結果,需要換思路。
Hachoir is a Python library to view and edit a binary stream field by field. In other words, Hachoir allows you to 「browse」 any binary stream just like you browse directories and files.
即是說使用Hachoir可以直接查看文件的二進制文件,提取文件的 metadata 即可獲取到數碼照片與視頻的初始創建時間了。
實現過程到還是到我的項目主頁 Github 一覽吧。
執行完Python腳本,再來看各個文件名,以具體拍照時間格式嚴格命名並排序,感覺整個世界瞬間變得美好了。
② 使用Python實時將gps返回的經緯度轉化為圖片
主要就是做了兩件事情:
1.生成一張有文本信息的JPG圖片
2.寫入EXIF信息
生成照片需要PIL和libjpeg
import Imageimport ImageDrawimport ImageFontdef create_pic(path, text=[], type='jpeg'):
img = Image.new("RGB", (2448, 3264), '#37b6ce')#顏色和大小
draw = ImageDraw.Draw(img)
font = ImageFont.truetype('ziti.ttf', 120)#ttf是字體,120是字型大小
for h in range(0, len(text)):#多行文本
draw.text((256, 256 + 120 * h), text[h], font=font)
img.save(path, type)#保存
# img.show()
讀寫EXIF信息需要pyexiv2,獲取google的經緯度需要geopy
順便說下經緯度的表示:
一般exif里看到的都是這樣的57°55'56.6",是度,分,秒這么展示的,google上獲取來的是十進制的57.9323888888888
所以需要轉換一下再寫進去
公式:57°55'56.6" =57+55/60+56.6/3600=57.9323888888888
etemplate=pyexiv2.ImageMetadata('IMG_4408.JPG')template.read()#exif信息很多,所以找個真正手機拍攝的照片當模版googlev3=geopy.GoogleV3()place,gps=googlev3.geocode(location)#獲取gps信息,location寫地名,比如『北京王府井』,偶爾會被牆,最好掛個代理defset_exif(path,date_time=None,gps=()):
"""
datetime=2014:10:0412:41:38
geo=(lat=39.12315,lng=115.12231)
"""
metadata=pyexiv2.ImageMetadata(path)
metadata.read()
forkintemplate.exif_keys:
metadata[k]=pyexiv2.ExifTag(k,template[k].value)
ifnotdate_time:
date_str=pyexiv2.utils.exif(date_time)
metadata['Exif.Photo.DateTimeOriginal']=date_str
metadata['Exif.Photo.DateTimeDigitized']=date_str
metadata['Exif.Image.DateTime']=date_str
iflen(geo)>0:
c_lat=decimal2coordinate(geo[0],['S','N'])
c_lng=decimal2coordinate(geo[1],['W','E'])
metadata["Exif.GPSInfo.GPSLatitude"]=coordinate2rational(c_lat[0],c_lat[1],c_lat[2])
metadata["Exif.GPSInfo.GPSLatitudeRef"]=c_lat[3]
metadata["Exif.GPSInfo.GPSLongitude"]=coordinate2rational(c_lng[0],c_lng[1],c_lng[2])
metadata["Exif.GPSInfo.GPSLongitudeRef"]=c_lng[3]
else:
metadata._delete_exif_tag("Exif.GPSInfo.GPSLatitude")
metadata._delete_exif_tag("Exif.GPSInfo.GPSLatitudeRef")
metadata._delete_exif_tag("Exif.GPSInfo.GPSLongitude")
metadata._delete_exif_tag("Exif.GPSInfo.GPSLongitudeRef")
metadata.write()defdecimal2coordinate(value,loc):
"""
loc=lat=>["S","N"],lng=>["W","E"]
retrunD,M,S,locate
"""
ifvalue<0:
loc_value=loc[0]
elifvalue>0:
loc_value=loc[1]
else:
loc_value=""
abs_value=abs(value)
deg=int(abs_value)
t1=(abs_value-deg)*60
min=int(t1)
sec=round((t1-min)*60,5)
return(deg,min,sec,loc_value)defcoordinate2rational(D,M,S):
return(fractions.Fraction(D,1),fractions.Fraction(int((M+S/60)*100),100),fractions.Fraction(0,1))
③ python遍歷excel工作表並合並單元格
代碼如下:
# -*- coding: utf-8 -*-import xlrdimport uuidclass Student():
def __init__(self, id, **kw):
self.id = id
for k, v in kw.items():
setattr(self, k, v)
def __str__(self):
return '%s(id=%s,column1=%s,column2=%s,column3=%s,column4=%s)' \ % (
self.__class__.__name__, self.id, self.column1, self.column2, self.column3,
self.column4)def read_excel():
# 打開文件
workbook = xlrd.open_workbook(r'py.xlsx')
# 獲取所有sheet
print('列印所有sheet:', workbook.sheet_names())
sheet2 = workbook.sheet_by_index(0) # sheet索引從0開始
rows_num = sheet2.nrows
cols_num = sheet2.ncols for r in range(rows_num):
# 一行數據的實體類
entity_dict = {}
for c in range(cols_num):
cell_value = sheet2.row_values(r)[c]
# print('第%d行第%d列的值:[%s]' % (r, c, sheet2.row_values(r)[c]))
if (cell_value is None or cell_value == ''):
cell_value = (get_merged_cells_value(sheet2, r, c))
# 構建Entity
the_key = 'column' + str(c + 1);
# 動態設置各屬性值
entity_dict[the_key] = cell_value
entity_dict['id'] = getUUID()
stu = Student(**entity_dict)
print(stu)def get_merged_cells(sheet):
"""
獲取所有的合並單元格,格式如下:
[(4, 5, 2, 4), (5, 6, 2, 4), (1, 4, 3, 4)]
(4, 5, 2, 4) 的含義為:行 從下標4開始,到下標5(不包含) 列 從下標2開始,到下標4(不包含),為合並單元格
:param sheet:
:return:
"""
return sheet.merged_cellsdef get_merged_cells_value(sheet, row_index, col_index):
"""
先判斷給定的單元格,是否屬於合並單元格;
如果是合並單元格,就返回合並單元格的內容
:return:
"""
merged = get_merged_cells(sheet)
for (rlow, rhigh, clow, chigh) in merged:
if (row_index >= rlow and row_index < rhigh):
if (col_index >= clow and col_index < chigh):
cell_value = sheet.cell_value(rlow, clow)
# print('該單元格[%d,%d]屬於合並單元格,值為[%s]' % (row_index, col_index, cell_value))
return cell_value break
return Nonedef getUUID():
return uuid.uuid1().hexif __name__ == "__main__":
read_excel()
④ python怎麼打開文件模式
讀寫文件是最常見的IO操作。Python內置了讀寫文件的函數,用法和C是兼容的。
讀寫文件前,我們先必須了解一下,在磁碟上讀寫文件的功能都是由操作系統提供的,現代操作系統不允許普通的程序直接操作磁碟,所以,讀寫文件就是請求操作系統打開一個文件對象(通常稱為文件描述符),然後,通過操作系統提供的介面從這個文件對象中讀取數據(讀文件),或者把數據寫入這個文件對象(寫文件)。
讀文件
要以讀文件的模式打開一個文件對象,使用Python內置的open()函數,傳入文件名和標示符:
>>> f = open('/Users/michael/test.txt', 'r')
標示符'r'表示讀,這樣,我們就成功地打開了一個文件。
如果文件不存在,open()函數就會拋出一個IOError的錯誤,並且給出錯誤碼和詳細的信息告訴你文件不存在:
>>> f=open('/Users/michael/notfound.txt', 'r')
Traceback (most recent call last):
File "<stdin>", line 1, in <mole>
FileNotFoundError: [Errno 2] No such file or directory: '/Users/michael/notfound.txt'
如果文件打開成功,接下來,調用read()方法可以一次讀取文件的全部內容,Python把內容讀到內存,用一個str對象表示:
>>> f.read()'Hello, world!'
最後一步是調用close()方法關閉文件。文件使用完畢後必須關閉,因為文件對象會佔用操作系統的資源,並且操作系統同一時間能打開的文件數量也是有限的:
>>> f.close()
由於文件讀寫時都有可能產生IOError,一旦出錯,後面的f.close()就不會調用。所以,為了保證無論是否出錯都能正確地關閉文件,我們可以使用try ... finally來實現:
try:
f = open('/path/to/file', 'r')
print(f.read())finally: if f:
f.close()
但是每次都這么寫實在太繁瑣,所以,Python引入了with語句來自動幫我們調用close()方法:
with open('/path/to/file', 'r') as f:
print(f.read())
這和前面的try ... finally是一樣的,但是代碼更佳簡潔,並且不必調用f.close()方法。
調用read()會一次性讀取文件的全部內容,如果文件有10G,內存就爆了,所以,要保險起見,可以反復調用read(size)方法,每次最多讀取size個位元組的內容。另外,調用readline()可以每次讀取一行內容,調用readlines()一次讀取所有內容並按行返回list。因此,要根據需要決定怎麼調用。
如果文件很小,read()一次性讀取最方便;如果不能確定文件大小,反復調用read(size)比較保險;如果是配置文件,調用readlines()最方便:
for line in f.readlines():
print(line.strip()) # 把末尾的'\n'刪掉
file-like Object
像open()函數返回的這種有個read()方法的對象,在Python中統稱為file-like Object。除了file外,還可以是內存的位元組流,網路流,自定義流等等。file-like Object不要求從特定類繼承,只要寫個read()方法就行。
StringIO就是在內存中創建的file-like Object,常用作臨時緩沖。
二進制文件
前面講的默認都是讀取文本文件,並且是UTF-8編碼的文本文件。要讀取二進制文件,比如圖片、視頻等等,用'rb'模式打開文件即可:
>>> f = open('/Users/michael/test.jpg', 'rb')>>> f.read()b'\xff\xd8\xff\xe1\x00\x18Exif\x00\x00...' # 十六進製表示的位元組
字元編碼
要讀取非UTF-8編碼的文本文件,需要給open()函數傳入encoding參數,例如,讀取GBK編碼的文件:
>>> f = open('/Users/michael/gbk.txt', 'r', encoding='gbk')>>> f.read()'測試'
遇到有些編碼不規范的文件,你可能會遇到UnicodeDecodeError,因為在文本文件中可能夾雜了一些非法編碼的字元。遇到這種情況,open()函數還接收一個errors參數,表示如果遇到編碼錯誤後如何處理。最簡單的方式是直接忽略:
>>> f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')
寫文件
寫文件和讀文件是一樣的,唯一區別是調用open()函數時,傳入標識符'w'或者'wb'表示寫文本文件或寫二進制文件:
>>> f = open('/Users/michael/test.txt', 'w')>>> f.write('Hello, world!')>>> f.close()
你可以反復調用write()來寫入文件,但是務必要調用f.close()來關閉文件。當我們寫文件時,操作系統往往不會立刻把數據寫入磁碟,而是放到內存緩存起來,空閑的時候再慢慢寫入。只有調用close()方法時,操作系統才保證把沒有寫入的數據全部寫入磁碟。忘記調用close()的後果是數據可能只寫了一部分到磁碟,剩下的丟失了。所以,還是用with語句來得保險:
with open('/Users/michael/test.txt', 'w') as f:
f.write('Hello, world!')
要寫入特定編碼的文本文件,請給open()函數傳入encoding參數,將字元串自動轉換成指定編碼。
細心的童鞋會發現,以'w'模式寫入文件時,如果文件已存在,會直接覆蓋(相當於刪掉後新寫入一個文件)。如果我們希望追加到文件末尾怎麼辦?可以傳入'a'以追加(append)模式寫入。
所有模式的定義及含義可以參考Python的官方文檔。