【问题标题】:Scrapy spider not including all requested pagesScrapy 蜘蛛不包括所有请求的页面
【发布时间】:2014-04-26 01:26:45
【问题描述】:

我有一个用于 Yelp 的 Scrapy 脚本,在大多数情况下,它可以正常工作。本质上,我可以为它提供 Yelp 页面列表,它应该返回所有页面的所有评论。到目前为止的脚本如下:

from scrapy.spider import Spider
from scrapy.selector import Selector
from scrapy.http import Request
import re

from yelp2.items import YelpReviewItem

RESTAURANTS = ['sixteen-chicago']

def createRestaurantPageLinks(self, response):
    reviewsPerPage = 40
    sel = Selector(response)
    totalReviews = int(sel.xpath('//div[@class="rating-info clearfix"]//span[@itemprop="reviewCount"]/text()').extract()[0].strip().split(' ')[0])
    pages = [Request(url=response.url + '?start=' + str(reviewsPerPage*(n+1)), callback=self.parse) for n in range(totalReviews/reviewsPerPage)]
    return pages

class Yelp2aSpider(Spider):
    name = "yelp2a"
    allowed_domains = ["yelp.com"]
    start_urls = ['http://www.yelp.com/biz/%s' % s for s in RESTAURANTS]

    def parse(self, response):
        requests = []

        sel = Selector(response)
        reviews = sel.xpath('//div[@class="review review-with-no-actions"]')
        items = []

        for review in reviews:
            item = YelpReviewItem()
                item['venueName'] = sel.xpath('//meta[@property="og:title"]/@content').extract()
                item['reviewer'] = review.xpath('.//li[@class="user-name"]/a/text()').extract()
                item['reviewerLoc'] = review.xpath('.//li[@class="user-location"]/b/text()').extract()
                item['rating'] = review.xpath('.//meta[@itemprop="ratingValue"]/@content').extract()
                item['reviewDate'] = review.xpath('.//meta[@itemprop="datePublished"]/@content').extract()
                item['reviewText'] = review.xpath('.//p[@itemprop="description"]/text()').extract()
                item['url'] = response.url
                items.append(item)
        return items

        if response.url.find('?start=') == -1:
            requests += createRestaurantPageLinks(self, response)
            return requests

但是,我遇到的问题是,这个特定的脚本会抓取每个请求评论的每一页,但第一页除外。如果我注释掉最后一个“if”语句,它只会刮掉第一页。我怀疑我需要的只是一个简单的“else”命令,但我很难过......非常感谢帮助!

编辑:这是基于收到的援助的当前代码...

from scrapy.spider import Spider
from scrapy.selector import Selector
from scrapy.http import Request
import re

from yelp2.items import YelpReviewItem

RESTAURANTS = ['sixteen-chicago']

def createRestaurantPageLinks(self, response):
    reviewsPerPage = 40
    sel = Selector(response)
    totalReviews = int(sel.xpath('//div[@class="rating-info clearfix"]//span[@itemprop="reviewCount"]/text()').extract()[0].strip().split(' ')[0])
    pages = [Request(url=response.url + '?start=' + str(reviewsPerPage*(n+1)), callback=self.parse) for n in range(totalReviews/reviewsPerPage)]
    return pages

class Yelp2aSpider(Spider):
    name = "yelp2a"
    allowed_domains = ["yelp.com"]
    start_urls = ['http://www.yelp.com/biz/%s' % s for s in RESTAURANTS]

    def parse(self, response):
        requests = []

        sel = Selector(response)
        reviews = sel.xpath('//div[@class="review review-with-no-actions"]')
        items = []

        for review in reviews:
            item = YelpReviewItem()
            item['venueName'] = sel.xpath('//meta[@property="og:title"]/@content').extract()
            item['reviewer'] = review.xpath('.//li[@class="user-name"]/a/text()').extract()
            item['reviewerLoc'] = review.xpath('.//li[@class="user-location"]/b/text()').extract()
            item['rating'] = review.xpath('.//meta[@itemprop="ratingValue"]/@content').extract()
            item['reviewDate'] = review.xpath('.//meta[@itemprop="datePublished"]/@content').extract()
            item['reviewText'] = review.xpath('.//p[@itemprop="description"]/text()').extract()
            item['url'] = response.url
        yield item

        if response.url.find('?start=') == -1:
            requests += createRestaurantPageLinks(self, response)
            for request in requests:
                    yield request

正如下面评论中提到的,按原样运行此代码会爬取每个所需页面,但它只返回每个页面的一条评论,而不是所有评论。

我尝试将yield item 更改为yield items,但每次抓取的网址都会返回ERROR: Spider must return Request, BaseItem or None, got 'list' in <GET http://www.yelp.com/biz/[...]> 的错误消息。

