Одного сеанса на всех уже достаточно. Внутри него пул соединений, и при разрыве соседнее соединение уходит другому потоку вместе с недочитанным телом. Отсюда и разбор, который спотыкается на середине.
Дальше про сам порядок ловли, он одинаковый для любой плавающей ошибки.
Первое: усилить условия, при которых она вылезает. Раз падает раз в четыре часа при тридцати потоках, ставьте девяносто и убирайте паузы. Такая ошибка живёт на редком совпадении, а частота совпадений растёт быстрее числа потоков.
Второе: каждая строка журнала несёт имя потока и номер задачи. Иначе потом ничего не сведёте.
log = logging.getLogger("progon")
def obrabotat(zadacha):
metka = "%s/%s" % (threading.current_thread().name, zadacha["id"])
log.info("vzyal %s", metka)
otvet = ses.get(zadacha["url"], timeout=20)
log.info("otvet %s %s bayt", metka, len(otvet.content))
return razobrat(otvet)
```
Третье: падение оставляет после себя вещдок. Одной строчки в журнале мало.
слушаю провод, а не догадки