使用协程(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='',
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()
上一篇:Android数据库相关整理


下一篇:Sqlite多线程相关整理