首页 > 解决方案 > Scrapy - 不能做多个回调

问题描述

我在浏览多个页面时遇到问题。这是我的名为引号的scrapy代码课程。

class quotes(scrapy.Spider):
    name = 'quotes'
    start_urls = ['http://books.toscrape.com/?']

def parse(self, response):
    all_links = response.css('.nav-list ul li')
    for links in all_links:
        link = links.css('a::attr(href)').get()
        yield response.follow(link, callback = self.books_detail)

def books_detail(self, response):
    yas = {
    'title':[],
    'price':[],
    'availability':[],
    'category':[]
    }
    yas['category'].append(response.css('h1::text').extract())
    all_divs = response.css('.col-lg-3')
    for div in all_divs:
        link = div.css('.product_pod a::attr(href)').get()
        title = response.follow(link, callback = self.get_title)
        
        yas['price'].append(div.css('.price_color::text').extract())
        yas['availability'].append(div.css('.availability::text')[1].extract())
    yield yas


def get_title(self,response):   
    print('testing')
    title = response.css('h1::text').extract()
    yield {"title":title}

所以我使用 response.follow 转到函数books_details并在该函数中,我再次调用 response.follow 来调用get_title。我从 get_title 获取“标题”,从主页获取其他详细信息。我可以从books_details函数中很好地抓取信息,可以从代码行中很好地获取标题页的链接。

link = div.css('.product_pod a::attr(href)').get()

但是使用 response.follow 我不能去 get_title 功能。 任何帮助,将不胜感激。谢谢。

标签: pythonscrapy

解决方案


您应该产生请求,而不是直接运行它,并用于meta=将数据发送到下一个解析器

yield response.follow(link, callback=self.get_title, meta={'item': yas})

在下一个解析器中你可以得到它

yas = response.meta['item']

然后您可以添加新值并生成所有数据

yas["title"] = response.css('h1::text').extract()

yield yas

请参阅Scrapy yield items from multiple requests中的其他示例

Doc: Request 和 Response , Request.meta 特殊键


您可以将其放在一个文件中并作为普通脚本 ( python script.py) 运行而无需创建项目的最少工作代码。

还有其他变化。

您不应该将所有书籍都放在一个列表中,而应分别生成每本书。Scrapy 将保留所有结果,当您使用选项保存在 csv 中时,它将保存所有结果。

对于每本书,您都应该创建新词典。如果您多次使用同一个字典,那么它将覆盖数据,并且您可能会使用相同的数据获得许多结果。

import scrapy

class QuotesSpider(scrapy.Spider):

    name = 'quotes'

    start_urls = ['http://books.toscrape.com/']

    def parse(self, response):
        all_links = response.css('.nav-list ul li')
        for links in all_links:
            link = links.css('a::attr(href)').get()
            yield response.follow(link, callback=self.books_detail)


    def books_detail(self, response):

        all_divs = response.css('.col-lg-3')

        for div in all_divs:
            # every book in separated dictionary and it has to be new dictionary - because it could overwrite old data
            book = {
                'category': response.css('h1::text').extract(),
                'price': div.css('.price_color::text').extract()[0].strip(),
                'availability': div.css('.availability::text')[1].extract().strip(),
            }

            link = div.css('.product_pod a::attr(href)').get()
            yield response.follow(link, callback=self.get_title, meta={'item': book})


    def get_title(self, response):
        book = response.meta['item']

        print('testing:', response.url)

        book["title"] = response.css('h1::text').extract()[0].strip() 

        yield book

# --- run without project and save in `output.csv` ---

from scrapy.crawler import CrawlerProcess

c = CrawlerProcess({
    'USER_AGENT': 'Mozilla/5.0',
    # save in file CSV, JSON or XML
    'FEED_FORMAT': 'csv',     # csv, json, xml
    'FEED_URI': 'output.csv', #
})

c.crawl(QuotesSpider)
c.start()

推荐阅读