【问题标题】:Scrapy skip request based on previous crawl from same spiderScrapy 跳过请求基于来自同一蜘蛛的先前爬网
【发布时间】:2021-10-13 20:39:34
【问题描述】:

在下面的示例中,每个桶都有很多球。任一桶中可能有也可能没有红球。为了确定一个球是否是红色的,我们抓取它。

如果找到一个红球,我想停止爬行其余的球(即我不希望发出下一个球的请求,我知道它不会是红色的,因为我'已经找到了)。

桶和球标识符是基本 URL 的查询参数。

我尝试过的 #1

维护一个类状态并检查一个桶是否已经有红球

class BucketsBallsSpider(scrapy.Spider):
    name = 'test_spider'
    base_url = 'https://bucketswithballs.com'
    buckets = []
    balls = []
    buckets_with_red_balls = []
    
    def start_requests(self):
        for bucket in self.buckets:
            for ball in self.balls:
                if bucket in self.buckets_with_red_balls:
                    break
                url = add_or_replace_parameter(self.base_url, 'bucket', bucket)
                url = add_or_replace_parameter(url, 'ball', ball)
                yield scrapy.Request(url, self.parse)
                
    def parse(self, response, **kwargs):
        is_red_ball = response.xpath('//*[@id="is_red_ball"]').extract()
        if is_red_ball:
            bucket_id = url_query_parameter(response.url, 'bucket')
            self.buckets_with_red_balls.append(bucket_id)
            yield {'bucket_with_red_ball': bucket_id}

我尝试过的 #2

解析方法中的yield请求

class BucketsBallsSpider(scrapy.Spider):
    name = 'test_spider'
    base_url = 'https://bucketswithballs.com'
    buckets = []
    balls = []
    buckets_with_red_balls = []

    def start_requests(self):
        # Start from first bucket and first ball
        url = add_or_replace_parameter(self.base_url, 'bucket', self.buckets[0])
        url = add_or_replace_parameter(url, 'ball', self.balls[0])
        yield scrapy.Request(url, self.parse)

    def parse(self, response, **kwargs):
        is_red_ball = response.xpath('//*[@id="is_red_ball"]').extract()
        if is_red_ball:
            bucket_id = url_query_parameter(response.url, 'bucket')
            self.buckets_with_red_balls.append(bucket_id)
            yield {'bucket_with_red_ball': bucket_id}

        # Scrapy filter will skip duplicates
        for bucket in self.buckets:
            for ball in self.balls:
                if bucket in self.buckets_with_red_balls:
                    break
                url = add_or_replace_parameter(self.base_url, 'bucket', bucket)
                url = add_or_replace_parameter(url, 'ball', ball)
                yield scrapy.Request(url, self.parse)

对于每个示例,Scrapy 在控制台中告诉我它抓取了每个 URL。出于性能原因,我想避免这种情况。

【问题讨论】:

    标签: python python-3.x scrapy scrapy-pipeline


    【解决方案1】:

    它不起作用,因为 Scrapy 以异步方式工作,我认为您不能停止其他请求,因为它们可能已经在进行中。当发现红球时,您可以引发CloseSpider() 异常来终止蜘蛛,但并发请求将在蜘蛛关闭之前完成。见 Scrapy 架构here

    如果您需要它停止并且在找到红球后不发出任何请求,我认为您希望它是同步的。例如,使用 Python requests 可能会更容易。

    话虽如此,我更新了您的示例以同步工作(我尚未测试)。这将强制 Scrapy 一个一个地发出请求,无论它被配置为发出多个并发请求,但效率不是很高。

    class BucketsBallsSpider(scrapy.Spider):
    name = 'test_spider'
    base_url = 'https://bucketswithballs.com'
    buckets = []
    balls = []
    
    current_bucket_idx = 0
    current_ball_idx = 0
    
    def start_requests(self):
        # Start from first bucket and first ball
        url = add_or_replace_parameter(self.base_url, 'bucket', self.buckets[0])
        url = add_or_replace_parameter(url, 'ball', self.balls[0])
        yield scrapy.Request(url, self.parse)
    
    def parse(self, response, **kwargs):
        is_red_ball = response.xpath('//*[@id="is_red_ball"]').extract()
        if is_red_ball:
            bucket_id = url_query_parameter(response.url, 'bucket')
            yield {'bucket_with_red_ball': bucket_id}
            return
    
        next_bucket, next_ball = self._get_next_bucket_and_ball()
        if not next_bucket:
            return      
    
        url = add_or_replace_parameter(self.base_url, 'bucket', next_bucket)
        url = add_or_replace_parameter(url, 'ball', next_ball)
        yield scrapy.Request(url, self.parse)
    
    def _get_next_bucket_and_ball(self):
        if self.current_ball_idx < len(self.balls) - 1:
            self.current_ball_idx += 1
    
        else:
            self.current_ball_idx = 0   
            if self.current_bucket_idx < len(self.buckets) - 1:
                self.current_bucket_idx += 1
            else:
                # No more buckets/balls to try
                return None, None
    
        next_bucket = self.buckets[self.current_bucket_idx]
        next_ball = self.balls[self.current_ball_idx]
        return next_bucket, next_ball
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多