【问题讨论】:

  • 这到底有什么问题?
  • 我想你也想用review.xpath(.//替换sel.xpath(//的值review.xpath(.//,对吧?
  • 我编辑了我的答案并进行了更正。这种方式对我有用,如果它解决了你的问题,请告诉我。
  • 最后成功了吗...?
  • 是的!最后!感谢您所有的帮助!我将发布暂时有效的解决方案。

标签: python web-scraping web-crawler scrapy scrapy-spider


【解决方案1】:

您需要稍微重新组织方法。首先在parse() 方法中解析餐厅页面。然后,返回评论请求并以另一种方法处理响应,例如parse_review():

import re

from scrapy.item import Item, Field
from scrapy.spider import Spider
from scrapy.selector import Selector
from scrapy.http import Request

from yelp2.items import YelpReviewItem


RESTAURANTS = ['sixteen-chicago']

class Yelp2aSpider(Spider):
    name = "yelp2a"
    allowed_domains = ["yelp.com"]
    start_urls = ['http://www.yelp.com/biz/%s' % s for s in RESTAURANTS]

    def parse(self, response):
        reviewsPerPage = 40
        sel = Selector(response)
        totalReviews = int(sel.xpath('//div[@class="rating-info clearfix"]//span[@itemprop="reviewCount"]/text()').extract()[0].strip().split(' ')[0])
        pages = [Request(url=response.url + '?start=' + str(reviewsPerPage*(n+1)), callback=self.parse_review) for n in range(totalReviews/reviewsPerPage)]
        return pages

    def parse_review(self, response):
        sel = Selector(response)
        reviews = sel.xpath('//div[@class="review review-with-no-actions"]')
        for review in reviews:
            item = YelpReviewItem()
            item['venueName'] = sel.xpath('//meta[@property="og:title"]/@content').extract()
            item['reviewer'] = review.xpath('.//li[@class="user-name"]/a/text()').extract()
            item['reviewerLoc'] = review.xpath('.//li[@class="user-location"]/b/text()').extract()
            item['rating'] = review.xpath('.//meta[@itemprop="ratingValue"]/@content').extract()
            item['reviewDate'] = review.xpath('.//meta[@itemprop="datePublished"]/@content').extract()
            item['reviewText'] = review.xpath('.//p[@itemprop="description"]/text()').extract()
            item['url'] = response.url
            yield item

【讨论】:

  • 没有骰子。这仍然只返回除第一个页面之外的每一页。
【解决方案2】:

如果您在多个地方返回项目/请求,则应将 return 语句替换为 yield 语句,这会将您的函数变成一个生成器,每次生成时都会返回一个新元素(产量它),在它们全部返回之前不退出函数。否则,就像您现在的代码一样,您的函数将在第一个 return 之后退出,并且不会发送后续页面的请求。

编辑:更正 - 你应该一次产生一个项目/请求,所以:

替换

for review in reviews:
    item = ...
return items

for review in reviews:
    item = ...
    yield item

替换

return requests

for request in requests:
    yield request

【讨论】:

  • 我尝试按照您的建议将return 语句与yield 语句交换,使用我的原始代码和上面建议的代码,但在这两种情况下我都会收到错误消息。具体来说,我尝试将return items 更改为yield items,但这给了我SyntaxError: 'return' with argument inside generator 的错误。事实上,我已经尝试了几乎所有我能想到的变体,将return 换成yield,所有的排列要么给我一条错误消息,要么不改变行为。
  • 如果您在同一个函数中同时使用了yieldreturn,您提到的错误应该会出现,如果您将函数中的一个return 更改为yield,您应该更改它们全部。这只是我关于 yield 的 2 美分,我不确定其他错误或您原来的问题。
  • 我已经根据您的建议更新了上面的代码——它现在正在抓取每一页,但每页只返回一个项目。请参阅上面的更新代码。
  • yield item 应该在循环内部而不是外部 - 请参阅我的示例。将该行再缩进一次(以便它产生每个项目,而不仅仅是最后一个)
【解决方案3】:

最终的答案确实在于单个yield 行的缩进。这是最终完成我需要它做的事情的代码。

from scrapy.spider import Spider
from scrapy.selector import Selector
from scrapy.http import Request
import re

from yelp2.items import YelpReviewItem

RESTAURANTS = ['sixteen-chicago']

def createRestaurantPageLinks(self, response):
    reviewsPerPage = 40
    sel = Selector(response)
    totalReviews = int(sel.xpath('//div[@class="rating-info clearfix"]//span[@itemprop="reviewCount"]/text()').extract()[0].strip().split(' ')[0])
    pages = [Request(url=response.url + '?start=' + str(reviewsPerPage*(n+1)), callback=self.parse) for n in range(totalReviews/reviewsPerPage)]
    return pages

class YelpXSpider(Spider):
    name = "yelpx"
    allowed_domains = ["yelp.com"]
    start_urls = ['http://www.yelp.com/biz/%s' % s for s in RESTAURANTS]

    def parse(self, response):
        requests = []

        sel = Selector(response)
        reviews = sel.xpath('//div[@class="review review-with-no-actions"]')
        items = []

        for review in reviews:
            item = YelpReviewItem()
            item['venueName'] = sel.xpath('//meta[@property="og:title"]/@content').extract()
            item['reviewer'] = review.xpath('.//li[@class="user-name"]/a/text()').extract()
            item['reviewerLoc'] = review.xpath('.//li[@class="user-location"]/b/text()').extract()
            item['rating'] = review.xpath('.//meta[@itemprop="ratingValue"]/@content').extract()
            item['reviewDate'] = review.xpath('.//meta[@itemprop="datePublished"]/@content').extract()
            item['reviewText'] = review.xpath('.//p[@itemprop="description"]/text()').extract()
            item['url'] = response.url
            yield item

        if response.url.find('?start=') == -1:
            requests += createRestaurantPageLinks(self, response)
            for request in requests:
                    yield request

感谢大家帮助菜鸟!

【讨论】:

    猜你喜欢
    • 2012-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-11
    • 2017-05-21
    相关资源
    最近更新 更多