如何在Python Scrapy中限制每个站点的后续页面数

gpa*_*rov 6 python web-crawler scrapy

我正在尝试构建一个可以有效地从许多网站上抓取文本信息的蜘蛛.由于我是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')
Run Code Online (Sandbox Code Playgroud)

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

Rom*_*rov 5

为此,您可以基于 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
Run Code Online (Sandbox Code Playgroud)

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


ale*_*cxe 4

我会创建每个类stats = defaultdict(int)变量self.stats[response.url](website, depth)parse_item.

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

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

  • 另一个问题的最新答案指向 DEPTH_LIMIT 设置 - http://stackoverflow.com/a/18901802/263421 (2认同)