编程语言
首页 > 编程语言> > python – 基于start_urls的Scrapy CrawlSpider动态规则?

python – 基于start_urls的Scrapy CrawlSpider动态规则?

作者:互联网

我正在编写一个Scrapy scraper,它使用CrawlSpider来抓取网站,浏览其内部链接,并抓取任何外部链接的内容(链接与不同于原始域的域).

我设法使用2个规则执行此操作,但它们基于正在爬网的网站的域.如果我想在多个网站上运行这个问题,我会遇到一个问题,因为我不知道我目前正在使用哪个“start_url”,因此我无法正确更改规则.

这是我到目前为止提出的,它适用于一个网站,我不知道如何将其应用于网站列表:

class HomepagesSpider(CrawlSpider):
    name = 'homepages'

    homepage = 'http://www.somesite.com'

    start_urls = [homepage]

    # strip http and www
    domain = homepage.replace('http://', '').replace('https://', '').replace('www.', '')
    domain = domain[:-1] if domain[-1] == '/' else domain

    rules = (
        Rule(LinkExtractor(allow_domains=(domain), deny_domains=()), callback='parse_internal', follow=True),
        Rule(LinkExtractor(allow_domains=(), deny_domains=(domain)), callback='parse_external', follow=False),
    )

    def parse_internal(self, response):

        # log internal page...

    def parse_external(self, response):

        # parse external page...

这可以通过在调用scraper时将start_url作为参数传递来完成,但我正在寻找一种在scraper本身内以编程方式执行此操作的方法.

有任何想法吗?
谢谢!

西蒙.

解决方法:

我找到了一个very similar question,并使用了接受的答案中提出的第二个选项来为这个问题开发一个解决方法,因为它在scrapy中不支持开箱即用.

我创建了一个函数,它将url作为输入并为其创建规则:

def rules_for_url(self, url):

    domain = Tools.get_domain(url)

    rules = (
        Rule(LinkExtractor(allow_domains=(domain), deny_domains=()), callback='parse_internal', follow=True),
        Rule(LinkExtractor(allow_domains=(), deny_domains=(domain)), callback='parse_external', follow=False),
    )

    return rules

然后我重写了一些CrawlSpider的函数.

>我将_rules更改为字典,其中键是不同的网站域,值是该域的规则(使用rules_for_url). _rules的数量在_compile_rules中完成
>然后我在_requests_to_follow和_response_downloaded中进行适当的更改以支持使用_rules的新方法.

_rules = {}

def _requests_to_follow(self, response):
    if not isinstance(response, HtmlResponse):
        return
    seen = set()

    domain = Tools.get_domain(response.url)
    for n, rule in enumerate(self._rules[domain]):
        links = [lnk for lnk in rule.link_extractor.extract_links(response) 
                 if lnk not in seen]
        if links and rule.process_links:
            links = rule.process_links(links)
        for link in links:
            seen.add(link)
            r = self._build_request(domain + ';' + str(n), link)
            yield rule.process_request(r)

def _response_downloaded(self, response):

    meta_rule = response.meta['rule'].split(';')
    domain = meta_rule[0]
    rule_n = int(meta_rule[1])

    rule = self._rules[domain][rule_n]
    return self._parse_response(response, rule.callback, rule.cb_kwargs, rule.follow)

def _compile_rules(self):
    def get_method(method):
        if callable(method):
            return method
        elif isinstance(method, six.string_types):
            return getattr(self, method, None)

    for url in self.start_urls:
        url_rules = self.rules_for_url(url)
        domain = Tools.get_domain(url)
        self._rules[domain] = [copy.copy(r) for r in url_rules]
        for rule in self._rules[domain]:
            rule.callback = get_method(rule.callback)
            rule.process_links = get_method(rule.process_links)
            rule.process_request = get_method(rule.process_request)

查看原始功能here.

现在蜘蛛将简单地遍历start_urls中的每个url并创建一组特定于该url的规则.然后对每个被抓取的网站使用适当的规则.

希望这有助于将来偶然发现这个问题的任何人.

西蒙.

标签:scrapy-spider,python,web-scraping,scrapy,web-crawler
来源: https://codeday.me/bug/20191007/1867848.html