使用协程(gevent)实现请求
协程,又称微线程。英文名Coroutine。
协程最大的优势就是协程极高的执行效率。因为子程序切换不是线程切换,而是由程序自身控制,因此,没有线程切换的开销,和多线程比,线程数量越多,协程的性能优势就越明显。
第二大优势就是不需要多线程的锁机制,因为只有一个线程,也不存在同时写变量冲突,在协程中控制共享资源不加锁,只需要判断状态就好了,所以执行效率比多线程高很多。
因为协程是一个线程执行,那怎么利用多核CPU呢?最简单的方法是多进程+协程,既充分利用多核,又充分发挥协程的高效率,可获得极高的性能。后续会就这一块单独开写一篇协程+多进程的测试文章。
Python对协程的支持还非常有限,用在generator中的yield可以一定程度上实现协程。虽然支持不完全,但已经可以发挥相当大的威力了。
Python通过yield
提供了对协程的基本支持,但是不完全。而第三方的gevent为Python提供了比较完善的协程支持。
gevent是第三方库,通过greenlet实现协程,其基本思想是:
当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。
由于切换是在IO操作时自动完成,所以gevent需要修改Python自带的一些标准库,这一过程在启动时通过monkey patch完成:
所以在导入库的时候就要导入以下的库,这样子才可以实现交替运行机制,否则就都还是顺序运行机制
from gevent import monkey; monkey.patch_all()
以下是窒执行协程的一个代码,代码不多,只是几行代码而已
# urls = ['www.google.com', 'www.example.com', 'www.python.org'] # #使用的列表解析的方式形成list,而是不需要使用for和append的冗余代码区生成,简洁 # jobs = [gevent.spawn(socket.gethostbyname, url) for url in urls] # gevent.joinall(jobs, timeout=2) # print [job.value for job in jobs]
以下是使用协程抓取的一个电话号码的信息
# -*- coding:utf-8 -*- import requests from lxml import etree import gevent import MySQLdb import datetime class huoqu(object): def __init__(self): self.conn=MySQLdb.connect( host='localhost', port=3306, user='root', passwd='123456', db='cai', charset='utf8' ) self.cur=self.conn.cursor() self.sql='insert into t_number_pass values(%s,%s,%s,%s,%s,%s)' self.Add_time=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') def parse(self,pid): base_url='http://www.139018.com/ReportList_' url=base_url+str(pid+1) print url response=requests.get(url) #print response.text #/li[class="GlbBtmLn"]/div[[@class="Num"]/text() selector=etree.HTML(response.text) number=selector.xpath('//li/div[@class="Num"]/text()') type=selector.xpath('//li/div[@class="RptTp"]/text()') person=selector.xpath('//li/div[@class="Uper"]/text()') subtime=selector.xpath('//li/div[@class="UpTm"]/text()') text=selector.xpath('//li/div[@class="Txt"]/text()') for i in range(len(number)): self.cur.execute(self.sql,(number[i].encode('utf-8'),type[i].encode('utf-8'),person[i].encode('utf-8'),str(subtime[i].encode('utf-8')),text[i].encode('utf-8'),str(self.Add_time))) self.conn.commit() print number print type print person print subtime print text def asynchronous(self): threads=[] for i in range(6043): threads.append(gevent.spawn(self.parse,i)) gevent.joinall(threads) def close_sql(self): self.cur.close() self.conn.close() asy=huoqu() asy.asynchronous() asy.close_sql()