在 Scrapy 中,如何让 yield Request(url, callback) 中的 callback 函数执行完成后才执行其后的 Python 代码?
我需要先用回调函数 callback 改变类变量,然后使用类变量,但发现程序的实际执行是不等待 yield Request(url,callback)中的 callbak 函数执行完成,便执行其后的语句了。怎么办呢?
写了个程序来表达我的意图:
# -*- coding: utf-8 -*-
from scrapy import Spider, Request
class ZhihuSpider(Spider):
name = “debug_zhihu”
allowed_domains = [“www.zhihu.com”]
list_for_test = []
def start_requests(self):
yield Request('https://www.zhihu.com/people/excited-vczh/following', self.change_list)
print('after sent Request statement,list_for_test:', self.list_for_test)
# 想打印出“[0,1,2,3,4]”,结果打印出"[]"
def change_list(self):
for each_item in range(0, 5):
self.list_for_test.append(each_item)
在 Scrapy 中,如何让 yield Request(url, callback) 中的 callback 函数执行完成后才执行其后的 Python 代码?
发现补充中的代码错了,def change_list(self):应是 def change_list(self,response):
改后也输出不了想输出的"[0,1,2,3,4]"
在Scrapy里,yield Request(url, callback) 后面的代码会在发出请求后立即执行,这是异步框架的正常行为。如果你想让callback处理完再执行后续逻辑,得把后续代码移到callback函数里。
比如,你想先爬详情页,再处理数据,可以这样:
import scrapy
class MySpider(scrapy.Spider):
name = 'example'
start_urls = ['http://example.com/list']
def parse(self, response):
# 提取列表页链接
detail_links = response.css('a.detail::attr(href)').getall()
for link in detail_links:
# 把后续处理逻辑都放到callback里
yield scrapy.Request(
url=response.urljoin(link),
callback=self.parse_detail_and_continue
)
def parse_detail_and_continue(self, response):
# 这里是原来的callback逻辑
item = {
'title': response.css('h1::text').get(),
'url': response.url
}
# 这里放原来在yield Request后面想执行的代码
self.logger.info(f'处理完 {response.url}')
# 可以继续yield更多请求或处理
# yield scrapy.Request(...)
yield item
如果callback里还有异步请求,同样道理,继续把代码往更深层的callback里放。Scrapy就是这么个“回调套回调”的玩法。
总结:把后续代码挪到callback函数里。
把 print 写进 callback 函数的最后。。。。。
还有别的方法吗?实际代码还有函数“ change_list2 ”也要改变类变量,想在所有改变执行完后再处理类变量。
请参考:
https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014317799226173f45ce40636141b6abc8424e12b5fb27000
如果一个函数定义中包含 yield 关键字,那么这个函数就不再是一个普通函数,而是一个 generator:
这里,最难理解的就是 generator 和函数的执行流程不一样。函数是顺序执行,遇到 return 语句或者最后一行函数语句就返回。而变成 generator 的函数,在每次调用 next()的时候执行,遇到 yield 语句返回,再次执行时从上次返回的 yield 语句处继续执行。
所以直接用啃 scrapy 的方法来学 python 不是个好选择,这个坑我也掉过,就是看教程的时候完全是表面上的理解生成器的概念,一用起来就傻了,必须得动手写几个才能彻底明白
把 yield Request(url,callback)其后的语句放在 callback 的后面执行
没有什么好方法了,你的需求就应该是把函数放进 callback 里面。
只能 callback 吧。回调地狱就是那么来的
你给的参考资料看过了,我想不是 yield 的问题,因为 callback 函数会执行,只不过在 callback 函数执行前就执行 yield Request(url,callback)后的语句了。
好的,谢谢各位
机制就是这样,为了效率,要是你 callback 里面花了一个很长时间的的 IO 操作,整个框架都在等你的 callback 然后 yield 带出从而发生阻塞那效率会很低,具体怎么实现的话就要结合 twisted 看源码了
还没有实验 加个修饰器行不行?在修饰器中等待?
scrapy 是异步的,reuqest 请求也是放进队列的
“回调函数 callback 改变类变量,然后使用类变量”
是发出单个请求->callback 链->print,还是发出所有的请求->完成每个请求的 callback 链->print ?
后者可以用 def spider_closed(self, spider) 参考 https://doc.scrapy.org/en/latest/topics/signals.html
谢谢,我想要实现的正是你说的“发出所有的请求->完成每个请求的 callback 链->print ? “。
方法可行,谢谢啦。
还找到了一个办法,直接在类中加个函数:
def closed(self, reason):
print(list_for_test)
参考:
https://stackoverflow.com/a/33312325/7011350


