2015-07-23 26 views
12

Mój pająk ma poważny wyciek pamięci. Po 15 minutach działa jego pamięć 5 gb i scrapy mówi (używając prefs()), że 900k żąda obiektów i to wszystko. Jaki może być powód tak dużej liczby obiektów życiowych? Żądanie wzrasta i robi się coraz mniej. Wszystkie inne obiekty są bliskie zeru.Przeciek pamięci pająka Scrapy

Mój pająk wygląda następująco:

class ExternalLinkSpider(CrawlSpider): 
    name = 'external_link_spider' 
    allowed_domains = [''] 
    start_urls = [''] 

    rules = (Rule(LxmlLinkExtractor(allow=()), callback='parse_obj', follow=True),) 

    def parse_obj(self, response): 
    if not isinstance(response, HtmlResponse): 
     return 
    for link in LxmlLinkExtractor(allow=(), deny=self.allowed_domains).extract_links(response): 
     if not link.nofollow: 
      yield LinkCrawlItem(domain=link.url) 

Tutaj wyjście preferencjach()

HtmlResponse      2 oldest: 0s ago 
ExternalLinkSpider     1 oldest: 3285s ago 
LinkCrawlItem      2 oldest: 0s ago 
Request      1663405 oldest: 3284s ago 

Pamięć 100k ociera stron może trafić znak 40GB na niektórych stronach (na przykład co to victorinox.com osiągnąć 35 GB pamięci na 100k stron zeskrobanych stron). Na innych jest znacznie mniej.

UPD.

Objgraph for oldest request after some time of run

enter image description here

Odpowiedz

7

Istnieje kilka możliwych problemów widzę od razu.

Przed rozpoczęciem jednak, chciałem wspomnieć, że prefs() nie pokazuje liczby oczekujących w kolejce, pokazuje liczbę obiektów Request(), które są żywe. Można odwoływać się do obiektu żądania i utrzymywać go przy życiu, nawet jeśli nie ma go już w kolejce do pobrania.

Naprawdę nie widzę niczego w podanym przez ciebie kodzie, który by to spowodował, ale powinieneś o tym pamiętać.

Od razu, nietoperza, chciałbym zapytać: czy używasz ciasteczek? Jeśli nie, witryny, które przekazują identyfikator sesji jako zmienną GET, wygenerują nowy identyfikator sesji dla każdej wizyty strony. Zasadniczo będziesz kontynuował kolejkowanie w górę tych samych stron w kółko. Na przykład, victorinox.com będzie miało coś w rodzaju "jsessionid = 18537CBA2F198E3C1A5C9EE17B6C63AD" w swoim ciągu URL, z identyfikatorem zmieniającym się przy każdym nowym ładowaniu strony.

Po drugie, możesz trafić w pułapkę pająka. To znaczy strona, która po prostu przeładowuje się, z nową nieskończoną ilością linków. Pomyśl o kalendarzu z linkiem do "następnego miesiąca" i "poprzedniego miesiąca". Nie widzę jednak bezpośrednio na victorinox.com.

Po trzecie, z podanego kodu pająk nie jest ograniczony do żadnej konkretnej domeny. Wyodrębni każdy znaleziony link na każdej stronie, uruchamiając na każdym z nich: parse_obj. Strona główna do victorinox.com na przykład ma link do http://www.youtube.com/victorinoxswissarmy. To z kolei zapełni twoje prośby ogromnymi linkami do YouTube.

Musisz jednak rozwiązać więcej problemów, aby dowiedzieć się, co dokładnie się dzieje.

Niektóre strategie może być użyty:

  1. Załóż nowy Downloader Middleware i dziennika wszystkich wniosków (do pliku lub bazy danych). Przejrzyj prośby o dziwne zachowanie.
  2. Ogranicz głębokość, aby zapobiec nieskończonemu opuszczaniu króliczej dziury.
  3. Ograniczenie domeny w celu sprawdzenia, czy nadal stanowi problem.

Jeśli uważasz, że masz uzasadnione prawo do generowania wielu żądań, a pamięcią jest problem, włącz trwałą kolejkę zadań i zamiast tego zapisuj żądania na dysku. Polecam jednak odrzucić to jako pierwszy krok, ponieważ jest bardziej prawdopodobne, że twój robot nie działa tak, jak chciałeś.

+0

Po trzecie - mój kod jest ograniczony do jednej domeny. Ale domena może być dowolna. Im ustawienie pozwala domenom dynamicznie, więc jestem chwytając tylko jedną domenę na raz. Jeśli chodzi o ciasteczka - dobrze. Stała kolejka - powiedziałem w grupie użytkowników scrapy, że są bardzo powolni z dużą liczbą żądań, więc nie jest to opcja :( – Aldarund

+0

OK, to nie było pokazane w twoim kodzie, dlatego wspominałem o tym! jest powolny i wydaje mi się, że jest on przeznaczony raczej do wstrzymywania/wznawiania kolejek, różnica prędkości naprawdę jest pamięcią w porównaniu z dyskiem w tej instancji – Rejected

+0

Ustawiłem nawet kolejkę FifoMemoryQueue, ale wciąż najstarszy obiekt żądania jest prawie tak stary jak pająk obiekt powinien być przetwarzany i zwalniany? – Aldarund