① 基于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的官方文档。