Как скачать файл по http с авторизацией в python 3.0, обходя баги?

У меня есть скрипт, который я хотел бы продолжить использовать, но похоже, что мне нужно либо найти какой-то обходной путь для ошибки в Python 3, либо вернуться к версии 2.6, и, следовательно, мне придется понизить и другие скрипты...

Надеюсь, кому-то здесь уже удалось найти обходной путь.

Проблема в том, что из-за новых изменений в Python 3.0, касающихся байтов и строк, не весь код библиотеки, по-видимому, протестирован.

У меня есть скрипт, который загружает страницу с веб-сервера. Этот скрипт передал имя пользователя и пароль как часть URL-адреса в Python 2.6, но в Python 3.0 это больше не работает.

Например, это:

import urllib.request;
url = "http://username:password@server/file";
urllib.request.urlretrieve(url, "temp.dat");

терпит неудачу с этим исключением:

Traceback (most recent call last):
  File "C:\Temp\test.py", line 5, in <module>
    urllib.request.urlretrieve(url, "test.html");
  File "C:\Python30\lib\urllib\request.py", line 134, in urlretrieve
    return _urlopener.retrieve(url, filename, reporthook, data)
  File "C:\Python30\lib\urllib\request.py", line 1476, in retrieve
    fp = self.open(url, data)
  File "C:\Python30\lib\urllib\request.py", line 1444, in open
    return getattr(self, name)(url)
  File "C:\Python30\lib\urllib\request.py", line 1618, in open_http
    return self._open_generic_http(http.client.HTTPConnection, url, data)
  File "C:\Python30\lib\urllib\request.py", line 1576, in _open_generic_http
    auth = base64.b64encode(user_passwd).strip()
  File "C:\Python30\lib\base64.py", line 56, in b64encode
    raise TypeError("expected bytes, not %s" % s.__class__.__name__)
TypeError: expected bytes, not str

По-видимому, кодировка base64 теперь требует байтов и выводит строку, и, таким образом, urlretrieve (или некоторый код в нем), который создает строку имени пользователя: пароль и пытается закодировать ее в base64 для простой авторизации, терпит неудачу.

Если вместо этого я попытаюсь использовать urlopen, например:

import urllib.request;
url = "http://username:password@server/file";
f = urllib.request.urlopen(url);
contents = f.read();

Затем он терпит неудачу с этим исключением:

Traceback (most recent call last):
  File "C:\Temp\test.py", line 5, in <module>
    f = urllib.request.urlopen(url);
  File "C:\Python30\lib\urllib\request.py", line 122, in urlopen
    return _opener.open(url, data, timeout)
  File "C:\Python30\lib\urllib\request.py", line 359, in open
    response = self._open(req, data)
  File "C:\Python30\lib\urllib\request.py", line 377, in _open
    '_open', req)
  File "C:\Python30\lib\urllib\request.py", line 337, in _call_chain
    result = func(*args)
  File "C:\Python30\lib\urllib\request.py", line 1082, in http_open
    return self.do_open(http.client.HTTPConnection, req)
  File "C:\Python30\lib\urllib\request.py", line 1051, in do_open
    h = http_class(host, timeout=req.timeout) # will parse host:port
  File "C:\Python30\lib\http\client.py", line 620, in __init__
    self._set_hostport(host, port)
  File "C:\Python30\lib\http\client.py", line 632, in _set_hostport
    raise InvalidURL("nonnumeric port: '%s'" % host[i+1:])
http.client.InvalidURL: nonnumeric port: 'password@server'

По-видимому, синтаксический анализ URL-адресов в этой «библиотеке поиска URL-адресов следующего поколения» не знает, что делать с именем пользователя и паролями в URL-адресе.

Какие еще у меня есть варианты?


person Lasse V. Karlsen    schedule 27.12.2008    source источник


Ответы (3)


Прямо из документации Py3k: http://docs.python.org/dev/py3k/library/urllib.request.html#examples

import urllib.request
# Create an OpenerDirector with support for Basic HTTP Authentication...
auth_handler = urllib.request.HTTPBasicAuthHandler()
auth_handler.add_password(realm='PDQ Application',
                          uri='https://mahler:8092/site-updates.py',
                          user='klem',
                          passwd='kadidd!ehopper')
opener = urllib.request.build_opener(auth_handler)
# ...and install it globally so it can be used with urlopen.
urllib.request.install_opener(opener)
urllib.request.urlopen('http://www.example.com/login.html')
person jb.    schedule 27.12.2008
comment
Вы хотели опубликовать этот пароль? Если нет, то предлагаю удалить ответ и разместить там новый с фиктивными данными. Спасибо за ответ, хотя, это выглядит многообещающе. - person Lasse V. Karlsen; 28.12.2008
comment
Клем, вероятно, очень зол, если это его настоящий пароль :) - person jb.; 28.12.2008

Я бы посоветовал сохранить вашу ветку 2. * в качестве производственной ветки, пока вы не сможете отсортировать материал 3.0.

Я собираюсь немного подождать, прежде чем перейти на Python 3.0. Вроде много народу в спешке, а я просто хочу, чтобы все разобрали, и приличный выбор сторонних библиотек. Это может занять год, это может занять 18 месяцев, но давление «обновления» для меня действительно невелико.

person Ali Afshar    schedule 28.12.2008

Вы можете использовать request.get для загрузки файла. Попробуйте пример кода:

import requests
from requests.auth import HTTPBasicAuth

def download_file(user_name, user_pwd, url, file_path):
    file_name = url.rsplit('/', 1)[-1]
    with requests.get(url, stream = True, auth = HTTPBasicAuth(user_name, user_pwd)) as response:
        with open(file_path + "/" + file_name, 'wb') as f:
            for chunk in response.iter_content(chunk_size = 8192):
                f.write(chunk)

# You will download the login.html file to /home/dan/
download_file("dan", "password", "http://www.example.com/login.html", "/home/dan/")

Наслаждайся этим!!

То же, что: https://stackoverflow.com/a/66796358/9265663

person W. Dan    schedule 25.03.2021