【问题标题】:How to limit number of followed pages per site in Python Scrapy如何在 Python Scrapy 中限制每个站点的关注页面数
【发布时间】:2013-05-21 17:52:22
【问题描述】:

我正在尝试构建一个可以有效地从许多网站上抓取文本信息的蜘蛛。由于我是 Python 用户,因此我被称为 Scrapy。但是,为了避免抓取庞大的网站,我想限制蜘蛛抓取不超过 20 个具有一定“深度”的页面每个网站。这是我的蜘蛛:

class DownloadSpider(CrawlSpider):
    name = 'downloader'
    download_path = '/home/MyProjects/crawler'
    rules = (Rule(SgmlLinkExtractor(), callback='parse_item', follow=True),)

    def __init__(self, *args, **kwargs):
        super(DownloadSpider, self).__init__(*args, **kwargs)
        self.urls_file_path = [kwargs.get('urls_file')]
        data = open(self.urls_file_path[0], 'r').readlines()
        self.allowed_domains = [urlparse(i).hostname.strip() for i in data] 
        self.start_urls = ['http://' + domain for domain in self.allowed_domains]

    def parse_start_url(self, response):
        return self.parse_item(response)

    def parse_item(self, response):
        self.fname = self.download_path + urlparse(response.url).hostname.strip()
        open(str(self.fname)+ '.txt', 'a').write(response.url)
        open(str(self.fname)+ '.txt', 'a').write('\n')

urls_file 是带有 urls 的文本文件的路径。我还在设置文件中设置了最大深度。这是我的问题:如果我设置了CLOSESPIDER_PAGECOUNT 异常,它会在total 抓取页面数(无论哪个站点)达到异常值时关闭蜘蛛。但是,当我从每个 url 抓取 20 页时,我需要停止抓取。 我还尝试使用 self.parsed_number += 1 之类的变量来计数,但这也不起作用——scrapy 似乎不会逐个 url,而是将它们混合在一起。 非常感谢任何建议!

【问题讨论】:

    标签: python scrapy web-crawler


    【解决方案1】:

    为此,您可以基于 SgmlLinkExtractor 创建自己的链接提取器类。它应该看起来像这样:

    from scrapy.selector import Selector
    from scrapy.utils.response import get_base_url
    
    from scrapy.contrib.linkextractors.sgml import SgmlLinkExtractor
    
    class LimitedLinkExtractor(SgmlLinkExtractor):
        def __init__(self, allow=(), deny=(), allow_domains=(), deny_domains=(), restrict_xpaths=(),
                     tags=('a', 'area'), attrs=('href'), canonicalize=True, unique=True, process_value=None,
                     deny_extensions=None, max_pages=20):
            self.max_pages=max_pages
    
            SgmlLinkExtractor.__init__(self, allow=allow, deny=deny, allow_domains=allow_domains, deny_domains=deny_domains, restrict_xpaths=restrict_xpaths,
                     tags=tags, attrs=attrs, canonicalize=canonicalize, unique=unique, process_value=process_value,
                     deny_extensions=deny_extensions)
    
        def extract_links(self, response):
            base_url = None
            if self.restrict_xpaths:
                sel = Selector(response)
                base_url = get_base_url(response)
                body = u''.join(f
                                for x in self.restrict_xpaths
                                for f in sel.xpath(x).extract()
                                ).encode(response.encoding, errors='xmlcharrefreplace')
            else:
                body = response.body
    
            links = self._extract_links(body, response.url, response.encoding, base_url)
            links = self._process_links(links)
            links = links[0:self.max_pages]
            return links
    

    这个子类的代码完全基于类SgmlLinkExtractor的代码。我刚刚将变量 self.max_pages 添加到类构造函数中,并在 extract_links 方法的末尾剪切了链接列表。但是你可以用更聪明的方式来删减这个列表。

    【讨论】:

      【解决方案2】:

      我会创建每个类的变量,用stats = defaultdict(int) 初始化它并在parse_item 中增加self.stats[response.url](或者在你的情况下,键可能是像(website, depth) 这样的元组)。

      这就是我想象的方式——理论上应该可行。如果您需要示例,请告诉我。

      仅供参考,您可以在urlparse.urlparse 的帮助下提取基本网址并计算深度(请参阅docs)。

      【讨论】:

      • 另一个问题的最新答案指向 DEPTH_LIMIT 设置 - stackoverflow.com/a/18901802/263421
      • 那么self.stats[response.url]大于20之后怎么办?爬虫会停止爬取这个网站吗?
      • @alecxe 你能举个例子说明这应该如何工作吗?
      猜你喜欢
      • 1970-01-01
      • 2013-03-28
      • 2015-09-22
      • 2016-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多