Python:调用 Python 对象时超出最大递归深度

新手上路,请多包涵

我构建了一个爬虫,它必须在大约 500 万个页面上运行(通过增加 url ID),然后解析包含我需要的信息的页面。

在使用在 url (200K) 上运行的算法并保存好的和坏的结果后,我发现我浪费了很多时间。我可以看到有一些返回的减数,我可以用它们来检查下一个有效的 url。

您可以很快看到减数(少数第一个“好 ID”中的一个小例子)-

 510000011 # +8
510000029 # +18
510000037 # +8
510000045 # +8
510000052 # +7
510000060 # +8
510000078 # +18
510000086 # +8
510000094 # +8
510000102 # +8
510000110 # etc'
510000128
510000136
510000144
510000151
510000169
510000177
510000185
510000193
510000201

在爬取了大约 200K 个 url 后只给了我 14K 个好的结果我知道我在浪费时间并且需要优化它,所以我运行了一些统计数据并构建了一个函数来检查 urls 同时将 id 增加 8\18\17\ 8(顶部返回减数)等’。

这是功能 -

 def checkNextID(ID):
    global numOfRuns, curRes, lastResult
    while ID < lastResult:
        try:
            numOfRuns += 1
            if numOfRuns % 10 == 0:
                time.sleep(3) # sleep every 10 iterations
            if isValid(ID + 8):
                parseHTML(curRes)
                checkNextID(ID + 8)
                return 0
            if isValid(ID + 18):
                parseHTML(curRes)
                checkNextID(ID + 18)
                return 0
            if isValid(ID + 7):
                parseHTML(curRes)
                checkNextID(ID + 7)
                return 0
            if isValid(ID + 17):
                parseHTML(curRes)
                checkNextID(ID + 17)
                return 0
            if isValid(ID+6):
                parseHTML(curRes)
                checkNextID(ID + 6)
                return 0
            if isValid(ID + 16):
                parseHTML(curRes)
                checkNextID(ID + 16)
                return 0
            else:
                checkNextID(ID + 1)
                return 0
        except Exception, e:
            print "somethin went wrong: " + str(e)

基本上做的是 -checkNextID(ID) 获取我知道的第一个包含数据减 8 的 ID,因此第一次迭代将匹配第一个“if isValid”子句(isValid(ID + 8) 将返回 True)。

lastResult 是一个保存最后已知 url id 的变量,所以我们将运行直到 numOfRuns 是

isValid() 是一个获取 ID + 减数之一的函数,如果 url 包含我需要的内容则返回 True 并将 url 的 soup 对象保存到名为“ curRes ”的全局变量中,如果 url 不包含则返回 False ‘包含我需要的数据。

parseHTML 是一个获取 soup 对象 (curRes) 的函数,解析我需要的数据,然后将数据保存到 csv,然后返回 True。

如果 isValid() 返回 True,我们将调用 parseHTML() 然后尝试检查下一个 ID+减数(通过调用 checkNextID(ID + 减数),如果它们都不会返回我正在寻找的内容,我将将它增加 1 并再次检查,直到我找到下一个有效的 url。

您可以在 此处 查看其余代码

运行代码后,我得到了大约 950~ 好的结果,突然出现异常 -

“出了点问题:调用 Python 对象时超出了最大递归深度”

我可以在 WireShark 上看到 scipt 停留在 id - 510009541(我用 510000003 开始我的脚本),脚本尝试获取具有该 ID 的 url 几次,然后我注意到错误并停止了它。

我真的很兴奋看到我得到了相同的结果,但比我的旧脚本快 25 到 40 倍,HTTP 请求更少,非常精确,我发现 1000 个好结果只错过了 1 个结果,这是我发现的,它是不可能朗姆酒 500 万次,我让我的旧脚本运行了 30 小时并得到了 14-15K 的结果,而我的新脚本在 5-10 分钟内给了我 960~ 个结果。

我读到了有关堆栈限制的信息,但必须有一个解决方案来解决我试图在 Python 中实现的算法(我不能回到我的旧 “算法” ,它永远不会结束)。

谢谢!

原文由 YSY 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 939
2 个回答

这将递归变成一个循环:

 def checkNextID(ID):
    global numOfRuns, curRes, lastResult
    while ID < lastResult:
        try:
            numOfRuns += 1
            if numOfRuns % 10 == 0:
                time.sleep(3) # sleep every 10 iterations
            if isValid(ID + 8):
                parseHTML(curRes)
                ID = ID + 8
            elif isValid(ID + 18):
                parseHTML(curRes)
                ID = ID + 18
            elif isValid(ID + 7):
                parseHTML(curRes)
                ID = ID + 7
            elif isValid(ID + 17):
                parseHTML(curRes)
                ID = ID + 17
            elif isValid(ID+6):
                parseHTML(curRes)
                ID = ID + 6
            elif isValid(ID + 16):
                parseHTML(curRes)
                ID = ID + 16
            else:
                ID = ID + 1
        except Exception, e:
            print "somethin went wrong: " + str(e)

原文由 Dan D. 发布,翻译遵循 CC BY-SA 3.0 许可协议

Python 对递归没有很好的支持,因为它缺少 TRE( 尾递归消除)。

这意味着对递归函数的每次调用都会创建一个函数调用堆栈,并且由于堆栈深度有限制(默认情况下为 1000),您可以通过 sys.getrecursionlimit 检查(当然您可以更改它使用 sys.setrecursionlimit 但不推荐)你的程序在达到这个限制时会崩溃。

由于其他答案已经为您提供了一种更好的方法来解决您的问题(即用简单循环替换递归),如果您仍然想使用递归,还有另一种解决方案,即使用许多方法之一像这样在 python 实现 TRE。

注意: 我的回答是为了让您更深入地了解为什么会出现错误,并且我不建议您使用我已经解释过的 TRE,因为在您的情况下,循环会更好且更容易阅读。

原文由 mouad 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题