2007-07-04
Python练习:在线更新工具(07-04)
每天学习一点Python Cookbook,总觉得不练习还是不能掌握要领,于是想写一个小工具:
1.能根据配置文件,在线更新需要的工具
2.很多绿色软件没有提供在线更新功能(如SciTe,还有卡巴的病毒库),而只是给出了更新的地址
3.更新地址有的是固定的(如卡巴的病毒库,360等),也有是不固定的(连接地址总是体现为文件名的变化)
首先实现最简单的功能:
1.读写配置文件
2.下载有固定地址的文件 ,判断是否更新的依据是文件的大小,也就是html头中的Content-Length字段
3.对于变化地址的文件,分析其下载页面,找出需要的地址,如对于PyScripter,下载页面为http://pyscripter.googlepages.com/,而更新的地址可以写成: http://pyscripter.googlecode.com/files/PyScripterv.+zip,使用了简单的正则式.
对于配置文件,先定义一下它的格式:
本地文件名,文件大小,保存目录,下载地址,下载页地址,正则式字符串
......
使用逗号分割字段,以后好处理,对于有固定地址的文件,可以没有后两项.
为了方便处理,定义一个简单的数据结构:
class UpdateItem:
'''
test.zip,20,c:\temp,http://www.xxx.com,
'''
def __init__(self,sequence):
self.fileName = sequence[0]
self.fileSize = int(sequence[1])
self.filePath = sequence[2]
self.url = sequence[3]
self.len = 4
if len(sequence) > 4:
self.pageUrl = sequence[4]
self.regstr = sequence[5]
self.len = 6
def __str__(self):
str1 = ','.join((self.fileName,str(self.fileSize),self.filePath,self.url))
if self.len != 4:
str1 = ','.join((str1, self.pageUrl,self.regstr))
return str1
def __len__(self):
return self.len
其中的变量保存配置文件中对应的字段,构造函数中传递的sequnce列表是为了方便代码的书写.
__len__方法是为了日后判断是否有后两个字段用的.
__str__方法是为了以后写配置文件时方便,直接用str(item)就可以了.
还是用java的思路来写Python代码,所以看起来比较怪,哈哈.
关于读配置文件,我决定首先要求用写一个简单的配置文件,否则程序进行不下去啊:
if __name__ == '__main__':
'''
'''
lists = []
if os.path.exists('updater.dat '):
f = open('updater.dat')
try:
for line in f:
args = line.split(',')
lists.append(UpdateItem(args))
finally:
f.close()
else:
print 'Make a config file first!'
这里为了方便,我们定义配置文件叫updater.dat了,用一个列表保存所有需要下载的项.
关于写配置文件,当下载完成后,需要更新数据,可以这样写:
f = open('updater.dat','w')
try:
for item in lists:
f.write(str(item))
finally:
f.close()
这就用到了UpdateItem的__str__方法了.
下面就是程序的关键了部分:下载,首先是主程序的接口:
for item in lists:
parse = UrlParse(item)
parse.download()
我们定义一个类,UrlParse,接收下载配置项,然后提供分析和下载的功能:
class UrlParse:
def __init__(self, item):
self.item = item
里面保留一个item对象,方便以后处理.
在下载以前,首先要判断是否需要更新,仅仅判断html头部就可以了:
def isNewSize(self, oldsize):
self.socket = urllib.urlopen(self.item.url)
fileSize = int(self.socket.headers['Content-Length'])
if oldsize == fileSize:
return False
self.item.fileSize = fileSize
return True
如果文件大小发生了变化,就认为需要更新(算法太简单,不过应该能适用于大多数情况)
下面是下载函数了,它首先要判读是否要先分析出下载地址:
def download(self):
if len( self.item) > 4:
##have to parse the page first
self.socket = urllib.urlopen(self.item.pageUrl)
try:
content = self.socket.read()
pattern = self.item.regstr
pattern = pattern.replace(':','%3A')
pattern = pattern.replace('/','%2F')
match = re.search(pattern,content)
self.item.url = match.group()[:]
self.item.url = self.item.url.replace('%3A',':')
self.item.url = self.item.url.replace('%2F','/')
print 'Get a file url==>', self.item.url
finally:
self.socket.close()
这里用到UpdateItem的__len__方法,只要配置文件里面多于4项,就认为需要分析文件的下载地址,首先把下载页面读入content中,然后来根据正则式找出需要的地址,因为在content中,':'被替换为'%3A','/'被替换为'%2F', 所以我们首先要修正一下正则式字符串.查找完毕后,再把它改回来(好像这个方法有些笨,留下以后改).
接下来,得到了文件的下载地址,需要判断是否需要下载了:
if not self.isNewSize(self.item.fileSize):
print self.item.fileName, '==>Up to Date'
return
最后的工作,就是下载了,这里我是自己写代码下载的,以后可以改成用wget下载,更快一些:
f = open(''.join((self.item.filePath,'/',self.item.fileName)), 'wb')
try:
while True:
data = self.socket.read(8192)
if not data:
break
f.write(data)
finally:
self.socket.close()
f.close()
这样,简单的updater就完成了,下面是完整的代码:
import urllib,os,re
class UpdateItem:
'''
test.zip,20,c:\temp,http://www.xxx.com,
'''
def __init__(self,sequence):
self.fileName = sequence[0]
self.fileSize = int(sequence[1])
self.filePath = sequence[2]
self.url = sequence[3]
self.len = 4
if len(sequence) > 4:
self.pageUrl = sequence[4]
self.regstr = sequence[5]
self.len = 6
def __str__(self):
str1 = ','.join((self.fileName,str(self.fileSize),self.filePath,self.url))
if self.len != 4:
str1 = ','.join((str1,self.pageUrl,self.regstr))
return str1
def __len__(self):
return self.len
class UrlParse:
def __init__(self, item):
self.item = item
def isNewSize(self, oldsize):
self.socket = urllib.urlopen(self.item.url)
fileSize = int(self.socket.headers['Content-Length'])
if oldsize == fileSize:
return False
self.item.fileSize = fileSize
return True
def download(self):
if len(self.item) > 4:
##have to parse the page first
self.socket = urllib.urlopen(self.item.pageUrl )
try:
content = self.socket.read()
pattern = self.item.regstr
pattern = pattern.replace(':','%3A')
pattern = pattern.replace ('/','%2F')
match = re.search(pattern,content)
self.item.url = match.group()[:]
self.item.url = self.item.url.replace('%3A',':')
self.item.url = self.item.url.replace('%2F','/')
print 'Get a file url==>',self.item.url
finally:
self.socket.close()
if not self.isNewSize(self.item.fileSize):
print self.item.fileName, '==>Up to Date'
return
f = open(''.join((self.item.filePath,'/',self.item.fileName )), 'wb')
try:
while True:
data = self.socket.read(8192)
if not data:
break
f.write(data)
finally:
self.socket.close()
f.close()
if __name__ == '__main__':
'''
'''
lists = []
if os.path.exists('updater.dat'):
f = open(' updater.dat')
try:
for line in f:
args = line.split(',')
lists.append(UpdateItem(args))
finally:
f.close()
for item in lists:
parse = UrlParse(item)
parse.download()
f = open('updater.dat','w')
try:
for item in lists:
f.write(str(item))
finally:
f.close()
else:
print 'Please make a config file.'
应该改进的地方:
1.多线程,可以同时对多个文件进行判断,也可以提示更友好的信息,比如下载进度
2.下载方式,使用第三方的下载工具,这样更快
3.改进代码,让代码更易读.
1.能根据配置文件,在线更新需要的工具
2.很多绿色软件没有提供在线更新功能(如SciTe,还有卡巴的病毒库),而只是给出了更新的地址
3.更新地址有的是固定的(如卡巴的病毒库,360等),也有是不固定的(连接地址总是体现为文件名的变化)
首先实现最简单的功能:
1.读写配置文件
2.下载有固定地址的文件 ,判断是否更新的依据是文件的大小,也就是html头中的Content-Length字段
3.对于变化地址的文件,分析其下载页面,找出需要的地址,如对于PyScripter,下载页面为http://pyscripter.googlepages.com/,而更新的地址可以写成: http://pyscripter.googlecode.com/files/PyScripterv.+zip,使用了简单的正则式.
对于配置文件,先定义一下它的格式:
本地文件名,文件大小,保存目录,下载地址,下载页地址,正则式字符串
......
使用逗号分割字段,以后好处理,对于有固定地址的文件,可以没有后两项.
为了方便处理,定义一个简单的数据结构:
class UpdateItem:
'''
test.zip,20,c:\temp,http://www.xxx.com,
'''
def __init__(self,sequence):
self.fileName = sequence[0]
self.fileSize = int(sequence[1])
self.filePath = sequence[2]
self.url = sequence[3]
self.len = 4
if len(sequence) > 4:
self.pageUrl = sequence[4]
self.regstr = sequence[5]
self.len = 6
def __str__(self):
str1 = ','.join((self.fileName,str(self.fileSize),self.filePath,self.url))
if self.len != 4:
str1 = ','.join((str1, self.pageUrl,self.regstr))
return str1
def __len__(self):
return self.len
其中的变量保存配置文件中对应的字段,构造函数中传递的sequnce列表是为了方便代码的书写.
__len__方法是为了日后判断是否有后两个字段用的.
__str__方法是为了以后写配置文件时方便,直接用str(item)就可以了.
还是用java的思路来写Python代码,所以看起来比较怪,哈哈.
关于读配置文件,我决定首先要求用写一个简单的配置文件,否则程序进行不下去啊:
if __name__ == '__main__':
'''
'''
lists = []
if os.path.exists('updater.dat '):
f = open('updater.dat')
try:
for line in f:
args = line.split(',')
lists.append(UpdateItem(args))
finally:
f.close()
else:
print 'Make a config file first!'
这里为了方便,我们定义配置文件叫updater.dat了,用一个列表保存所有需要下载的项.
关于写配置文件,当下载完成后,需要更新数据,可以这样写:
f = open('updater.dat','w')
try:
for item in lists:
f.write(str(item))
finally:
f.close()
这就用到了UpdateItem的__str__方法了.
下面就是程序的关键了部分:下载,首先是主程序的接口:
for item in lists:
parse = UrlParse(item)
parse.download()
我们定义一个类,UrlParse,接收下载配置项,然后提供分析和下载的功能:
class UrlParse:
def __init__(self, item):
self.item = item
里面保留一个item对象,方便以后处理.
在下载以前,首先要判断是否需要更新,仅仅判断html头部就可以了:
def isNewSize(self, oldsize):
self.socket = urllib.urlopen(self.item.url)
fileSize = int(self.socket.headers['Content-Length'])
if oldsize == fileSize:
return False
self.item.fileSize = fileSize
return True
如果文件大小发生了变化,就认为需要更新(算法太简单,不过应该能适用于大多数情况)
下面是下载函数了,它首先要判读是否要先分析出下载地址:
def download(self):
if len( self.item) > 4:
##have to parse the page first
self.socket = urllib.urlopen(self.item.pageUrl)
try:
content = self.socket.read()
pattern = self.item.regstr
pattern = pattern.replace(':','%3A')
pattern = pattern.replace('/','%2F')
match = re.search(pattern,content)
self.item.url = match.group()[:]
self.item.url = self.item.url.replace('%3A',':')
self.item.url = self.item.url.replace('%2F','/')
print 'Get a file url==>', self.item.url
finally:
self.socket.close()
这里用到UpdateItem的__len__方法,只要配置文件里面多于4项,就认为需要分析文件的下载地址,首先把下载页面读入content中,然后来根据正则式找出需要的地址,因为在content中,':'被替换为'%3A','/'被替换为'%2F', 所以我们首先要修正一下正则式字符串.查找完毕后,再把它改回来(好像这个方法有些笨,留下以后改).
接下来,得到了文件的下载地址,需要判断是否需要下载了:
if not self.isNewSize(self.item.fileSize):
print self.item.fileName, '==>Up to Date'
return
最后的工作,就是下载了,这里我是自己写代码下载的,以后可以改成用wget下载,更快一些:
f = open(''.join((self.item.filePath,'/',self.item.fileName)), 'wb')
try:
while True:
data = self.socket.read(8192)
if not data:
break
f.write(data)
finally:
self.socket.close()
f.close()
这样,简单的updater就完成了,下面是完整的代码:
import urllib,os,re
class UpdateItem:
'''
test.zip,20,c:\temp,http://www.xxx.com,
'''
def __init__(self,sequence):
self.fileName = sequence[0]
self.fileSize = int(sequence[1])
self.filePath = sequence[2]
self.url = sequence[3]
self.len = 4
if len(sequence) > 4:
self.pageUrl = sequence[4]
self.regstr = sequence[5]
self.len = 6
def __str__(self):
str1 = ','.join((self.fileName,str(self.fileSize),self.filePath,self.url))
if self.len != 4:
str1 = ','.join((str1,self.pageUrl,self.regstr))
return str1
def __len__(self):
return self.len
class UrlParse:
def __init__(self, item):
self.item = item
def isNewSize(self, oldsize):
self.socket = urllib.urlopen(self.item.url)
fileSize = int(self.socket.headers['Content-Length'])
if oldsize == fileSize:
return False
self.item.fileSize = fileSize
return True
def download(self):
if len(self.item) > 4:
##have to parse the page first
self.socket = urllib.urlopen(self.item.pageUrl )
try:
content = self.socket.read()
pattern = self.item.regstr
pattern = pattern.replace(':','%3A')
pattern = pattern.replace ('/','%2F')
match = re.search(pattern,content)
self.item.url = match.group()[:]
self.item.url = self.item.url.replace('%3A',':')
self.item.url = self.item.url.replace('%2F','/')
print 'Get a file url==>',self.item.url
finally:
self.socket.close()
if not self.isNewSize(self.item.fileSize):
print self.item.fileName, '==>Up to Date'
return
f = open(''.join((self.item.filePath,'/',self.item.fileName )), 'wb')
try:
while True:
data = self.socket.read(8192)
if not data:
break
f.write(data)
finally:
self.socket.close()
f.close()
if __name__ == '__main__':
'''
'''
lists = []
if os.path.exists('updater.dat'):
f = open(' updater.dat')
try:
for line in f:
args = line.split(',')
lists.append(UpdateItem(args))
finally:
f.close()
for item in lists:
parse = UrlParse(item)
parse.download()
f = open('updater.dat','w')
try:
for item in lists:
f.write(str(item))
finally:
f.close()
else:
print 'Please make a config file.'
应该改进的地方:
1.多线程,可以同时对多个文件进行判断,也可以提示更友好的信息,比如下载进度
2.下载方式,使用第三方的下载工具,这样更快
3.改进代码,让代码更易读.