urllib2 を使用して URL をフェッチするクローラーを作成しました。
数回のリクエストごとに奇妙な動作が発生し、Wireshark で分析してみましたが、問題を理解できませんでした。
getPAGE()
URL のフェッチを担当します。URL のフェッチに成功した場合は URL のコンテンツ (response.read()) を返し、それ以外の場合は None を返します。
def getPAGE(FetchAddress):
attempts = 0
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:5.0) Gecko/20100101 Firefox/5.0'}
while attempts < 2:
req = Request(FetchAddress, None ,headers)
try:
response = urlopen(req) #fetching the url
except HTTPError, e:
print 'The server didn\'t do the request.'
print 'Error code: ', str(e.code) + " address: " + FetchAddress
time.sleep(4)
attempts += 1
except URLError, e:
print 'Failed to reach the server.'
print 'Reason: ', str(e.reason) + " address: " + FetchAddress
time.sleep(4)
attempts += 1
except Exception, e:
print 'Something bad happened in gatPAGE.'
print 'Reason: ', str(e.reason) + " address: " + FetchAddress
time.sleep(4)
attempts += 1
else:
return response.read()
return None
これは、フェッチしたページが有効かどうかを呼び出しgetPAGE()
てチェックする関数です (- companyID = soup.find('span',id='lblCompanyNumber').string
companyID が None の場合、ページは有効ではありません)。ページが有効な場合は、スープ オブジェクトを「curRes」という名前のグローバル変数に保存します。 '。
def isValid(ID):
global curRes
try:
address = urlPath+str(ID)
page = getPAGE(address)
if page == None:
saveToCsv(ID, badRequest = True)
return False
except Exception, e:
print "An error occured in the first Exception block of parseHTML : " + str(e) +' address: ' + address
else:
try:
soup = BeautifulSoup(page)
except TypeError, e:
print "An error occured in the second Exception block of parseHTML : " + str(e) +' address: ' + address
return False
try:
companyID = soup.find('span',id='lblCompanyNumber').string
if (companyID == None): #if lblCompanyNumber is None we can assume that we don't have the content we want, save in the bad log file
saveToCsv(ID, isEmpty = True)
return False
else:
curRes = soup #we have the data we need, save the soup obj to a global variable
return True
except Exception, e:
print "Error while parsing this page, third exception block: " + str(e) + ' id: ' + address
return False
奇妙な行動は -
- urllib2 が GET 要求を実行し、応答を待たずに次の GET 要求を送信する場合があります (最後の要求を無視します)。
- コードが約 20 分間スタックした後、 「[errno 10054] 既存の接続がリモート ホストによって強制的に閉じられました」というメッセージが表示されることがあります。コードがスタックしている間、サーバーからの応答を待っています。URL をコピーしてフェッチしようとしました。手動で、1 秒以内 (?) に応答を取得します。
- getPAGE() 関数は、URL を返せなかった場合、 isValid() に None を返します。エラーが発生することがあります -
このページの解析中にエラーが発生しました。3 番目の例外ブロック: 'NoneType' オブジェクトに属性 'string' id がありません:....
getPAGE() から有効な結果が得られた場合にのみスープ オブジェクトを作成しているため、奇妙です。スープ関数は None を返しているようで、実行しようとするたびに例外が発生しています。
companyID =soup.find('span',id='lblCompanyNumber').string
スープ オブジェクトは決して None であってはなりません。コードのその部分に到達した場合、getPAGE() から HTML を取得する必要があります。
私はチェックして、問題が最初の問題に何らかの形で関連していることを確認しました(GETを送信し、返信を待たずに、(WireSharkで)その例外を取得するたびに、urllib2がGETリクエストを送信したURLに対するものであることがわかりましたしかし、応答を待たずに先に進み、getPAGE() はその URL に対して None を返す必要がありましたが、None を返す場合、isValid(ID) は「if page == None:」条件を渡しません。なぜそれが起こっているのかわからないので、問題を再現することは不可能です.
time.sleep() がurllib2 threading で問題を引き起こす可能性があることを読んだので、使用を避けるべきでしょうか?
なぜ urllib2 は常に応答を待たないのですか (まれにしか応答を待ちません)。
「[errno 10054] 既存の接続は、リモート ホストによって強制的に閉じられました」というエラーはどうすればよいですか? ところで - 例外は getPAGE() try: except ブロックによってキャッチされません。最初の isValid() try: except: ブロックによってキャッチされます。
try:
address = urlPath+str(ID)
page = getPAGE(address)
if page == None:
saveToCsv(ID, badRequest = True)
return False
except Exception, e:
print "An error occured in the first Exception block of parseHTML : " + str(e) +' address: ' + address
ありがとう!