2017-03-02 4 views
0

Ich schreibe einen Scrapy Scraper, der CrawlSpider verwendet, um Websites zu crawlen, über ihre internen Links zu gehen und den Inhalt von externen Links (Verknüpfungen mit einer anderen Domäne als der ursprünglichen Domäne) zu scrappen.Dynamische Regeln basierend auf start_urls für Scrapy CrawlSpider?

Ich schaffte das mit 2 Regeln, aber sie basieren auf der Domäne der Website, die gecrawlt wird. Wenn ich das auf mehreren Websites ausführen möchte, stoße ich auf ein Problem, weil ich nicht weiß, auf welcher "start_url" ich gerade bin, sodass ich die Regel nicht ändern kann.

Hier ist, was ich mit so weit kam, ist es für eine Website funktioniert und ich bin nicht sicher, wie es zu einer Liste von Websites gelten:

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... 

Dies ist wahrscheinlich nur um vorbei die getan werden kann, start_url als Argument beim Aufruf des Scraper, aber ich suche nach einer Möglichkeit, das programmatisch innerhalb des Scraper selbst zu tun.

Irgendwelche Ideen? Danke!

Simon.

Antwort

1

Ich habe in der akzeptierten Antwort präsentierte die zweite Option ein very similar question und verwendet fand eine Abhilfe für dieses Problem zu entwickeln, da ist es nicht out-of-the-box in scrapy unterstützt.

Ich habe eine Funktion erstellt, die eine URL als Eingabe erhält und schafft Regeln dafür:

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 

ich dann einige CrawlSpider Funktionen außer Kraft setzen.

  1. Ich änderte _rules in ein Wörterbuch, in dem die Schlüssel sind die verschiedenen Website-Domains und die Werte die Regeln für die Domäne sind (unter Verwendung von rules_for_url). Die Bevölkerung von _rules wird in _compile_rules getan

  2. ich dann die entsprechenden Änderungen in _requests_to_follow und _response_downloaded macht die neue Art und Weise der Verwendung von _rules zu unterstützen.

_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) 

die ursprünglichen Funktionen here See.

Jetzt wird die Spinne einfach über jede URL in start_urls gehen und eine Reihe von Regeln für diese URL erstellen. Verwenden Sie dann die entsprechenden Regeln für jede Website, die gecrawlt wird.

Hoffe das hilft jedem, der auf dieses Problem in der Zukunft stolpert.

Simon.

0

Iterieren Sie alle Website-Links in start_urls und füllen Sie die Felder allowed_domains und deny_domains. Und dann Regeln definieren.

start_urls = ["www.website1.com", "www.website2.com", "www.website3.com", "www.website4.com"] 

allow_domains = [] 
deny_domains = [] 

for link in start_urls 

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

    allow_domains.extend([domain]) 
    deny_domains.extend([domain]) 


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

Nein, die Inhalte externer Links werden gelöscht. Interne Links werden nicht gekratzt, sondern nur gecrawlt, um alle externen Links auf der Website zu finden. – Simon

+0

@Simon Dann müssen Sie Ihre Selektoren in 'parse_internal' debuggen .. sie könnten falsch sein – Umair

+0

Wie sind sie falsch? Ich denke, Sie haben vielleicht einen Absatz in der Frage verpasst.Ich habe in der Frage geschrieben, dass der Code funktioniert, ich suche nur nach einer Möglichkeit, dies auf mehrere start_urls anzuwenden. – Simon

Verwandte Themen