【问题标题】:How to scrape multile parts of a single page with Python Scrapy?如何使用 Python Scrapy 抓取单个页面的多个部分?
【发布时间】:2017-05-07 01:15:57
【问题描述】:

鉴于我有一个 HTML 文件,其中包含多个具有不同结构的部分,这些部分需要大不相同的抓取。 蜘蛛布局的最佳做法是什么?

我应该使用一只蜘蛛还是多只蜘蛛?我是否应该多次请求同一个 URL,每次都使用不同的回调函数?或者只是顺序解析不同的部分?我问的是能够很好地与框架的其他部分(例如 itemspipleines 配合使用)以及性能、限制和缓存方面。

那么,有什么最佳实践建议吗?社区中使用的规则或约定?

多个请求

如果我多次请求一个 URL,它会被缓存/限制吗?还是对引擎的每个请求都会导致对“外部网络服务器”的请求?

class MultiSpider(scrapy.Spider):

    """Parse the parts in parallel."""

    name = 'multispider'

    def start_requests(self):
        url = 'https://en.wikipedia.org/wiki/Main_Page'
        yield scrapy.Request(url=url, callback=self.parser_01)
        yield scrapy.Request(url=url, callback=self.parser_02)

    def parser_01(self, response):
        selector = response.xpath('//some/path')
        if selector is not None:
            # do stuff with *selector* and
            yield {}

    def parser_0(self, response):
        selector = response.xpath('//some/other/path')
        if selector is not None:
            # do very different stuff with *selector* and
            yield {}

多个解析器函数

如果我想避免使用庞大的 parse 函数,而是将多个函数用于不同的任务/部分,是否有特别好的/坏的方法来构建它(例如“如何从哪里产生”)?

class SeqSpider(scrapy.Spider):

    """Parse the page sequentially."""

    name = 'seqspider'

    start_urls = ['https://en.wikipedia.org/wiki/Main_Page', ]

    def parse(self, response):
        selector = response.xpath('//some/path')
        if selector:
            yield from self.parser_01(response, selector):
        selector = response.xpath('//some/other/path')
        if selector:
            yield from self.parser_02(response, selector):

    def parser_01(self, response, selector):
        # do stuff with *selector* and
        yield {}

    def parser_0(self, response, selector):
        # do very different stuff with *selector* and
        yield {}

【问题讨论】:

    标签: python scrapy scrapy-spider


    【解决方案1】:

    要回答您关于如何构建蜘蛛/最佳实践的问题,我通常会这样做:

    1. 避免构建在同一页面上工作的多个爬虫 - 因为目标网站的带宽通常是瓶颈,并且在网站上创建不必要的流量被视为不礼貌的抓取。

    2. 对同一个 URL 创建多个请求也是如此:它可能会为目标网站带来不必要的流量,这不是好的爬虫行为。此外,scrapy 默认会过滤重复请求,因此您可能会突然想知道为什么没有执行所有请求。

    (注意:有一些方法可以解决这个问题,也可以使用代理等......但这会使事情变得不必要的复杂)

    1. 因此,如果您想避免执行许多不同操作的大型解析方法,请随意将其拆分。与您在自己的示例中建议的非常相似。我什至会更进一步,将完整的处理步骤封装到单独的解析方法中,例如

      class SomeSpider(scrapy.Spider):
      
          def parse(self, response):
              yield self.parse_widgets_type_a(response)
              yield self.parse_widgets_type_b(response)
              # ....
              yield self.follow_interesting_links(response)
      
          def parse_widgets_type_a(self, response):
              # ....
      
          def parse_widgets_type_b(self, response):
              # ....
      
          def follow_interesting_links(self, response):
              # ....
              yield Request(interesting_url)
      

    基于此模板,您甚至可能希望将不同的解析方法重构为不同的 Mixin 类。

    【讨论】:

    • 谢谢!这有很大帮助。但我必须在parse 函数中使用yield from,否则我会得到ERROR: Spider must return Request, BaseItem, dict or None, got 'generator'
    【解决方案2】:

    如果是单页,我建议使用一个蜘蛛。请求页面一次并解析您需要的所有数据(您可以为此使用一个或多个函数)。

    我也建议使用物品,例如

    import scrapy
    
    class AmazonItem(scrapy.Item):
        product_name = scrapy.Field()
        product_asin = scrapy.Field()
        product_avg_stars = scrapy.Field()
        product_num_reviews = scrapy.Field()
        pass
    

    如果您想将抓取的数据保存到您拥有的数据库中,则应使用管道。

    【讨论】:

    • 感谢您的信息!我已经使用了物品和管道。来自 Django,Scrapy 很容易上手。我倾向于在常见的编码实践和类似的事情上更加挣扎;)尤其是在如何构建我的代码以保持事情干净、分离并且希望不会太低效方面。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多