1.1. twisted
1.1.1. twised简介
scrapy是基于twised的,我们需要对scrapy有一定了解才能更好学习scrapy。
twisted和asyncio类比,都是为了支持携程而诞生的,但是twisted是老牌的,asyncio是python3.4之后才出现的。
1.1.2. schedule
定义多少秒后执行一个任务
from twisted.internet import reactor
def my_print(s):
print(f"my_print: {s}")
# 这里是定义, 没有实际运行
reactor.callLater(5, my_print, "hello,wolrd")
# 5秒后执行my_print
reactor.run()
reactor.callLater会将某个任务加入到事件循环中,并设置好多少秒后开始执行,当然了,这个需要reactor.run()来启动。
定义一个周期任务
from twisted.internet import reactor, task
def my_print(s):
print(f"my_print: {s}")
# 这里是定义, 没有实际运行,周期性调用任务
loop = task.LoopingCall(my_print, "hello")
# 每2秒执行一次
loop.start(2)
# 开始执行
reactor.run()
1.1.3. Deferred
deferred表示任务未来会产生结果,当任务产生结果时,deferred会调用注册过来的回调函数。并将结果传递给它。
from twisted.internet import defer, reactor
def my_print(s):
msg = f"my_print: {s}"
print(msg)
return msg
def get_data(input_data):
deferred = defer.Deferred()
# 注册一下,一秒后执行callback 将input_data作为参数
reactor.callLater(1, deferred.callback, input_data*3)
return deferred
deferred = get_data(10)
deferred.addCallback(my_print)
reactor.run()
get_data方法返回了一个 defer.Deferred
对象。 表示这个函数是一个异步任务,会在未来的某个时间产生结果,这里通过 reactor.callLater(1, deferred.callback, input_data*3)
来表示将来的1秒执行这个callback方法, 将 input_data*3
的结果作为参数传递给callback方法。到 my_print
接收的参数就是30了, 执行了简单的打印工作。
上面是成功回调的案例,异常回调同理。
1.1.4. 回调链
如果需要注册多个回调函数,那就需要使用回调链, 回调链是按照顺序执行的。
成功回调链: 上一个的输出作为下一个的输入。 比如cb1的输出作为cb2的输入。
异常回调链: 如果上一个回调链执行失败,则执行下一个的异常回调。 比如cb1执行失败,则执行eb2的回调,将异常传递给eb2。
交互: 如果eb1 处理了异常,那么cb2会被调用,并将eb1的返回值作为参数。
总结: 当前级别的回调,只根据上一个级别的回调执行情况进行触发。
1.1.5. DeferredList
当需要等待多个deferred的执行完毕的时候,我们可以使用deferedlist。
from twisted.internet import defer
def print_result(result):
for ok,value in result:
if ok:
print(f'success: {value}')
else:
print(f'fail: {value.getErrorMessage()}')
d1 = defer.Deferred()
d2 = defer.Deferred()
d3 = defer.Deferred()
dl = defer.DeferredList([d1,d2,d3],consumeErrors=True)
dl.addCallback(print_result)
d1.callback(1)
d2.callback(2)
d3.errback(Exception('this has a error'))
# success: 1
# success: 2
# fail: this has a error
通过deferredlist,就可以将多个deferred的执行结果,合并到一起。
1.1.6. defer.inlineCallbacks
这个是在scrapy中看到比较多的。 这个是一个装饰器, 我们对比看看使用装饰器和不使用装饰器的区别。
from twisted.internet import defer, reactor
def my_print(s):
msg = f"my_print: {s}"
print(msg)
return msg
def get_data(input_data):
deferred = defer.Deferred()
# 注册一下,一秒后执行callback 将input_data作为参数
reactor.callLater(1, deferred.callback, input_data*3)
return deferred
def method_1():
deferred = get_data(10)
deferred.addCallback(my_print)
reactor.run()
def method_2():
method_3()
reactor.run()
@defer.inlineCallbacks
def method_3():
result = yield get_data(10).addCallback(my_print)
print(result)
#method_1()
method_2()
# my_print: 30
# my_print: 30
通过@defer.inlineCallbacks, 我们将回调函数注册后,可以直接yield等待回调结果。