V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
scb20100708
V2EX  ›  Python

在 scrapy 中,怎么让 yield Request(url,callback)中的 callback 函数执行完成后才执行 yield Request(url,callback)其后的语句?

  •  
  •   scb20100708 · 2017-11-17 14:33:17 +08:00 · 6612 次点击
    这是一个创建于 2597 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我需要先用回调函数 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)
    
    14 条回复    2017-11-18 16:59:18 +08:00
    scb20100708
        1
    scb20100708  
    OP
       2017-11-17 14:49:10 +08:00
    发现补充中的代码错了,def change_list(self):应是 def change_list(self,response):
    改后也输出不了想输出的"[0,1,2,3,4]"
    leavic
        2
    leavic  
       2017-11-17 14:50:25 +08:00
    把 print 写进 callback 函数的最后。。。。。
    scb20100708
        3
    scb20100708  
    OP
       2017-11-17 15:00:18 +08:00
    @leavic
    还有别的方法吗?实际代码还有函数“ change_list2 ”也要改变类变量,想在所有改变执行完后再处理类变量。
    860670496
        4
    860670496  
       2017-11-17 15:21:30 +08:00
    请参考:
    https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000/0014317799226173f45ce40636141b6abc8424e12b5fb27000

    如果一个函数定义中包含 yield 关键字,那么这个函数就不再是一个普通函数,而是一个 generator:

    这里,最难理解的就是 generator 和函数的执行流程不一样。函数是顺序执行,遇到 return 语句或者最后一行函数语句就返回。而变成 generator 的函数,在每次调用 next()的时候执行,遇到 yield 语句返回,再次执行时从上次返回的 yield 语句处继续执行。

    所以直接用啃 scrapy 的方法来学 python 不是个好选择,这个坑我也掉过,就是看教程的时候完全是表面上的理解生成器的概念,一用起来就傻了,必须得动手写几个才能彻底明白
    knightdf
        5
    knightdf  
       2017-11-17 15:36:36 +08:00
    把 yield Request(url,callback)其后的语句放在 callback 的后面执行
    leavic
        6
    leavic  
       2017-11-17 15:50:23 +08:00   ❤️ 1
    @scb20100708 没有什么好方法了,你的需求就应该是把函数放进 callback 里面。
    iyaozhen
        7
    iyaozhen  
       2017-11-17 16:05:06 +08:00 via Android
    只能 callback 吧。回调地狱就是那么来的
    scb20100708
        8
    scb20100708  
    OP
       2017-11-17 19:11:06 +08:00
    @860670496
    你给的参考资料看过了,我想不是 yield 的问题,因为 callback 函数会执行,只不过在 callback 函数执行前就执行 yield Request(url,callback)后的语句了。
    @leavic @knightdf @iyaozhen
    好的,谢谢各位
    hcnhcn012
        9
    hcnhcn012  
       2017-11-17 19:29:44 +08:00 via iPhone
    机制就是这样,为了效率,要是你 callback 里面花了一个很长时间的的 IO 操作,整个框架都在等你的 callback 然后 yield 带出从而发生阻塞那效率会很低,具体怎么实现的话就要结合 twisted 看源码了
    RadishWind
        10
    RadishWind  
       2017-11-17 19:32:43 +08:00
    还没有实验 加个修饰器行不行?在修饰器中等待?
    zhijiansha
        11
    zhijiansha  
       2017-11-17 19:46:01 +08:00
    scrapy 是异步的,reuqest 请求也是放进队列的
    larsenlouis
        12
    larsenlouis  
       2017-11-17 20:24:37 +08:00   ❤️ 1
    “回调函数 callback 改变类变量,然后使用类变量”
    是发出单个请求->callback 链->print,还是发出所有的请求->完成每个请求的 callback 链->print ?
    后者可以用 def spider_closed(self, spider) 参考 https://doc.scrapy.org/en/latest/topics/signals.html
    scb20100708
        13
    scb20100708  
    OP
       2017-11-17 20:54:35 +08:00
    @hcnhcn012
    谢谢,twisted 还没用过,这个问题先搁这儿吧
    @RadishWind
    感觉和把 print 语句放在 callback 中一样,谢谢哈
    @zhijiansha
    谢谢
    scb20100708
        14
    scb20100708  
    OP
       2017-11-18 16:59:18 +08:00
    @larsenlouis
    谢谢,我想要实现的正是你说的“发出所有的请求->完成每个请求的 callback 链->print ? “。
    方法可行,谢谢啦。
    还找到了一个办法,直接在类中加个函数:
    def closed(self, reason):
    print(list_for_test)
    参考:
    https://stackoverflow.com/a/33312325/7011350
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2602 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 04:15 · PVG 12:15 · LAX 20:15 · JFK 23:15
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.