2017-03-02 87 views
0

我正在寫一個Scrapy刮板,它使用CrawlSpider抓取網站,瀏覽它們的內部鏈接,並且抓取任何外部鏈接的內容(與原始域不同的鏈接)。基於Scrapy CrawlSpider的start_urls的動態規則?

我設法做到這一點與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本身內以編程方式執行該操作的方法。

任何想法? 謝謝!

Simon。

回答

1

我發現一個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的功能。

  1. 我改變_rules成字典,其中鍵是不同的網站域和的值是該域的規則(使用rules_for_url)。 _rules的人口在_compile_rules

  2. 做到,那麼我做出_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中的每個網址,併爲該網址創建一組特定的規則。然後爲每個正在爬網的網站使用適當的規則。

希望這可以幫助任何在將來遇到這個問題的人。

Simon。

0

遍歷start_urls中的所有網站鏈接並填充allowed_domainsdeny_domains數組。然後定義規則。

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

不,外部鏈接的內容被刮掉。內部鏈接不會被抓取,只能被抓取才能找到網站上的所有外部鏈接。 – Simon

+0

@Simon那麼你需要在'parse_internal'中調試你的選擇器..它們可能不正確 – Umair

+0

它們不正確?我想你可能錯過了這個問題中的一段。我在代碼編寫工作的問題中寫道,我只是想找一種方法將它應用於多個start_urls。 – Simon