以下面的爬虫程序为例,详细学习python多线程编程。
知识点:
守护进程:也叫Daemon进程, 运行在linux或unix一种后台进程。不依赖终端运行,运行过程不会被终端打断,信息不会在终端上被显示出来,周期性执行任务或者等待某些事件的发生,与系统共生死,在系统启动时守护进程运行于后台,当系统关闭时,该进程也结束。
守护线程:python中的守护线程类似于java中的守护线程,像守护进程一样,在后台执行某些任务,当全部的user用户任务执行完毕,守护线程也结束。比如说java虚拟机的垃圾回收线程。具体参考守护线程总结
import urllib2
from threading import Thread,RLock
from Queue import Queue #Python中的Queue对象也提供了对线程同步的支持。使用Queue对象可以实现多个生产者和多个消费者形成的FIFO的队列。
import time
'''f = Fetcher(threads=10) #设定下载线程数为10 for url in urls: f.push(url) #把所有url推入下载队列 while f.taskleft(): #若还有未完成下载的线程 content = f.pop() #从下载完成队列中取出结果 do_with(content) # 处理content内容'''
#在threading模块中,定义两种类型的琐:threading.Lock和threading.RLock。
class Fetcher:
def __init__(self,threads):# 创建完对象后调用,对当前对象的实例的一些初始化,无返回值
self.opener = urllib2.build_opener(urllib2.HTTPHandler)#build_opener默认添加几个处理器,但提供快捷的方法来添加或更新默认处理器。
self.lock =RLock() #线程锁,同一线程不能多次调用acquire,死锁
self.q_req = Queue() #任务队列,实现线程同步
self.q_ans = Queue() #完成队列
self.threads = threads
for i in range(threads):
t = Thread(target=self.threadget)#threadget函数作为参数
t.setDaemon(True)#主线程A启动了子线程B,调用b.setDaemaon(True),则主线程结束时,会把子线程B也杀死,必须在start()之前调用,默认false.
t.start()
self.running = 0
''' 当我们获得一个url正在使用一个opener(一个urllib2.OpenerDirector的实例),正常情况下默认是urlopen,但是我们可以创建opener,并通过handler处理该url。见[opener学习链接](http://blog.csdn.net/hshl1214/article/details/45853863) '''
def __del__(self): #解构时需等待两个队列完成
time.sleep(0.5)
self.q_req.join()
self.q_ans.join()
def taskleft(self):
return self.q_req.qsize()+self.q_ans.qsize()+self.running
def push(self,req):
self.q_req.put(req)#调用队列对象的put()方法在队尾插入一个项目。put()有两个参数,第一个item为必需的,为插入项目的值;第二个block为可选参数,默认为1。如果队列当前为空且block为1,put()方法就使调用线程暂停,直到空出一个数据单元。如果block为0,put方法将引发Full异常。
def pop(self):
return self.q_ans.get()
def threadget(self):
while True:
req = self.q_req.get()#调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True,get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。
self.lock.acquire()
#with self.lock: #要保证该操作的原子性,进入critical area
self.running += 1
self.lock.release()
try:
ans = self.opener.open(req).read()
except Exception, what:
ans = ''
print what
self.q_ans.put((req,ans))
#with self.lock:
self.lock.acquire()
self.running -= 1
self.lock.release()
self.q_req.task_done()#在完成一项工作之后,Queue.task_done() 函数向任务已经完成的队列发送一个信号
time.sleep(0.1) # don't spam
if __name__ == "__main__":
#访问限制的问题见http://www.pythonclub.org/python-network-application/observer-spider#%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%B9%B6%E5%8F%91%E6%8A%93%E5%8F%96
headers = {
'User-Agent':'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.1.6) Gecko/20091201 Firefox/3.5.6'
}
req = urllib2.Request(
url ="http://blog.csdn.net/zhaoyl03/article/details/8631645" ,
headers = headers
)
links = []
links.append("http://china.cnr.cn/yaowen/20151205/t20151205_520695743.shtml")
links.append("http://www.cankaoxiaoxi.com/roll/roll10/2015/1205/1017899.shtml")
links.append("http://baijia.baidu.com/?tn=topic&topicid=xd61e4WS")
links.append(req)
f = Fetcher(threads=6)
for url in links:
f.push(url)
while f.taskleft():
url,content = f.pop()
print url,len(content)