2013-05-21 19 views
5

Próbuję zbudować pająka, który mógłby skutecznie skasować informacje tekstowe z wielu stron internetowych. Ponieważ jestem użytkownikiem Pythona, zostałem skierowany do Scrapy. Jednak, aby uniknąć oszpecania ogromnych stron internetowych, chcę ograniczyć pająka do zeskrobywania nie więcej niż 20 stron o określonej "głębokości" na stronie internetowej. Oto mój pająk:Jak ograniczyć liczbę stron śledzonych w witrynie w Pythonie Scrapy

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

plik urls_file jest ścieżką do pliku tekstowego z adresami URL. Ustawiłem również maksymalną głębokość w pliku ustawień. Oto mój problem: jeśli ustawię wyjątek CLOSESPIDER_PAGECOUNT, zamyka on pająki, gdy liczba zeskrobanych stron (niezależnie od witryny) osiągnie wartość wyjątku, całkowita. Muszę jednak przestać skrobać, gdy skrobię, powiedz 20 stron z każdego adresu URL. Próbowałem również zachować liczenie ze zmienną taką jak self.parsed_number + = 1, ale to też nie działało - wygląda na to, że scrapy nie są adresowane przez URL, ale je mieszają. Każda rada jest doceniana!

Odpowiedz

2

bym zrobić zmienną per-klasy zainicjować go z stats = defaultdict(int) i przyrost self.stats[response.url] (lub może być kluczem może być krotka jak (website, depth) w danym przypadku) w parse_item.

Tak to sobie wyobrażam - powinien działać w teorii. Daj mi znać, jeśli potrzebujesz przykładu.

FYI, możesz wyodrębnić podstawowy adres URL i obliczyć głębokość przy pomocy urlparse.urlparse (patrz docs).

+1

Nowsza odpowiedź na inne pytanie wskazuje ustawienie DEPTH_LIMIT - http://stackoverflow.com/a/18901802/263421 –

+0

@dwightgunning yup, dziękuję za notatkę. – alecxe

+0

to co zrobić, gdy 'self.stats [response.url]' jest większe niż 20? robot indeksujący przestanie indeksować tę witrynę? – ningyuwhut

2

Aby to zrobić, możesz utworzyć własną klasę ekstraktora linków na podstawie SgmlLinkExtractor. Powinien wyglądać mniej więcej tak:

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 

Kod tej podklasy całkowicie oparty na kodzie klasy SgmlLinkExtractor. Właśnie dodałem zmienną self.max_pages do konstruktora klasy i linii, która wytnie listę linków na końcu metody extract_links. Ale możesz wyciąć tę listę w bardziej inteligentny sposób.

Powiązane problemy