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

YSY*_*YSY 34 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
Run Code Online (Sandbox Code Playgroud)

在抓取大约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)
Run Code Online (Sandbox Code Playgroud)

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

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

isValid()是一个获取ID +其中一个副转义的函数,如果url包含我需要的内容,则返回True,并将url的汤对象保存到名为' curRes ' 的全局变量中,如果url没有,则返回False不包含我需要的数据.

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

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

你可以在这里看到其余的代码

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

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

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

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

我读到了堆栈限制,但是我必须有一个解决方案,我试图在Python中实现这个算法(我不能回到我原来的"算法",它永远不会结束).

谢谢!

mou*_*uad 36

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

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

由于其他答案已经为您提供了一个更好的方法来解决这个问题(通过简单的循环替换递归),如果您仍然想要使用递归,那么可以使用其中一种方法在这样的蟒蛇实施TRE 一个.

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


小智 23

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

import sys
sys.setrecursionlimit(10000)
Run Code Online (Sandbox Code Playgroud)

  • 我有一个相当不错的有斑点的 27 iInch iMac,这导致它因“总线错误:10”和 Python 退出而窒息 (2认同)

Dan*_* D. 14

这会将递归转换为循环:

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)
Run Code Online (Sandbox Code Playgroud)


Ler*_*ang 8

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

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