from twisted.internet import reactor #事件循环(终止条件,所有的socket都已经移除)
from twisted.web.client import getPage #socket对象(如果下载完成,自动从时间循环中移除...)
from twisted.internet import defer #defer.Deferred 特殊的socket对象 (不会发请求,手动移除)
#自定义一个Request 类
classRequest(object):def __init__(self, url, callback):"""初始化接受url和callback回调函数
:param url: 请求的url
:param callback: 获取内容后的callback"""self.url=url
self.callback=callback#响应对象
classHttpResponse(object):def __init__(self, content, request):"""初始化相应内容
:param content: 下载 下来的响应的content
:param request: response对应的request"""
#响应的内容
self.content =content#响应的请求
self.request =request#response对应的request
self.url =request.url#将内容转换为文本
self.text = str(content, encoding='utf-8')classChoutiSpider(object):"""初始化顶一个小蜘蛛"""name= 'chouti'
#蜘蛛一开始的执行方法
defstart_requests(self):
start_url= ['http://www.baidu.com', 'http://www.bing.com', ]for url instart_url:yieldRequest(url, self.parse)#收到response后的解析函数
defparse(self, response):print(response) #response是下载的页面
yield Request('http://www.cnblogs.com', callback=self.parse)importqueue#这里是调度器
Q =queue.Queue()#定义了一个引擎类
classEngine(object):def __init__(self):#引擎关闭
self._close =None#最大的请求数
self.max = 5
#正在爬的请求
self.crawlling =[]#拿着相应的回调函数
defget_response_callback(self, content, request):""":param content: 响应的content
:param request: 响应对应的request
:return:"""
#一旦执行回调函数,就可以从调度中拿走这个请求
self.crawlling.remove(request)#将内容封装成 一个 HttpResponse对象
rep =HttpResponse(content, request)#调用请求时的回调函数,将封装的HttpResponse传递进去
result =request.callback(rep)importtypes#查看回调函数是否继续返回迭代器对象
ifisinstance(result, types.GeneratorType):#将回调函数 新的请求放到调度器
for req inresult:
Q.put(req)#从调度器取请求,执行,下载,并控制最大并发数
def_next_request(self):"""去取request对象,并发送请求
最大并发数限制
:return:"""
print(self.crawlling, Q.qsize())#如果调度器的长度为0,而且处于正在爬取的数目也为 0 ,那么就说明该关闭了
if Q.qsize() == 0 and len(self.crawlling) ==0:#直接调用 defer.Deferred().callback(None)就会关闭defer
self._close.callback(None)return
#如果正在爬取的数目超过了最大的并发限制,直接返回
if len(self.crawlling) >=self.max:return
#如果没有达到并发限制,就执行以下内容
while len(self.crawlling)
req = Q.get(block=False)#把拿到的请求放到 正在爬取的列表中
self.crawlling.append(req)#获取相应的页面
d = getPage(req.url.encode('utf-8'))#页面下载完成,get_response_callback,调用用户spider中定义的parse方法,并且将新请求添加到调度器
d.addCallback(self.get_response_callback, req)#未达到最大并发数,可以再去调度器中获取Request
#继续给d添加回调函数,这个回调函数可以是匿名函数
d.addCallback(lambda_: reactor.callLater(0, self._next_request))exceptException as e:print(e)return@defer.inlineCallbacksdefcrawl(self, spider):#将start_requests包含的生成器,初始Request对象添加到调度器
start_requests =iter(spider.start_requests())whileTrue:try:#拿到每个request,放到调度器中
request =next(start_requests)
Q.put( request)exceptStopIteration as e:break
#去调度器中取request,并发送请求
#self._next_request()
reactor.callLater(0, self._next_request)#初始化self._close
self._close =defer.Deferred()yieldself._close#初始化一个抽屉爬虫
spider =ChoutiSpider()
_active=set()#实例化一个引擎对象
engine =Engine()#引擎对象 调用 crawl方法,运行指定的spider
d =engine.crawl(spider)#将crawl方法放到set中
_active.add(d)#实例化一个DeferredList,将_active 内容放进去,返回一个defer.Deferred()对象,若defer.Deferred()被关闭,dd就为空
dd =defer.DeferredList(_active)#一旦dd里面为空,就调用reactor.stop()方法
dd.addBoth(lambdaa: reactor.stop())#让它run起来
reactor.run()