#python #multithreading
Вопрос:
Я использую параллельные фьючерсы, чтобы удалить список H1 с веб-страниц и добавить их в список под названием archive_h1_list. Проблема в том, что, как только параллельные фьючерсы попадают в исключение, они перестают добавлять список.
Когда я печатаю полученный список ниже, он останавливается после первого исключения. ['Example Domain', 'Example Domain', 'Exception Error!']
он никогда не продолжает обрабатывать последнее https://www.example.com h1 в списке после попадания в исключение.
import concurrent.futures
from urllib.request import urlopen
from bs4 import BeautifulSoup
CONNECTIONS = 8
archive_url_list = ["https://www.example.com", "https://www.example.com", "sdfihaslkhasd", "https://www.example.com"]
archive_h1_list = []
def get_archive_h1(h1_url):
html = urlopen(h1_url)
bsh = BeautifulSoup(html.read(), 'lxml')
return bsh.h1.text.strip()
def concurrent_calls():
with concurrent.futures.ThreadPoolExecutor(max_workers=CONNECTIONS) as executor:
f1 = executor.map(get_archive_h1, archive_url_list)
try:
for future in f1:
archive_h1_list.append(future)
except Exception:
archive_h1_list.append("Exception Error!")
pass
Ожидаемый результат должен быть:
['Example Domain', 'Example Domain', 'Exception Error!', 'Example Domain']
Ответ №1:
Это потому, что ваш for
цикл находится внутри try
, и когда вы ловите исключение try
, блок приостанавливается, и except
блок выполняется, таким образом, ваш for
цикл прерывается.
Одним из способов решить эту проблему было бы переместить цикл for за пределы try
блока, однако, согласно документации Executor.map
:
Если вызов функции вызывает исключение, то это исключение будет вызвано, когда его значение будет извлечено из итератора.
Что делает обработку исключений довольно неприятной за пределами вашей функции.
Поэтому первое решение состоит в том, чтобы перехватывать исключения внутри get_archive_h1
:
def get_archive_h1(h1_url):
try:
html = urlopen(h1_url)
bsh = BeautifulSoup(html.read(), 'lxml')
return bsh.h1.text.strip()
except Exception:
return "Exception Error!"
def concurrent_calls():
with concurrent.futures.ThreadPoolExecutor(max_workers=CONNECTIONS) as executor:
f1 = executor.map(get_archive_h1, archive_url_list)
for future in f1:
archive_h1_list.append(future)
Другим решением является использование другого метода исполнителя, при котором у вас будет больше контроля над вашим будущим разрешением, т. е. Executor.submit
:
def concurrent_calls():
with concurrent.futures.ThreadPoolExecutor(max_workers=CONNECTIONS) as executor:
futures = [executor.submit(get_archive_h1, url) for url in archive_url_list]
for future in futures:
try:
archive_h1_list.append(future.result())
except Exception:
archive_h1_list.append("Exception Error!")
pass
Комментарии:
1. Большое спасибо! В этом есть смысл. Мне нужно использовать карту, потому что мне нужно вернуть список в исходном порядке. Спасибо, что нашли время объяснить мне это . Действительно ценю это.