BeautifulSoup: 运行时错误: 递归深度超过最大限制

13

使用BeautifulSoup时,我无法避免Python RuntimeError的最大递归深度。

我正在尝试递归处理嵌套的代码段并提取内容。美化后的HTML如下所示(不要问为什么它看起来像这样:)):

<div><code><code><code><code>Code in here</code></code></code></code></div>

我要把我的soup对象传递给的函数是:

def _strip_descendent_code(self, soup):
    sys.setrecursionlimit(2000)
    # soup = BeautifulSoup(html, 'lxml')
    for code in soup.findAll('code'):
        s = ""
        for c in code.descendents:
            if not isinstance(c, NavigableString):
                if c.name != code.name:
                    continue
                elif c.name == code.name:
                    if isinstance(c, NavigableString):
                        s += str(c)
                    else:
                        continue
        code.append(s)
    return str(soup)

您可以看到我正在尝试增加默认递归限制,但这不是一个解决方案。我已经增加到计算机内存限制的点,但上面的函数从来没有起作用。

任何帮助让它起作用并指出错误/问题将不胜感激。

堆栈跟踪重复显示如下内容:

  File "/Users/almccann/.virtualenvs/evernoteghost/lib/python3.4/site-packages/bs4/element.py", line 1234, in find
    l = self.find_all(name, attrs, recursive, text, 1, **kwargs)
  File "/Users/almccann/.virtualenvs/evernoteghost/lib/python3.4/site-packages/bs4/element.py", line 1255, in find_all
    return self._find_all(name, attrs, text, limit, generator, **kwargs)
  File "/Users/almccann/.virtualenvs/evernoteghost/lib/python3.4/site-packages/bs4/element.py", line 529, in _find_all
    i = next(generator)
  File "/Users/almccann/.virtualenvs/evernoteghost/lib/python3.4/site-packages/bs4/element.py", line 1269, in descendants
    stopNode = self._last_descendant().next_element
  File "/Users/almccann/.virtualenvs/evernoteghost/lib/python3.4/site-packages/bs4/element.py", line 284, in _last_descendant
    if is_initialized and self.next_sibling:
  File "/Users/almccann/.virtualenvs/evernoteghost/lib/python3.4/site-packages/bs4/element.py", line 997, in __getattr__
    return self.find(tag)
  File "/Users/almccann/.virtualenvs/evernoteghost/lib/python3.4/site-packages/bs4/element.py", line 1234, in find
    l = self.find_all(name, attrs, recursive, text, 1, **kwargs)
  File "/Users/almccann/.virtualenvs/evernoteghost/lib/python3.4/site-packages/bs4/element.py", line 1255, in find_all
    return self._find_all(name, attrs, text, limit, generator, **kwargs)
  File "/Users/almccann/.virtualenvs/evernoteghost/lib/python3.4/site-packages/bs4/element.py", line 529, in _find_all
    i = next(generator)
  File "/Users/almccann/.virtualenvs/evernoteghost/lib/python3.4/site-packages/bs4/element.py", line 1269, in descendants
    stopNode = self._last_descendant().next_element
  File "/Users/almccann/.virtualenvs/evernoteghost/lib/python3.4/site-packages/bs4/element.py", line 284, in _last_descendant
    if is_initialized and self.next_sibling:
  File "/Users/almccann/.virtualenvs/evernoteghost/lib/python3.4/site-packages/bs4/element.py", line 997, in __getattr__
    return self.find(tag)
  File "/Users/almccann/.virtualenvs/evernoteghost/lib/python3.4/site-packages/bs4/element.py", line 1234, in find
    l = self.find_all(name, attrs, recursive, text, 1, **kwargs)
  File "/Users/almccann/.virtualenvs/evernoteghost/lib/python3.4/site-packages/bs4/element.py", line 1255, in find_all
    return self._find_all(name, attrs, text, limit, generator, **kwargs)
  File "/Users/almccann/.virtualenvs/evernoteghost/lib/python3.4/site-packages/bs4/element.py", line 512, in _find_all
    strainer = SoupStrainer(name, attrs, text, **kwargs)
  File "/Users/almccann/.virtualenvs/evernoteghost/lib/python3.4/site-packages/bs4/element.py", line 1548, in __init__
    self.text = self._normalize_search_value(text)
  File "/Users/almccann/.virtualenvs/evernoteghost/lib/python3.4/site-packages/bs4/element.py", line 1553, in _normalize_search_value
    if (isinstance(value, str) or isinstance(value, collections.Callable) or hasattr(value, 'match')
RuntimeError: maximum recursion depth exceeded while calling a Python object

请包含您所看到的精确错误消息和堆栈跟踪。 - dimo414
如果那个 bug 是问题的原因,解决方案应该很简单,只需要更新你的 BeautifulSoup 安装程序;它已经关闭了三年。 - dimo414
我的意思是同样的问题,但我使用的是最新的BeautifulSoup版本4.4.0。 - almccann
不想删除标签而选择减少它们的数量是否可行? - WGS
分解也可以,但我认为错误更深。使用以下代码时我会得到相同的错误: for code in soup.findAll('code'): for c in code.children: logging.debug(unicode(c)) - almccann
显示剩余5条评论
3个回答

15

我曾经遇到过这个问题,并浏览了很多网页。我总结了两种解决方法。

然而,我认为我们应该知道为什么会出现这种情况。Python限制递归的次数(默认次数为1000)。可以使用print sys.getrecursionlimit()查看此数字。我猜想BeautifulSoup使用递归来查找子元素。当递归超过1000次时,将出现RuntimeError: maximum recursion depth exceeded

第一种方法:使用sys.setrecursionlimit()设置递归次数的限制。您显然可以将其设置为1000000,但这可能会导致segmentation fault

第二种方法:使用try-except。如果出现maximum recursion depth exceeded,我们的算法可能存在问题。一般来说,我们可以使用循环代替递归。在您的问题中,我们可以提前使用replace()或正则表达式处理HTML。

最后,我给出一个例子。

from bs4 import BeautifulSoup
import sys   
#sys.setrecursionlimit(10000)

try:
    doc = ''.join(['<br>' for x in range(1000)])
    soup = BeautifulSoup(doc, 'html.parser')
    a = soup.find('br')
    for i in a:
        print i
except:
    print 'failed'

如果去掉#,它可以打印出doc

希望能对你有所帮助。


4
为什么OP的样例代码需要1000次递归? - Geoffrey Negiar

5

我不确定为什么这样可以解决问题(我没有检查源代码),但是加上 .text.get_text() 似乎可以让我避开错误。

例如,将

lambda x: BeautifulSoup(x, 'html.parser')

改为

lambda x: BeautifulSoup(x, 'html.parser').get_text() 似乎可以在不抛出递归深度错误的情况下正常工作。


.get_text() 添加到 BeautifulSoup(x, 'html.parser') 会将文档从 <class 'bs4.BeautifulSoup'> 转换为 <class 'str'>。 将整个文档转换为字符串似乎不是一个好的解决方案。 - aubaub

0
问题很老,但我最近一直在处理这个错误。 我注意到我没有使用最新版本的BeautifulSoup,所以我将其升级到4.12.2,问题得到解决。
pip install beautifulsoup4 --upgrade

我认为这是一个 bug,因为如果 HTML 具有多个嵌套节点,它失败没有任何意义。它是一个专为处理 HTML 设计的库,应该能够接受任何 HTML,并且不应该引发那样的错误。我之前使用的版本是 4.11.2,但我将其升级到 4.12.2,问题就解决了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接