scrapy使用yield返回Request的步骤是怎么样的
Python的yield是一个比较特别的关键字。
>>> def test_yield():
... for i in range(3):
... yield i
...
>>> test_yield()
<generator object test_yield at 0x01AB2C88>
很明显的看到,yield不同于return。return直接返回函数返回值。而包含yield的函数,不返回而是生成了一个对象。这个对象叫做生成器(generator)。实际上test_yield中的for循环并没有在调用test_yield函数时执行完毕,而是每次遇到yield都会停止在执行yield前,当你调用生成器的next方法时,yield就会执行,这时返回紧接着yield的变量。
>>> gen = test_yield()
>>> gen.next()
0
>>> gen.next()
1
>>> gen.next()
2
>>> gen.next()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
可以看到,返回2时,生成器中的for循环已经执行完毕,函数中没有可执行的yield。这时再next就会抛出StopIteration的异常,以此表示生成器的结束。
实际上,生成器也可用循环进行迭代。
>>> for each in test_yield():
... print each
...
0
1
2
而且Python中xrange不同于range产生list,xrange产生的是一个生成器。
>>> range(3)
[0, 1, 2]
>>> xrange(3)
xrange(3)
在Scrapy中,Spider解析网页的方法中就用到yield。当然,不用yield也是可以的,但你需要返回一个包含从传入的Response中解析出的所有Request或是Item的list。
class DemoSpider(Spider):
name = 'demo'
...
def parse(self, response):
sel = Selector(response)
for url in sel.xpath('//a/@href').extract():
yield Request(url)
或者
class DemoSpider(Spider):
name = 'demo'
...
def parse(self, response):
sel = Selector(response)
requests = []
for url in sel.xpath('//a/@href').extract():
requests.append(Request(url))
return requests
总之,要返回一个可迭代的对象。
那么,为何要存在yield这种东西?直接返回list不成吗?试想一下,如果需要返回包含成百上千个元素的list,想必会占用很多计算机资源以及时间。如果用yield就可以缓和这种情况了。
以上部分代码仅适用Python2。
def parse(self, response):
result_list = []
for h3 in response.xpath("//h3").extract():
result_list.append(MyItem(title=h3)
for url in response.xpath("//a/@href").extract():
result_list.append(scrapy.Request(url, callback=self.parse))
return result_list
区别在于用了yield的函数会返回一个生成器,生成器不会一次把所有值全部返回给你,而是你每调用一次next返回一个值。
如果你想了解生成器和迭代器,可以去看相关文档。
它们的用法很简单:
for item in list:
process(item)
for item in iterator:
process(item)
for item in generator:
process(item)
Python会帮你处理内部细节,你只管用就行了。