HI,
我有这个简短的蜘蛛代码:
class TestSpider(CrawlSpider):
name = "test"
allowed_domains = ["google.com", "yahoo.com"]
start_urls = [
"http://google.com"
]
def parse2(self, response, i):
print "page2, i: ", i
# traceback.print_stack()
def parse(self, response):
for i in range(5):
print "page1 i : ", i
link = "http://www.google.com/search?q=" + str(i)
yield Request(link, callback=lambda r:self.parse2(r, i))
我希望输出像这样:
page1 i : 0
page1 i : 1
page1 i : 2
page1 i : 3
page1 i : 4
page2 i : 0
page2 i : 1
page2 i : 2
page2 i : 3
page2 i : 4
然而,,实际输出是这样的:
page1 i : 0
page1 i : 1
page1 i : 2
page1 i : 3
page1 i : 4
page2 i : 4
page2 i : 4
page2 i : 4
page2 i : 4
page2 i : 4
所以,我传递给callback=lambda r:self.parse2(r, i)
的争论是错误的。
代码出了什么问题?
答案 0 :(得分:38)
根据Scrapy文档,使用lambda将阻止库作业功能工作(http://doc.scrapy.org/en/latest/topics/jobs.html)。
Request()和FormRequest()都包含一个名为meta的字典,可用于传递参数。
def some_callback(self, response):
somearg = 'test'
yield Request('http://www.example.com',
meta={'somearg': somearg},
callback=self.other_callback)
def other_callback(self, response):
somearg = response.meta['somearg']
print "the argument passed is:", somearg
答案 1 :(得分:11)
lambdas正在访问i
,它正在闭包中,因此它们都引用相同的值(调用lambda时,i
函数中的parse
的值)。更简单的重建现象是:
>>> def do(x):
... for i in range(x):
... yield lambda: i
...
>>> delayed = list(do(3))
>>> for d in delayed:
... print d()
...
2
2
2
您可以看到lambdas中的i
都绑定到函数i
中do
的值。它们将返回它当前具有的任何值,并且只要任何lambda都存活,python将保持该范围保持活动以保留它的值。这就是所谓的闭包。
一个简单但丑陋的工作是
>>> def do(x):
... for i in range(x):
... yield lambda i=i: i
...
>>> delayed = list(do(3))
>>> for d in delayed:
... print d()
...
0
1
2
这是有效的,因为在循环中,i
的当前值绑定到lambda的参数i
。或者(也许更清楚一点)lambda r, x=i: (r, x)
。重要的是通过在lambda 的主体之外进行赋值(稍后才执行),您将变量绑定到{{1>}的当前值而不是它在循环结束时所需的值。这使得lambda不会在i
上关闭,并且每个lambda都有自己的值。
所以你需要做的就是改变行
i
到
yield Request(link, callback=lambda r:self.parse2(r, i))
你真是太棒了。
答案 2 :(得分:2)
lambda r:self.parse2(r, i)
绑定变量名i
,而不是i
的值。稍后,当评估lambda时,使用闭包中i
的当前值,即i
的 last 值。这很容易证明。
>>> def make_funcs():
funcs = []
for x in range(5):
funcs.append(lambda: x)
return funcs
>>> f = make_funcs()
>>> f[0]()
4
>>> f[1]()
4
>>>
此处make_funcs
是一个函数,它返回一个函数列表,每个函数都绑定到x
。您希望调用时的函数分别打印值0到4。然而,他们都返回4
。
>>> def make_f(value):
def _func():
return value
return _func
>>> def make_funcs():
funcs = []
for x in range(5):
funcs.append(make_f(x))
return funcs
>>> f = make_funcs()
>>> f[0]()
0
>>> f[1]()
1
>>> f[4]()
4
>>>
我在这里使用显式的命名函数而不是lambda
。在这种情况下,变量的值被绑定而不是名称。因此,各个函数的行为与预期的一样。
我看到@Aaron已经通过an answer更改了您的lambda
。坚持下去,你会很高兴:)
答案 3 :(得分:2)
class TestSpider(CrawlSpider):
name = "test"
allowed_domains = ["google.com", "yahoo.com"]
start_urls = [
"http://google.com"
]
def parse(self, response):
for i in range(5):
print "page1 i : %s" % i
yield Request("http://www.google.com/search?q=%s" % i, callback=self.next, meta={'i': i})
def next(self, response):
print "page1 i : %s" % response.meta['i']
# traceback.print_stack()