如何在start_requests方法完成之前使Scrapy执行回调?

时间:2016-09-29 22:03:54

标签: scrapy twisted scrapy-spider

我有一个大型的相对网址文件,我想用Scrapy抓取,我已经编写了一些代码来逐行读取这个文件并构建我的蜘蛛解析请求。下面是一些示例代码。

蜘蛛:

def start_requests(self):
    with open(self._file) as infile:
        for line in infile:
            inlist = line.replace("\n","").split(",")
            item = MyItem(data = inlist[0])

            request = scrapy.Request(
                url = "http://foo.org/{0}".format(item["data"]),
                callback = self.parse_some_page
            )
            request.meta["item"]
            yield request


def parse_some_page(self,response):
    ...
    request = scrapy.Request(
        url = "http://foo.org/bar",
        callback = self.parse_some_page2
    )
    yield request

这很好用,但是输入文件很大,我发现parse_some_page2start_requests完成所有初始请求之前都没有调用table-responsive。有什么方法可以让Scrapy开始更早地调用回调吗?最终,在我开始看到物品流过管道之前,我不想等待一百万个请求。

1 个答案:

答案 0 :(得分:3)

我想出了两个解决方案。 1)如果有太多大型站点,则在单独的进程中运行蜘蛛。 2)通过Twisted 使用延迟和回调(请不要逃避,它不会太吓人)。我将讨论如何使用第二种方法,因为第一种方法可以简单地用Google搜索。

执行yield request的每个函数都将"阻止"直到结果可用。因此,您的parse_some_page()函数会生成一个scrapy响应对象,并且在返回响应之前不会继续执行下一个URL。我确实找到了一些需要一段时间才能获取的网站(主要是外国政府网站),希望它可以模拟您遇到的类似情况。这是一个简单快捷的例子:

# spider/stackoverflow_spider.py

from twisted.internet import defer
import scrapy

class StackOverflow(scrapy.Spider):

    name = 'stackoverflow'

    def start_requests(self):
        urls = [
            'http://www.gob.cl/en/',
            'http://www.thaigov.go.th/en.html',
            'https://www.yahoo.com',
            'https://www.stackoverflow.com',
            'https://swapi.co/',
        ]

        for index, url in enumerate(urls):
            # create callback chain after a response is returned
            deferred = defer.Deferred()
            deferred.addCallback(self.parse_some_page)
            deferred.addCallback(self.write_to_disk, url=url, filenumber=index+1)
            # add callbacks and errorbacks as needed

            yield scrapy.Request(
                url=url,
                callback=deferred.callback)     # this func will start the callback chain AFTER a response is returned

    def parse_some_page(self, response):
        print('[1] Parsing %s' % (response.url))
        return response.body    # this will be passed to the next callback

    def write_to_disk(self, content, url, filenumber):
        print('[2] Writing %s content to disk' % (url))
        filename = '%d.html' % filenumber
        with open(filename, 'wb') as f:
            f.write(content)
        # return what you want to pass to the next callback function
        # or raise an error and start Errbacks chain

我稍微改变了一点,以便更容易阅读和运行。在start_requests()中需要注意的第一件事是Deferred个对象被创建,并且回调函数在addCallback()循环中被链接(通过urls)。现在,请查看callback的{​​{1}}参数:

scrapy.Request

此代码段的作用是在请求yield scrapy.Request( url=url, callback=deferred.callback) 可用后立即启动回调链。在Twisted中,scrapy.Response仅在使用值执行Deferreds后才开始运行回调链。

提供响应后,Deferred.callback(result)函数将以parse_some_page()作为参数运行。你要做的就是在这里提取你需要的东西并将它传递给下一个回调(即Response我的例子)。如有必要,您可以在循环中向write_to_disk()添加更多回调。

因此,这个答案与您最初所做的之间的区别在于您使用Deferred先等待所有响应,然后执行回调。我的方法使用yield作为每个请求的回调,以便立即处理每个响应。

希望这有助于(和/或有效)。

参考

PS

我不知道这是否真的对你有用,因为我找不到一个太大而无法解析的网站。另外,我是品牌spankin' Scrapy的新人:D,但我有多年的扭曲。