Scrapy CrawlSpider 规则与多个回调

3 投票
1 回答
4402 浏览
提问于 2025-04-18 06:45

我正在尝试创建一个叫做ExampleSpider的爬虫,它是基于scrapy的CrawlSpider。我的ExampleSpider应该能够处理只包含艺术家信息的页面、只包含专辑信息的页面,以及一些同时包含专辑和艺术家信息的页面。

我已经能处理前两种情况了,但在第三种情况下出现了问题。我使用parse_artist(response)方法来处理艺术家数据,使用parse_album(response)方法来处理专辑数据。我的问题是,如果一个页面同时包含艺术家和专辑的数据,我应该如何定义我的规则呢?

  1. 我应该像下面这样做吗?(对同一个网址模式使用两个规则)
  2. 我应该使用多个回调吗?(scrapy支持多个回调吗?)
  3. 还有其他方法吗?(一种合适的方法)

    class ExampleSpider(CrawlSpider):
        name = 'example'
    
        start_urls = ['http://www.example.com']
    
        rules = [
            Rule(SgmlLinkExtractor(allow=[r'same regex_rule']), callback='parse_artist', follow=True),
            Rule(SgmlLinkExtractor(allow=[r'same regex_rule']), callback='parse_album', follow=True),
            # more rules .....
        ]
    
        def parse_artist(self, response):
            artist_item = ArtistItem()
            try:
                # do the scrape and assign to ArtistItem
            except Exception:
                # ignore for now
                pass
            return artist_item
            pass
    
        def parse_album(self, response):
            album_item = AlbumItem()
            try:
                # do the scrape and assign to AlbumItem
            except Exception:
                # ignore for now
                pass
            return album_item
            pass
        pass
    

1 个回答

9

CrawlSpider 会调用 _requests_to_follow() 这个方法来提取网址并生成请求以便继续跟进:

def _requests_to_follow(self, response):
    if not isinstance(response, HtmlResponse):
        return
    seen = set()
    for n, rule in enumerate(self._rules):
        links = [l for l in rule.link_extractor.extract_links(response) if l not in seen]
        if links and rule.process_links:
            links = rule.process_links(links)
        seen = seen.union(links)
        for link in links:
            r = Request(url=link.url, callback=self._response_downloaded)
            r.meta.update(rule=n, link_text=link.text)
            yield rule.process_request(r)

从上面可以看到:

  • 变量 seen 用来记住已经处理过的 urls
  • 每个 url 最多只会被一个 callback 处理。

你可以定义一个 parse_item() 方法来调用 parse_artist()parse_album()

rules = [
    Rule(SgmlLinkExtractor(allow=[r'same regex_rule']), callback='parse_item', follow=True),
    # more rules .....
]

def parse_item(self, response):

    yield self.parse_artist(response)
    yield self.parse_album(response)

撰写回答