Scrapy передает ответ, отсутствует один позиционный аргумент

Новичок в 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

Ошибка типа: в thesaurus() отсутствует 1 обязательный позиционный аргумент: "ответ"

Scrapy автоматически создает экземпляр объекта при вызове команды оболочки scrapy crawl words.

Насколько я понимаю, 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)

person Naltroc    schedule 25.05.2017    source источник


Ответы (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 = ''
person Granitosaurus    schedule 25.05.2017
comment
Спасибо за обзор. 1. Есть ли причина добавлять **kwargs к __init__, если я знаю, что в качестве аргумента всегда будет приниматься только keyword? 2. Похоже, что функция parse служит контроллером, сначала получая нужный анализатор, а затем передавая ему данные. Это разумно, но является ли это единственным способом передачи response данных? 3. Почему использование getattr(self, response.meta['site']) позволяет вызывать соответствующий метод без префикса self.? - person Naltroc; 25.05.2017
comment
По поводу №1. Поскольку вы наследуете от Паука, вы хотите передать кваргов отцовскому классу, здесь нет ничего особенного, что стоило бы передавать, но это шаблон, который делает это будущим доказательством. 2. Вы неправильно понимаете, как работает scrapy, по умолчанию паук запускает цепочку запросов для каждого URL-адреса в start_urls и с обратным вызовом по умолчанию parse(), где ответ — это объект ответа одного из этих start_urls. 3. Вы неправильно понимаете, что такое «я»; self является ссылкой на текущий объект класса, поэтому при использовании getattr он вам не нужен, так как он получает для вас независимую ссылку. - person Granitosaurus; 25.05.2017