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

时间:2011-07-24 20:15:00

标签: python algorithm recursion web-crawler depth

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

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

你可以非常快地看到减数(少数首先是“好身份证”) -

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网址后,我只有14K的好结果,我知道我浪费时间并需要优化它,所以我运行一些统计数据并构建了一个功能,可以检查网址,同时增加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 是一个保存最后一个已知网址ID的变量,因此我们将运行直到numOfRuns为

isValid()是一个获取ID +其中一个副交易的函数,如果网址包含我需要的内容,则返回True,并将网址的汤对象保存到名为“ - ”的全局变量中。 curRes ',如果网址不包含我需要的数据,则返回False。

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

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

您可以看到代码的其余部分here

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

  

“有些错误:调用a时超出了最大递归深度   Python对象“

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

我真的很高兴看到我得到了相同的结果,但比我的旧脚本快了25x-40倍,HTTP请求更少,非常精确,我只错过了1个结果,获得了1000个好结果,这是由我,不可能朗读5M次,我的旧脚本运行了30个小时,当我的新脚本在5-10分钟内给我960~结果时得到了14-15K的结果。

我读过有关堆栈限制的内容,但是我必须有一个解决方案,我想用Python实现的算法(我不能回到我原来的“算法”,它永远不会端)。

谢谢!

5 个答案:

答案 0 :(得分:33)

Python没有很好的支持递归,因为它缺少TRE(Tail Recursion Elimination)。

这意味着每次调用递归函数都会创建一个函数调用堆栈,因为堆栈深度限制(默认为1000),您可以通过sys.getrecursionlimit签出(当然您可以更改)它使用sys.setrecursionlimit,但不推荐)你的程序最终会在达到此限制时崩溃。

由于其他答案已经为你提供了一个更好的方法来解决这个问题(通过简单的循环替换递归),如果你仍然想要使用递归,那么还有另一个解决方案是使用其中一个许多在python中实现TRE的方法,如one

NB:我的回答是为了让您更深入地了解导致错误的原因,而且我并不建议您使用TRE,因为在您的情况下,循环会更好,更容易阅读。

答案 1 :(得分:15)

您可以通过以下方式增加堆栈容量:

import sys
sys.setrecursionlimit(10000)

答案 2 :(得分:13)

这会将递归转换为循环:

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)

答案 3 :(得分:2)

而不是进行递归,代码checkNextID(ID + 18)和类似代码的部分可以替换为ID+=18,然后如果删除return 0的所有实例,那么它应该执行同样的事情,但作为一个简单的循环然后,您应该在最后添加return 0并使您的变量非全局。

答案 4 :(得分:2)

您可以增加递归深度和线程堆栈大小。

import sys, threading
sys.setrecursionlimit(10**7) # max depth of recursion
threading.stack_size(2**27)  # new thread will get stack of such size