Как питонный способ ловить ошибки и продолжать идти в этом цикле?
У меня есть две функции, которые прекрасно работают, но, кажется, ломаются, когда я запускаю их вместе.
def scrape_all_pages(alphabet):
pages = get_all_urls(alphabet)
for page in pages:
scrape_table(page)
Я пытаюсь систематически очищать некоторые результаты поиска. Так get_all_pages()
создает список URL для каждой буквы в алфавите. Иногда есть тысячи страниц, но это прекрасно работает. Затем для каждой страницы scrape_table
царапает только стол, в котором я заинтересован. Это также работает отлично. Я могу запустить все это, и это работает нормально, но я работаю в Scraperwiki, и если я настрою его на запуск и уход, он неизменно выдаст мне ошибку "индекс списка вне диапазона". Это определенно проблема в scraperwiki, но я хотел бы найти способ, чтобы сосредоточиться на этой проблеме, добавив некоторые try/except
пункты и ошибки регистрации, когда я сталкиваюсь с ними. Что-то вроде:
def scrape_all_pages(alphabet):
try:
pages = get_all_urls(alphabet)
except:
## LOG THE ERROR IF THAT FAILS.
try:
for page in pages:
scrape_table(page)
except:
## LOG THE ERROR IF THAT FAILS
Однако я не смог понять, как регистрировать ошибки в общем. Кроме того, вышесказанное выглядит неуклюже, и по моему опыту, когда что-то выглядит неуклюже, у Python есть лучший способ. Есть ли способ лучше?
5 ответов
Это хороший способ, но. Вы не должны использовать только except
предложение, вы должны указать тип исключения, которое вы пытаетесь поймать. Также вы можете поймать ошибку и продолжить цикл.
def scrape_all_pages(alphabet):
try:
pages = get_all_urls(alphabet)
except IndexError: #IndexError is an example
## LOG THE ERROR IF THAT FAILS.
for page in pages:
try:
scrape_table(page)
except IndexError: # IndexError is an example
## LOG THE ERROR IF THAT FAILS and continue this loop
Вы можете указать определенный тип исключений для перехвата и переменную для хранения экземпляра исключения:
def scrape_all_pages(alphabet):
try:
pages = get_all_urls(alphabet)
for page in pages:
scrape_table(page)
except OutOfRangeError as error:
# Will only catch OutOfRangeError
print error
except Exception as error:
# Will only catch any other exception
print error
Отлов типа Exception перехватит все ошибки, поскольку они предположительно все наследуются от Exception.
Это единственный известный мне способ обнаружения ошибок.
Оберните информацию журналов вокруг диспетчера контекста, как это, хотя вы можете легко изменить детали в соответствии с вашими требованиями:
import traceback
# This is a context manager
class LogError(object):
def __init__(self, logfile, message):
self.logfile = logfile
self.message = message
def __enter__(self):
return self
def __exit__(self, type, value, tb):
if type is None or not issubclass(type, Exception):
# Allow KeyboardInterrupt and other non-standard exception to pass through
return
self.logfile.write("%s: %r\n" % (self.message, value))
traceback.print_exception(type, value, tb, file=self.logfile)
return True # "swallow" the traceback
# This is a helper class to maintain an open file object and
# a way to provide extra information to the context manager.
class ExceptionLogger(object):
def __init__(self, filename):
self.logfile = open(filename, "wa")
def __call__(self, message):
# override function() call so that I can specify a message
return LogError(self.logfile, message)
Ключевая часть заключается в том, что __exit__ может возвращать "True", в этом случае исключение игнорируется, и программа продолжает выполняться. Код также должен быть немного осторожным, так как может возникать KeyboardInterrupt (control-C), SystemExit или другое нестандартное исключение, и где вы действительно хотите, чтобы программа остановилась.
Вы можете использовать выше в вашем коде, как это:
elog = ExceptionLogger("/dev/tty")
with elog("Can I divide by 0?"):
1/0
for i in range(-4, 4):
with elog("Divisor is %d" % (i,)):
print "5/%d = %d" % (i, 5/i)
Этот фрагмент дает мне вывод:
Can I divide by 0?: ZeroDivisionError('integer division or modulo by zero',)
Traceback (most recent call last):
File "exception_logger.py", line 24, in <module>
1/0
ZeroDivisionError: integer division or modulo by zero
5/-4 = -2
5/-3 = -2
5/-2 = -3
5/-1 = -5
Divisor is 0: ZeroDivisionError('integer division or modulo by zero',)
Traceback (most recent call last):
File "exception_logger.py", line 28, in <module>
print "5/%d = %d" % (i, 5/i)
ZeroDivisionError: integer division or modulo by zero
5/1 = 5
5/2 = 2
5/3 = 1
Я думаю, что также легко увидеть, как можно изменить код для обработки регистрации только исключений IndexError или даже передать базовый тип исключения для перехвата.
Возможно, регистрируйте ошибку для каждой итерации, чтобы ошибка в одной итерации не нарушала ваш цикл:
for page in pages:
try:
scrape_table(page)
except:
#open error log file for append:
f=open("errors.txt","a")
#write error to file:
f.write("Error occured\n") # some message specific to this iteration (page) should be added here...
#close error log file:
f.close()
Лучше написать это так:
try:
pages = get_all_urls(alphabet)
except IndexError:
## LOG THE ERROR IF THAT FAILS.
for page in pages:
try:
scrape_table(page)
except IndexError:
continue ## this will bring you to the next item in for
## LOG THE ERROR IF THAT FAILS