Двойной вызов install_opener() не добавляет второй opener к первому. Он заменяет стандартный opener модуля. Если один объект знает прокси, а другой — вход на сайт, постройте единственный opener с нужными handler-ами.

import os
from urllib.request import (ProxyHandler, HTTPBasicAuthHandler,
                            HTTPPasswordMgrWithDefaultRealm, build_opener)

passwords = HTTPPasswordMgrWithDefaultRealm()
passwords.add_password(None, 'https://example.com/private/',
                       os.environ['SITE_USER'], os.environ['SITE_PASSWORD'])
opener = build_opener(
    ProxyHandler({'https': 'http://203.0.113.10:8080'}),
    HTTPBasicAuthHandler(passwords),
)
with opener.open('https://example.com/private/report', timeout=10) as response:
    body = response.read()

Прокси и назначение — примеры. Для публичного сервиса, доступного через публичный HTTP-прокси, список HTTP-прокси даёт кандидатов в endpoint-ы для handler-а маршрутизации. Этот opener направляет HTTPS-запрос через HTTP-прокси и обрабатывает Basic-запрос назначения. Он не настраивает аутентификацию прокси.

Если прокси тоже требует входа, выберите поддерживаемую схему аутентификации прокси отдельно. 407 на этапе CONNECT возникает раньше, чем назначение сможет выдать собственный запрос. Добавление учётных данных сайта в менеджер паролей не удовлетворит этот более ранний обмен.

Прямой вызов opener.open() сохраняет конфигурацию вместе с операцией. Менять значение по умолчанию на уровне модуля не нужно, если только приложение намеренно не хочет, чтобы все удобные вызовы использовали этот opener.

Похожие статьи