YardalGedal
@YardalGedal
yeah boy

Почему может расти потребление памяти Scrapy?

prefs() говорит, что больше всего памяти занимают объекты Selector() и Response().
Много - 10-12 гигабайт за несколько часов работы.
Работаю со скрапи так (базовый спайдер):
class BaseAcrossSearchSpider(BaseAcrossSearchMixin, BaseSpider):
    ITEMS_OBJECTS: str = ''
    ITEM_URL_OBJECT: str = ''
    NEXT_BUTTON_OBJECT: str = ''
    CONTINUE_IF_NEXT_BUTTON_OBJECT_IS: bool = True

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._last_page: int = self.START_PAGE

    def start_requests(self) -> None:
        yield Request(self._search_url(self.START_PAGE), callback=self.parse)

    def parse(self, response: Response) -> Any:
        for short_item in response.css(self.ITEMS_OBJECTS):
            yield Request(
                self._page_url(short_item.css(self.ITEM_URL_OBJECT).extract_first()),
                self._process_item(short_item)
            )

        if self.CONTINUE_IF_NEXT_BUTTON_OBJECT_IS is bool(response.css(self.NEXT_BUTTON_OBJECT)):
            yield Request(self._search_url(self._last_page + 1), self.parse)

    def _process_item(self, short_item: Selector) -> Callable:
        def wrapper(response: Response):
            """
            downloader мидлвейр проверяет, ходил ли спайдер уже по этому адресу (redis).
            Если ходил, возвращает пустой Response()
            """
            if response.body:
                return self._parse(self.FULL_MODEL,
                                   self.full_loader,
                                   response=response,
                                   url=response.url,
                                   utc_created_at=datetime.utcnow(),
                                   utc_actually_at=datetime.utcnow())
            else:
                return self._parse(self.SHORT_MODEL,
                                   self.short_loader,
                                   selector=short_item,
                                   url=response.url,
                                   utc_actually_at=datetime.utcnow())
        return wrapper

    def _parse(self,
               model: dict,
               loader,
               selector: Selector = None,
               response: Response = None,
               **kwargs):

        if not selector and response:
            selector = response.selector

        loader = loader(item=self.item(), selector=selector)

        for element, handler in model.items():
            if callable(handler):
                deque(map(loader.add_value, element, handler(selector)))
            else:
                loader.add_css(element, handler)

        for k, v in kwargs.items():
            loader.add_value(k, v)

        return loader.load_item()

    def _search_url(self, page: Optional[int]) -> str:
        ...

(дочерний спайдер)
class ChildaSpider(ChildaMixin, BaseAcrossSearchSpider):
    SHORT_MODEL = {
        ('price_base', 'price_total'): _get_prices,
    }

    FULL_MODEL = {
        'price_base': 'p.basePrice__price span::text',
        'price_total': 'p.totalPrice__price span::text',
        ...
        ('body_type', 'color', 'vin', 'engine_size', 'engine_type', 'drive_type',
         'steering_location', 'transmission', 'passengers_count', 'doors_count'):
            _get_elements_from_table(range(1, 11), 2)
    }

    ITEMS_OBJECTS = 'div.casetMain'
    ITEM_URL_OBJECT = 'a::attr("href")'
    NEXT_BUTTON_OBJECT = 'button.btnFunc pager__btn__next[disabled]'
    CONTINUE_IF_NEXT_BUTTON_OBJECT_IS = False


Где-то я что-то делаю не так?
  • Вопрос задан
  • 203 просмотра
Решения вопроса 1
YardalGedal
@YardalGedal Автор вопроса
yeah boy
В общем, на сколько я понял, память у меня и не текла.

Проблема в алгоритме, который в методе parse() и настройках, которые я использовал для парсинга. Значения конкуретных запросов и тредов у меня слишком высоки, а значение DEPTH_PRIORITY было установлено по-умолчанию (0).

Таким образом получалось, что страницы поиска парсились быстрее, чем генерировались айтемы на основании записей с них, создавалась длинная очередь и память переполнялось. Помогла установка значения DEPTH_PRIORITY = 1.
Однако скорость парсинга, к сожалению, снизилась.
Старт двух спайдеров в двух разных процессах немного улучшил ситуацию.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
07 мая 2024, в 14:11
4000 руб./за проект
07 мая 2024, в 14:10
10000 руб./за проект
07 мая 2024, в 14:04
1300 руб./за проект