备注:今年1月份写的文章,以后准备长期驻扎在这儿,就贴过来了。
1.12号晚上总算彻底的考完了所有的科目,昨天可以睡一个安稳的懒觉了。从床上爬起来之后,随便从书架上拿了一本书,竟然是《备战大学德语四级考试·词汇篇》,不觉想起当初“战绩辉煌”的德语课。翻开书,看了几个单词后,发现都忘记了该怎么发音,所以想把每个单词的发音放到P3里,等睡不着的时候可以听一听~。
所以,具体需求就是:根据一个文本文件,该文件中提供了一个单词列表,格式为每个单词占一行。需要根据这个列表,从某个网站上把对应单词的发音的mp3文件保存在本地磁盘上,而且mp3文件保存为相应的单词的名称。
大致就是这些,想想还缺点什么,恩,多线程---典型的多线程应用环境啊。确定一下实现环境,看来Python是首选了。因为快,当然是说开发速度快了~
再试几个单词后,基本就可以确定每个单词对应的查询页面的URL地址格式为:
<a href="http://media.dwds.de/dwds/media/sound/dwdswb_aussprache_dev/+%E5%8D%95%E8%AF%8D%E5%AF%B9%E5%BA%94%E7%9A%84%E5%93%88%E5%B8%8C%E5%80%BC.mp3">http://media.dwds.de/dwds/media/sound/dwdswb_aussprache_dev/+单词对应的哈希值.mp3</a>
不知道这里为什么要用哈希值,可以肯定的是不是用来提高检索速度的,因为单词本身就可以作为唯一的键,而且单词的最大长度应该也不会超过一个固定的上限值(比如:40?)。也许使用哈希值是为了防止用程序自动下载发音文件,减少对服务器的冲击吧,我猜。刚看到这个32位的串,我想大家第一反应应该都是猜它是不是单词对应的md5值(比如QQ登录的时候,就对针对密码进行三次md5加密),很不幸的,这个串不是(这个,可以使用Python在交互式模式下做一个简单的验证)。不过这个并不影响下载这个mp3文件,恩,就是先打开页面,然后从页面上找到mp3的URL,然后再下载。
好了,整理一下思路,简单的说,下载一个单词对应的mp3的流程如下:
Step1:从文件中读取一个单词
Step2:构造一个单词查询页面的URL,将此URL对应的html源代码保存到content中
Step3:使用正则表达式在content中搜索对应mp3文件的URL
Step4:读取mp3数据,在本地新建一个文件,把数据保存进去
Step5:如果没有结束,跳转到Step1
恩,挺简单的流程。还需要增添的设施就是多线程,测试表明,平均每下载一个单词将近4秒钟,不能在一个线程在访问网络或者保存文件的时候让CPU空闲啊。所以,在运行程序的时候需要传入两个参数,一个就是需要开启的线程的数量,另外一个就是保存单词列表的文件名。不过,等我改天有时间了,实现一个线程池,这样就省事了,把任务扔到池子里就行了。否则在程序中还要考虑加锁解锁这种琐碎的事情,因为保存单词列表的队列是共享资源。这些分析清楚了,差不多就可以写代码了。把代码贴到这儿,仅供参考:
#!/usr/bin/python
#Author:lichao
#Date:01-13-2012
#Description:Download the .mp3 sound files that correspoding to the words in the given file.
import threading
import time
import fileinput
import re
import urllib2
import sys
class DownloadWorker(threading.Thread):
global mutext
def __init__(self,wordsList,workerIndex):
threading.Thread.__init__(self)
self.queue=wordsList
self.index=workerIndex
def run(self):
print('worker%d start to work' % (self.index))
mutex.acquire()
self.word=self.queue.front()
mutex.release()
while self.word!="0":
url = "http://www.dwds.de/?qu="+self.word
urlContent = urllib2.urlopen(url).read()
urlList = re.findall('http://media.dwds.de/dwds/media/sound/dwdswb_aussprache_dev/.*\.mp3', urlContent)
try:
soundData = urllib2.urlopen(urlList[0]).read()
saveName=self.word+".mp3"
output = open(saveName,'wb')
output.write(soundData)
output.close()
print('%s:OK --Post by worker%d' % (self.word,self.index) )
except:
print('%s:FAILED --Post by worker%d' % (self.word,self.index) )
finally:
mutex.acquire()
self.word=self.queue.front()
mutex.release()
print('worker%d eixt' % self.index)
class WordsList():
def __init__(self,filePath):
self.t=[]
for line in fileinput.input(filePath):
if(len(line)>1 and line[len(line)-1]=='\n'):
line=line[0:len(line)-1]
self.t.append(line)
else:
self.t.append('0')
def front(self):
if(self.t[0]!='0'):
return self.t.pop(0)
else:
return self.t[0]
def main():
global mutex
mutex=threading.Lock()
workerNumber=int(sys.argv[1])
filePath=sys.argv[2]
wordsList=WordsList(filePath)
workerPool=[]
for i in range(0,workerNumber):
worker=DownloadWorker(wordsList,i)
workerPool.append(worker)
workerPool[i].start()
if __name__ == "__main__":
main()
下面两张截图是运行效果图,其中图1是运行效果图。是的,有些单词的mp3下载过程中出错了,这是由于某些单词的发音太简单了,这些单词级别估计是1级,估计是网站的设计者觉得这种简单的单词没有必要制作一个mp3文件放在上面。一般来说,稍难一点的单词的发音都能下载到的。图2是下载后的截图,以后可以用来催眠了。
<a target="_blank" href="http://blog.51cto.com/attachment/201202/163741917.png"></a>
图1:下载器运行效果
<a target="_blank" href="http://blog.51cto.com/attachment/201202/163749193.png"></a>
本文转自hipercomer 51CTO博客,原文链接:http://blog.51cto.com/hipercomer/789423