Python:Pythonオブジェクトを呼び出す際に最大再帰深さを超えました
-
25-10-2019 - |
質問
約5mページで実行する必要があるクローラーを構築しました(URL IDを増やすことで)。次に、「必要な情報を含むページを解析します。
URL(200k)で実行され、良い結果と悪い結果を保存したアルゴリズムを使用した後、私は多くの時間を無駄にしていることがわかりました。次の有効なURLを確認するために使用できるサブトラヘンドを返すいくつかのAAがあることがわかりました。
サブトラヘンドは非常に高速に見えます(最初の「良いID」の少数の小さなex ') -
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の結果が得られただけで、時間を無駄にして最適化する必要があることがわかったので、統計を実行し、8 18 17 でIDを増やしながらURLをチェックする関数を構築しました。 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を取得しているため、最初の反復は最初の「isvalid」節(isvalid(id + 8)がtrueを返します)と一致します。
ラストラスルト 最後の既知のURL IDを保存する変数なので、numofrunsが
isvalid() URLに必要なものが含まれており、URLのスープオブジェクトが名前が付けられたグローバルなVaribaleに保存されている場合、サブトラヘンドの1つを取得し、trueを返す関数です。カール'、URLに必要なデータが含まれていない場合、falseを返します。
parsehtml は、スープオブジェクト(カーレス)を取得し、必要なデータを解析し、データをCSVに保存してからtrueを返す関数です。
isvalid()がtrueを返す場合、parsehtml()を呼び出してから、次のid +を確認しようとします(checknextid(id + subrahends)を呼び出します。次の有効なURLを見つけるまで、1でそれを増やし、もう一度確認します。
コードの残りの部分を見ることができます ここ
コードを実行した後、私は約950〜良い結果を得ましたが、突然例外が提起されました -
「Somethinがうまくいかなかった:Pythonオブジェクトを呼び出している間、最大再帰深さを超えた」
Wiresharkで、SciptがID -510009541(510000003でスクリプトを開始した)に貼り付けられていることがわかりました。スクリプトは、エラーに気付いて停止する前に、そのIDでURLをそのIDで取得しようとしました。
同じ結果が得られましたが、HTTPリクエストが少なく、古いスクリプトよりも25x〜40倍速くなったことを見るのは本当にエキサイティングでした。ラムを5mにすることは不可能で、古いスクリプトが30時間実行され、新しいスクリプトが960〜5〜10分で結果を与えたときに14〜15Kの結果が得られました。
スタックの制限について読みましたが、Pythonで実装しようとしているアルゴリズムの解決策があるに違いありません(古いものに戻ることはできません "アルゴリズム", 、それは決して終わりません)。
ありがとう!
解決
これにより、再帰がループになります。
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)
他のヒント
Pythonは、TREが不足しているため、再帰を大きくサポートしていません(尾の再帰除去).
これは、再帰関数への各呼び出しが関数呼び出しスタックを作成することを意味し、スタック深度の制限があるため(デフォルトでは1000) sys.getrecursionlimit
(もちろん、使用して変更できます sys.setRecursionlimit しかし、それは推奨されません)あなたのプログラムは、この制限に達したときにクラッシュすることになります。
他の答えがすでにあなたのケースでこれを解決する方法についてはるかに良い方法を与えているので(これは単純なループで再帰を置き換えることです)このようなPythonでTREを実装します 1.
NB: 私の答えは、あなたがエラーを得る理由についてもっと洞察を与えることを意図しています。あなたの場合、あなたの場合はループがはるかに良く、読みやすくなるので、私がすでに説明したようにTREを使用することをお勧めしません。
以下でスタックの容量を増やすことができます。
import sys
sys.setrecursionlimit(10000)
再帰を行う代わりに、コードの部分は checkNextID(ID + 18)
同様のものに置き換えることができます ID+=18
, 、そして、すべてのインスタンスを削除する場合 return 0
, 、それは同じことをする必要がありますが、単純なループとして。次に、aを置く必要があります return 0
最後に、変数をグローバル以外にします。