【问题标题】:How to use the yield function to scrape data from multiple pages如何使用 yield 函数从多个页面中抓取数据
【发布时间】:2019-04-11 09:34:48
【问题描述】:

我正在尝试从亚马逊印度网站上抓取数据。在以下情况下,我无法使用 yield() 方法收集响应和解析元素: 1)我必须从产品页面移动到评论页面 2)我必须从一个评论页面移动到另一个评论页面

Product page

Review page

代码流程:

1) customerReviewData() 调用 getCustomerRatingsAndComments(response)

2) getCustomerRatingsAndComments(响应) 找到review页面的url,调用yield request方法,getCrrFromReviewPage(request)作为回调方法,带有这个review页面的url

3) getCrrFromReviewPage() 获取第一个评论页面的新响应,并从第一个评论页面(页面加载)中抓取所有元素并将其添加到 customerReviewDataList[]

4)如果存在则获取下一页的URL,递归调用getCrrFromReviewPage()方法,从下一页抓取元素,直到抓取完所有评论页

5) 所有评论都添加到 customerReviewDataList[]

我尝试过使用 yield() 更改参数,还查看了 yield() 和 Request/Response yieldscrapy 文档。

# -*- coding: utf-8 -*-
import scrapy
import logging

customerReviewDataList = []
customerReviewData = {}

#Get product name in <H1>
def getProductTitleH1(response):
    titleH1 =  response.xpath('normalize-space(//*[@id="productTitle"]/text())').extract()
    return titleH1

def getCustomerRatingsAndComments(response):
    #Fetches the relative url
    reviewRelativePageUrl = response.css('#reviews-medley-footer a::attr(href)').extract()[0]
    if reviewRelativePageUrl:
        #get absolute URL
        reviewPageAbsoluteUrl = response.urljoin(reviewRelativePageUrl)
        yield Request(url = reviewPageAbsoluteUrl, callback = getCrrFromReviewPage())
        self.log("yield request complete")

    return len(customerReviewDataList)

def getCrrFromReviewPage():

    userReviewsAndRatings = response.xpath('//div[@id="cm_cr-review_list"]/div[@data-hook="review"]')


    for userReviewAndRating in userReviewsAndRatings:
        customerReviewData[reviewTitle] = response.css('#cm_cr-review_list .review-title span ::text').extract()
        customerReviewData[reviewDescription] = response.css('#cm_cr-review_list .review-text span::text').extract()
        customerReviewDataList.append(customerReviewData) 

    reviewNextPageRelativeUrl = response.css('#cm_cr-pagination_bar .a-pagination .a-last a::attr(href)')[0].extract()

    if reviewNextPageRelativeUrl:
        reviewNextPageAbsoluteUrl = response.urljoin(reviewNextPageRelativeUrl)
        yield Request(url = reviewNextPageAbsoluteUrl, callback = getCrrFromReviewPage())


class UsAmazonSpider(scrapy.Spider):
    name = 'Test_Crawler'
    allowed_domains = ['amazon.in']
    start_urls = ['https://www.amazon.in/Philips-Trimmer-Cordless-Corded-QT4011/dp/B00JJIDBIC/ref=sr_1_3?keywords=philips&qid=1554266853&s=gateway&sr=8-3']

    def parse(self, response):
        titleH1 = getProductTitleH1(response),
        customerReviewData = getCustomerRatingsAndComments(response)

        yield{
        'Title_H1' : titleH1,
        'customer_Review_Data' : customerReviewData
        }


我收到以下回复:

{'Title_H1': (['Philips Beard Trimmer Cordless and Corded for Men QT4011/15'],), 'customer_Review_Data': <generator object getCustomerRatingsAndComments at 0x048AC630>}

“Customer_review_Data”应该是标题和评论的字典列表

我无法弄清楚我在这里犯了什么错误。

当我使用 log() 或 print() 查看 customerReviewDataList[] 中捕获了哪些数据时,也无法在控制台中看到数据。

我可以抓取 customerReviewDataList[] 中的所有评论,如果它们出现在产品页面中,

在我必须使用 yield 函数的这种情况下,我得到的输出如下所示 [https://ibb.co/kq8w6cf]

这是我正在寻找的输出类型:

{'customerReviewTitle': ['Difficult to find a charger adapter'],'customerReviewComment': ['I already have a phillips trimmer which was only cordless. ], 'customerReviewTitle': ['Good Product'],'customerReviewComment': ['Solves my need perfectly HK']}]}

感谢任何帮助。提前致谢。

【问题讨论】:

  • 我没有深入研究你的代码,但有两点跳出来:(1)不要使用全局变量;使用meta 从一个Request 与其回调进行通信 (2) callback = getCrrFromReviewPage()) 不符合您的想法;使用括号表示调用 getCrrFromReviewPage 并使用其返回值作为callable,而callback = getCrrFromReviewPage) 表示使用实际方法作为callback。您还应该让自己 a real Python editor 通知您 getCrrFromReviewPage 缺少 response 参数
  • 你的 Spider 课程在哪里?你完成了 Scrapy 教程吗?
  • 谢谢@MatthewLDaniel:我明白你的意思。 1,关于第 2 点,我尝试运行以下 callback = getCrrFromReviewPage()callback = getCrrFromReviewPage 并且还使用了 yield response.follow(url , self.callbackMethod) 但我的回调方法没有被调用/执行。另外,我们不必在回调方法中传递响应作为参数,scrapy 隐式获取传递的 URL 的响应,可以在回调方法中使用。
  • 感谢@Gallaecio,我正在学习教程,上面代码中的蜘蛛类。

标签: scrapy scrapy-splash


【解决方案1】:

您应该完成 Scrapy 教程。 Following links 部分应该对您特别有帮助。

这是您的代码的简化版本:

def data_request_iterator():
    yield Request('https://example.org')

class MySpider(Spider):
    name = 'myspider'
    start_urls = ['https://example.com']

    def parse(self, response):
        yield {
            'title': response.css('title::text').get(),
            'data': data_request_iterator(),
        }

相反,它应该是这样的:

class MySpider(Spider):
    name = 'myspider'
    start_urls = ['https://example.com']

    def parse(self, response):
        item = {
            'title': response.css('title::text').get(),
        }
        yield Request('https://example.org', meta={'item': item}, callback=self.parse_data)

    def parse_data(self, response):
        item = response.meta['item']
        # TODO: Extend item with data from this second response as needed.
        yield item

【讨论】:

  • 感谢@gallaecio,代码工作得非常好,当从其中递归调用 parse_data() 时,列表中的新旧 cmets 会产生多次,有什么办法吗,我可以将所有 cmets 存储在列表中,最后只调用一次 yield 项
  • 如果您在完成教程后仍然不确定,请针对第二个问题打开一个新的 Stackoverflow 问题。
猜你喜欢
  • 2021-07-12
  • 1970-01-01
  • 2017-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多