Превышена максимальная глубина рекурсии. Мультипроцессинг и BS4 - PullRequest
0 голосов
/ 26 августа 2018

Я пытаюсь заставить парсер использовать BeautifulSoup и многопроцессорность. У меня ошибка:

RecursionError: превышена максимальная глубина рекурсии

Мой код:

import bs4, requests, time
from multiprocessing.pool import Pool

html = requests.get('https://www.avito.ru/moskva/avtomobili/bmw/x6?sgtd=5&radius=0')
soup = bs4.BeautifulSoup(html.text, "html.parser")

divList = soup.find_all("div", {'class': 'item_table-header'})


def new_check():
    with Pool() as pool:
        pool.map(get_info, divList)

def get_info(each):
   pass

if __name__ == '__main__':
    new_check()

Почему я получаю эту ошибку и как ее исправить?

UPDATE: Весь текст ошибки

Traceback (most recent call last):
  File "C:/Users/eugen/PycharmProjects/avito/main.py", line 73, in <module> new_check()
  File "C:/Users/eugen/PycharmProjects/avito/main.py", line 67, in new_check
    pool.map(get_info, divList)
  File "C:\Users\eugen\AppData\Local\Programs\Python\Python36\lib\multiprocessing\pool.py", line 266, in map
    return self._map_async(func, iterable, mapstar, chunksize).get()
  File "C:\Users\eugen\AppData\Local\Programs\Python\Python36\lib\multiprocessing\pool.py", line 644, in get
    raise self._value
  File "C:\Users\eugen\AppData\Local\Programs\Python\Python36\lib\multiprocessing\pool.py", line 424, in _handle_tasks
    put(task)
  File "C:\Users\eugen\AppData\Local\Programs\Python\Python36\lib\multiprocessing\connection.py", line 206, in send
    self._send_bytes(_ForkingPickler.dumps(obj))
  File "C:\Users\eugen\AppData\Local\Programs\Python\Python36\lib\multiprocessing\reduction.py", line 51, in dumps
    cls(buf, protocol).dump(obj)
RecursionError: maximum recursion depth exceeded

1 Ответ

0 голосов
/ 26 августа 2018

Когда вы используете multiprocessing, все, что вы передаете работнику, должно быть засолено .

К сожалению, многие BeautifulSoup деревья не могут быть засолены.


Для этого есть несколько причин.Некоторые из них являются ошибками, которые с тех пор были исправлены, поэтому вы могли бы попробовать убедиться, что у вас установлена ​​последняя версия bs4, а некоторые относятся к разным парсерам или построителям деревьев ... но есть хороший шанс, что ничего подобного не произойдетhelp.

Но фундаментальная проблема заключается в том, что многие элементы в дереве содержат ссылки на остальную часть дерева.

Иногда это приводит к фактическому бесконечному циклу, поскольку круговые ссылки слишкомкосвенный для его обнаружения круговой ссылки.Но обычно это ошибка, которая исправляется.

Но, что еще более важно, даже когда цикл не является бесконечным , он все равно может тянуть более 1000 элементов со всех остальныхдерева, и этого уже достаточно, чтобы вызвать RecursionError.

И я думаю, что последнее происходит здесь.Если я возьму твой код и попытаюсь засечь divList[0], произойдет сбой.(Если я подниму предел рекурсии вверх и посчитаю кадры, ему нужна глубина 23080, что намного превышает значение по умолчанию, равное 1000.) Но если я возьму тот же самый div и проанализирую его отдельно, это успешнобез проблем.


Итак, одна возможность - просто сделать sys.setrecursionlimit(25000).Это решит проблему для этой конкретной страницы, но немного другой странице может потребоваться даже больше, чем это.(Кроме того, обычно не очень хорошая идея устанавливать такой высокий предел рекурсии - не столько из-за потраченной памяти, но потому, что это означает, что для реальной бесконечной рекурсии требуется в 25 раз больше, а в 25 раз больше потраченных ресурсов для обнаружения.)


Другой трюк заключается в написании кода, который «обрезает дерево», удаляя любые восходящие ссылки из div перед / при его выделении.Это отличное решение, за исключением того, что это может быть много работы, и требует погружения во внутренности того, как работает BeautifulSoup, что я сомневаюсь, что вы хотите сделать.


Самый простой обходной путь немногонеуклюже, но ... вы можете преобразовать суп в строку, передать его ребенку и попросить ребенка проанализировать его:

def new_check():
    divTexts = [str(div) for div in divList]
    with Pool() as pool:
        pool.map(get_info, divTexts)

def get_info(each):
    div = BeautifulSoup(each, 'html.parser')

if __name__ == '__main__':
    new_check()

Стоимость выполнения этого, вероятно, не будет иметь значения;большее беспокойство вызывает то, что если бы у вас был несовершенный HTML, преобразование в строку и повторный разбор не могли бы быть идеальным циклом.Поэтому я бы посоветовал вам сначала выполнить несколько тестов без многопроцессорной обработки, чтобы убедиться, что это не повлияет на результаты.

...