python - 超过最大递归深度。多处理和 bs4

标签 python parsing beautifulsoup multiprocessing

我正在尝试让解析器使用 beautifulSoup 和多处理。我有一个错误:

RecursionError: maximum recursion depth exceeded

我的代码是:

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

最佳答案

当你使用multiprocessing时,你传递给worker的所有东西都必须是pickled .

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


这有几个不同的原因。其中一些是已修复的错误,因此您可以尝试确保您拥有最新的 bs4 版本,还有一些特定于不同的解析器或树构建器......但很有可能没有这样的东西会有帮助。

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

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

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

我认为后者就是这里发生的事情。如果我使用你的代码并尝试 pickle divList[0],它会失败。 (如果我提高递归限制并计算帧数,它需要 23080 的深度,这远远超过默认值 1000。)但是如果我采用完全相同的 div 并解析它单独,它成功没有问题。


因此,一种可能性是只执行 sys.setrecursionlimit(25000)。这将解决这个确切页面的问题,但稍微不同的页面可能需要更多。 (此外,将递归限制设置得那么高通常不是一个好主意——与其说是因为浪费了内存,不如说是因为这意味着实际的无限递归需要 25 倍的时间和 25 倍的资源浪费来检测。)


另一个技巧是编写“修剪树”的代码,在您 pickle 之前/之时从 div 中消除任何向上链接。这是一个很好的解决方案,除了它可能需要大量工作,并且需要深入了解 BeautifulSoup 的工作原理,我怀疑您是否愿意这样做。


最简单的解决方法有点笨拙,但是……您可以将 soup 转换为字符串,将其传递给 child ,然后让 child 重新解析它:

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 不完美,转换为字符串并重新解析它可能不是完美的往返。因此,我建议您先在没有多处理的情况下进行一些测试,以确保这不会影响结果。

关于python - 超过最大递归深度。多处理和 bs4,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52021254/

相关文章:

python - 修改带有换行符的 BeautifulSoup .string

python - 处理 __init__ 中的异常

python - 使用 grid_2d_graph 在 networkx 中绘制 MxM 节点的正方形网格时移除旋转效果

python - 识别标签结构不同的分支

string - 通过 Powershell 进行日志解析 - 打印第 n 个元素之后的所有数组元素

Erlang 和 Antlr

python - 使用 python urllib2 发送 POST 请求并获得响应

Python循环练习

匹配上标文本的Python正则表达式

html - 我可以使用 Selenium 2 获取 HTML 数据而无需打开新的浏览器窗口吗?