【问题标题】:Scrapy passing response, missing one positional argumentScrapy 传递响应,缺少一个位置参数
【发布时间】:2017-05-25 14:50:22
【问题描述】:

python 的新手,来自 php。我想使用 Scrapy 抓取一些网站,并且已经很好地完成了教程和简单的脚本。现在写真正的交易会出现这个错误:

Traceback(最近一次调用最后一次):

文件 "C:\Users\Naltroc\Miniconda3\lib\site-packages\twisted\internet\defer.py", 第 653 行,在 _runCallbacks 中 current.result = callback(current.result, *args, **kw)

文件“C:\Users\Naltroc\Documents\Python Scripts\tutorial\tutorial\spiders\quotes_spider.py”,第 52 行,解析中 self.dispatchersite

TypeError: thesaurus() 缺少 1 个必需的位置参数:'response'

当调用 shell 命令scrapy crawl words 时,Scrapy 会自动实例化一个对象。

据我了解,self 是任何类方法的第一个参数。调用类方法时,您不要将self 作为参数传递,而是将其发送给您的变量。

首先这是调用:

# Scrapy automatically provides `response` to `parse()` when coming from `start_requests()`
def parse(self, response):
        site = response.meta['site']
        #same as "site = thesaurus"
        self.dispatcher[site](response)
        #same as "self.dispatcher['thesaurus'](response)

然后

def thesaurus(self, response):
        filename = 'thesaurus.txt'
        words = ''
        ul = response.css('.relevancy-block ul')
        for idx, u in enumerate(ul):
            if idx == 1: 
                break;
            words = u.css('.text::text').extract()

        self.save_words(filename, words)

在 php 中,这应该与调用 $this->thesaurus($response) 相同。 parse 显然将 response 作为变量发送,但 python 说它丢失了。 它去哪儿了?

完整代码在这里:

import scrapy

class WordSpider(scrapy.Spider):
    def __init__(self, keyword = 'apprehensive'):
        self.k = keyword
    name = "words"
    # Utilities
    def make_csv(self, words):
        csv = ''
        for word in words:
            csv += word + ','
        return csv

    def save_words(self, words, fp):
        with ofpen(fp, 'w') as f:
            f.seek(0)
            f.truncate()
            csv = self.make_csv(words)
            f.write(csv)

    # site specific parsers
    def thesaurus(self, response):
        filename = 'thesaurus.txt'
        words = ''
        print("in func self is defined as ", self)
        ul = response.css('.relevancy-block ul')
        for idx, u in enumerate(ul):
            if idx == 1:
                break;
            words = u.css('.text::text').extract()
            print("words is ", words)

        self.save_words(filename, words)

    def oxford(self):
        filename = 'oxford.txt'
        words = ''

    def collins(self):
        filename = 'collins.txt'
        words = ''

    # site/function mapping
    dispatcher = {
        'thesaurus': thesaurus,
        'oxford': oxford,
        'collins': collins,
    }

    def parse(self, response):
        site = response.meta['site']
        self.dispatcher[site](response)

    def start_requests(self):
        urls = {
            'thesaurus': 'http://www.thesaurus.com/browse/%s?s=t' % self.k,
            #'collins': 'https://www.collinsdictionary.com/dictionary/english-thesaurus/%s' % self.k,
            #'oxford': 'https://en.oxforddictionaries.com/thesaurus/%s' % self.k,
        }

        for site, url in urls.items():
            print(site, url)
            yield scrapy.Request(url, meta={'site': site}, callback=self.parse)

【问题讨论】:

    标签: python scrapy arguments web-crawler


    【解决方案1】:

    您的代码周围有很多微小的错误。我冒昧地清理了一下以遵循常见的 python/scrapy 习语:)

    import logging
    import scrapy
    
    
    # Utilities
    # should probably use csv module here or `scrapy crawl -o` flag instead
    def make_csv(words):
        csv = ''
        for word in words:
            csv += word + ','
        return csv
    
    
    def save_words(words, fp):
        with open(fp, 'w') as f:
            f.seek(0)
            f.truncate()
            csv = make_csv(words)
            f.write(csv)
    
    
    class WordSpider(scrapy.Spider):
        name = "words"
    
        def __init__(self, keyword='apprehensive', **kwargs):
            super(WordSpider, self).__init__(**kwargs)
            self.k = keyword
    
        def start_requests(self):
            urls = {
                'thesaurus': 'http://www.thesaurus.com/browse/%s?s=t' % self.k,
                # 'collins': 'https://www.collinsdictionary.com/dictionary/english-thesaurus/%s' % self.k,
                # 'oxford': 'https://en.oxforddictionaries.com/thesaurus/%s' % self.k,
            }
    
            for site, url in urls.items():
                yield scrapy.Request(url, meta={'site': site}, callback=self.parse)
    
        def parse(self, response):
            parser = getattr(self, response.meta['site'])  # retrieve method by name
            logging.info(f'parsing using: {parser}')
            parser(response)
    
        # site specific parsers
        def thesaurus(self, response):
            filename = 'thesaurus.txt'
            words = []
            print("in func self is defined as ", self)
            ul = response.css('.relevancy-block ul')
            for idx, u in enumerate(ul):
                if idx == 1:
                    break
                words = u.css('.text::text').extract()
                print("words is ", words)
            save_words(filename, words)
    
        def oxford(self):
            filename = 'oxford.txt'
            words = ''
    
        def collins(self):
            filename = 'collins.txt'
            words = ''
    

    【讨论】:

    • 感谢您的评论。 1. 如果我知道它总是只需要keyword 作为参数,是否有理由将**kwargs 添加到__init__? 2. 看起来parse 函数充当控制器,首先获取正确的解析器,然后将数据传递给它。这是合理的,但它是发送response 数据的唯一方法吗? 3、为什么使用getattr(self, response.meta['site'])允许调用适当的方法而不用前缀self.
    • 关于#1。由于您从 Spider 继承,您想将 kwargs 传递给父类,因此这里没有什么特别值得传递的,但它是一种模式,可以证明未来。 2. 您误解了scrapy 的工作原理,默认情况下,spider 会为start_urls 中的每个url 启动一系列请求,并且默认回调parse(),其中 response 是其中一个 start_urls 的响应对象。 3.你误解了自我是什么; self 是对当前类对象的引用,因此在使用 getattr 时,您不需要它,因为它会为您提供独立的引用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-16
    • 1970-01-01
    • 2021-09-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多