首页 > 解决方案 > 超出最大递归深度。多处理和 bs4

问题描述

我正在尝试让解析器使用 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()

为什么我会收到此错误以及如何解决?

更新: 所有错误文本都是

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

标签: pythonparsingbeautifulsoupmultiprocessing

解决方案


当你使用时multiprocessing,你传递给工人的所有东西都必须腌制

不幸的是,许多BeautifulSoup树不能腌制。


这有几个不同的原因。其中一些是已经修复的错误,因此您可以尝试确保您拥有最新的 bs4 版本,而另一些则特定于不同的解析器或树构建器……但很有可能这样的事情无济于事。

但根本问题是树中的许多元素都包含对树其余部分的引用。

有时,这会导致实际的无限循环,因为循环引用对于其循环引用检测来说过于间接。但这通常是一个可以修复的错误。

但是,更重要的是,即使循环不是无限的,它仍然可以从树的其余部分拖入 1000 多个元素,这已经足以导致RecursionError.

我认为后者就是这里发生的事情。如果我拿走你的代码并尝试 pickle 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 不完美,则转换为字符串并重新解析它可能不是完美的往返。所以,我建议你先做一些没有多处理的测试,以确保这不会影响结果。


推荐阅读