python - 超出最大递归深度。多处理和 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
解决方案
当你使用时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 不完美,则转换为字符串并重新解析它可能不是完美的往返。所以,我建议你先做一些没有多处理的测试,以确保这不会影响结果。
推荐阅读
- eclipse-plugin - 如何在 GefPalette 查看器中应用搜索过滤器?
- spring-boot - 如何使用 Spring Cloud 函数调用谷歌云函数以使用 Java 进行 Http 请求
- javascript - 无法使用 Discord.js 类别的属性
- php - 从 PHP 头文件快速下载文件
- java - 全局变量作为同一个 java 类中的方法参数:这是不好的编程习惯吗?
- asp.net - 创建了一个 microsoft graph api 请求,如何从中创建 Web 服务?
- powerbi - 总和日期差异的 DAX 行总计错误
- c - 求解按位与方程
- ios - react native ios app前台通知在react-native-push-notification pkg中有一些问题
- java - java - 如何在java流中使用order by进行多重分组和多重聚合?