Как мне загрузить файл через HTTP, используя Python?

У меня есть небольшая утилита, которую я использую для загрузки MP3 с веб-сайта по расписанию, а затем создаю / обновляет XML-файл подкаста, который я, очевидно, добавил в iTunes.

Обработка текста, который создает / обновляет файл XML, написана на Python. Я использую Wget внутри Windows .bat файл для загрузки фактического MP3 однако. Я бы предпочел, чтобы вся утилита была написана на Python.

Я изо всех сил пытался найти способ фактически загрузить файл в Python, поэтому я прибег к wget,

Итак, как мне загрузить файл с помощью Python?

31 ответ

Решение

В Python 2 используйте urllib2, который поставляется со стандартной библиотекой.

import urllib2
response = urllib2.urlopen('http://www.example.com/')
html = response.read()

Это самый простой способ использования библиотеки, за исключением обработки ошибок. Вы также можете делать более сложные вещи, такие как смена заголовков. Документацию можно найти здесь.

Еще один, используя urlretrieve:

import urllib
urllib.urlretrieve ("http://www.example.com/songs/mp3.mp3", "mp3.mp3")

(для Python 3+ используйте "import urllib.request" и urllib.request.urlretrieve)

Еще один, с "прогрессбаром"

import urllib2

url = "http://download.thinkbroadband.com/10MB.zip"

file_name = url.split('/')[-1]
u = urllib2.urlopen(url)
f = open(file_name, 'wb')
meta = u.info()
file_size = int(meta.getheaders("Content-Length")[0])
print "Downloading: %s Bytes: %s" % (file_name, file_size)

file_size_dl = 0
block_sz = 8192
while True:
    buffer = u.read(block_sz)
    if not buffer:
        break

    file_size_dl += len(buffer)
    f.write(buffer)
    status = r"%10d  [%3.2f%%]" % (file_size_dl, file_size_dl * 100. / file_size)
    status = status + chr(8)*(len(status)+1)
    print status,

f.close()

В 2012 году используйте библиотеку запросов Python

>>> import requests
>>> 
>>> url = "http://download.thinkbroadband.com/10MB.zip"
>>> r = requests.get(url)
>>> print len(r.content)
10485760

Вы можете запустить pip install requests чтобы получить это.

Запросы имеют много преимуществ по сравнению с альтернативами, потому что API намного проще. Это особенно верно, если вам нужно сделать аутентификацию. urllib и urllib2 довольно неинтуитивны и болезненны в этом случае.


2015-12-30

Люди выразили восхищение индикатором прогресса. Это круто, конечно. В настоящее время существует несколько готовых решений, в том числе tqdm:

from tqdm import tqdm
import requests

url = "http://download.thinkbroadband.com/10MB.zip"
response = requests.get(url, stream=True)

with open("10MB", "wb") as handle:
    for data in tqdm(response.iter_content()):
        handle.write(data)

По сути, это реализация @kvance, описанная 30 месяцев назад.

import urllib2
mp3file = urllib2.urlopen("http://www.example.com/songs/mp3.mp3")
with open('test.mp3','wb') as output:
  output.write(mp3file.read())

wb в open('test.mp3','wb') открывает файл (и стирает любой существующий файл) в двоичном режиме, чтобы вы могли сохранить с ним данные, а не только текст.

Python 3

  • urllib.request.urlopen

    import urllib.request
    response = urllib.request.urlopen('http://www.example.com/')
    html = response.read()
    
  • urllib.request.urlretrieve

    import urllib.request
    urllib.request.urlretrieve('http://www.example.com/songs/mp3.mp3', 'mp3.mp3')
    

Python 2

  • urllib2.urlopen (спасибо Кори)

    import urllib2
    response = urllib2.urlopen('http://www.example.com/')
    html = response.read()
    
  • urllib.urlretrieve (спасибо, ПаблоГ)

    import urllib
    urllib.urlretrieve('http://www.example.com/songs/mp3.mp3', 'mp3.mp3')
    
import os,requests
def download(url):
    get_response = requests.get(url,stream=True)
    file_name  = url.split("/")[-1]
    with open(file_name, 'wb') as f:
        for chunk in get_response.iter_content(chunk_size=1024):
            if chunk: # filter out keep-alive new chunks
                f.write(chunk)


download("https://example.com/example.jpg")

Используйте модуль wget:

import wget
wget.download('url')

Улучшенная версия кода PabloG для Python 2/3:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import ( division, absolute_import, print_function, unicode_literals )

import sys, os, tempfile, logging

if sys.version_info >= (3,):
    import urllib.request as urllib2
    import urllib.parse as urlparse
else:
    import urllib2
    import urlparse

def download_file(url, dest=None):
    """ 
    Download and save a file specified by url to dest directory,
    """
    u = urllib2.urlopen(url)

    scheme, netloc, path, query, fragment = urlparse.urlsplit(url)
    filename = os.path.basename(path)
    if not filename:
        filename = 'downloaded.file'
    if dest:
        filename = os.path.join(dest, filename)

    with open(filename, 'wb') as f:
        meta = u.info()
        meta_func = meta.getheaders if hasattr(meta, 'getheaders') else meta.get_all
        meta_length = meta_func("Content-Length")
        file_size = None
        if meta_length:
            file_size = int(meta_length[0])
        print("Downloading: {0} Bytes: {1}".format(url, file_size))

        file_size_dl = 0
        block_sz = 8192
        while True:
            buffer = u.read(block_sz)
            if not buffer:
                break

            file_size_dl += len(buffer)
            f.write(buffer)

            status = "{0:16}".format(file_size_dl)
            if file_size:
                status += "   [{0:6.2f}%]".format(file_size_dl * 100 / file_size)
            status += chr(13)
            print(status, end="")
        print()

    return filename

if __name__ == "__main__":  # Only run if this file is called directly
    print("Testing with 10MB download")
    url = "http://download.thinkbroadband.com/10MB.zip"
    filename = download_file(url)
    print(filename)

Все же просто Python 2 & Python 3 совместимый путь приходит с six библиотека:

from six.moves import urllib
urllib.request.urlretrieve("http://www.example.com/songs/mp3.mp3", "mp3.mp3")

Ниже приведены наиболее часто используемые вызовы для загрузки файлов в Python:

  1. urllib.urlretrieve ('url_to_file', file_name)

  2. urllib2.urlopen('url_to_file')

  3. requests.get(url)

  4. wget.download('url', file_name)

Замечания: urlopen а также urlretrieve найдены относительно плохими при загрузке больших файлов (размер> 500 МБ). requests.get сохраняет файл в памяти до завершения загрузки.

Написал библиотеку wget на чистом Python как раз для этого. Накачано urlretrieve с этими функциями, начиная с версии 2.0.

В python3 вы можете использовать библиотеки urllib3 и shutil. Загрузите их, используя pip или pip3 (в зависимости от того, используется ли python3 по умолчанию или нет)

pip3 install urllib3 shutil

Затем запустите этот код

import urllib.request
import shutil

url = "http://www.somewebsite.com/something.pdf"
output_file = "save_this_name.pdf"
with urllib.request.urlopen(url) as response, open(output_file, 'wb') as out_file:
    shutil.copyfileobj(response, out_file)

Обратите внимание, что вы загружаете urllib3 но использовать urllib в коде

Я согласен с Кори, urllib2 является более полным, чем urllib, и, вероятно, должен использоваться как модуль, если вы хотите делать более сложные вещи, но чтобы сделать ответы более полными, urllib - более простой модуль, если вы хотите только основы:

import urllib
response = urllib.urlopen('http://www.example.com/sound.mp3')
mp3 = response.read()

Будет работать нормально. Или, если вы не хотите иметь дело с объектом "response", вы можете вызвать read() напрямую:

import urllib
mp3 = urllib.urlopen('http://www.example.com/sound.mp3').read()

Если у вас установлен wget, вы можете использовать parallel_sync.

pip install parallel_sync

from parallel_sync import wget
urls = ['http://something.png', 'http://somthing.tar.gz', 'http://somthing.zip']
wget.download('/tmp', urls)
# or a single file:
wget.download('/tmp', urls[0], filenames='x.zip', extract=True)

Документ: https://pythonhosted.org/parallel_sync/pages/examples.html

Это довольно мощно. Он может загружать файлы параллельно, повторять попытки при сбое и даже может загружать файлы на удаленный компьютер.

Вы также можете получить отзыв о прогрессе с помощью urlretrieve:

def report(blocknr, blocksize, size):
    current = blocknr*blocksize
    sys.stdout.write("\r{0:.2f}%".format(100.0*current/size))

def downloadFile(url):
    print "\n",url
    fname = url.split('/')[-1]
    print fname
    urllib.urlretrieve(url, fname, report)

Вы можете python запросы

      import os
import requests


outfile = os.path.join(SAVE_DIR, file_name)
response = requests.get(URL, stream=True)
with open(outfile,'wb') as output:
  output.write(response.content)

Вы можете использовать шутил

      import os
import requests
import shutil
 
outfile = os.path.join(SAVE_DIR, file_name)
response = requests.get(url, stream = True)
with open(outfile, 'wb') as f:
  shutil.copyfileobj(response.content, f)
  • Если вы загружаете с ограниченного URL-адреса, не забудьте включить токен доступа в заголовки.

Ради полноты можно также вызвать любую программу для извлечения файлов, используя subprocess пакет. Программы, предназначенные для извлечения файлов, являются более мощными, чем функции Python, такие как urlretrieve, Например, wget можно скачать каталоги рекурсивно (-R), может иметь дело с FTP, перенаправлениями, HTTP прокси, может избежать повторной загрузки существующих файлов (-nc), а также aria2 может распараллеливать загрузки.

import subprocess
subprocess.check_output(['wget', '-O', 'example_output_file.html', 'https://example.com'])

В Jupyter Notebook можно также вызывать программы напрямую с ! синтаксис:

!wget -O example_output_file.html https://example.com

Используйте запросы Python в 5 строках

      import requets

remote_url = 'http://www.example.com/sound.mp3'
local_file_name = 'sound.mp3'

data = requests.get(remote_url)

# Save file data to local copy
with open(local_file_name, 'wb')as file:
    file.write(data.content)

Теперь сделайте что-нибудь с локальной копией удаленного файла.

Я написал следующее, которое работает в ванильном Python 2 или Python 3.


import sys
try:
    import urllib.request
    python3 = True
except ImportError:
    import urllib2
    python3 = False


def progress_callback_simple(downloaded,total):
    sys.stdout.write(
        "\r" +
        (len(str(total))-len(str(downloaded)))*" " + str(downloaded) + "/%d"%total +
        " [%3.2f%%]"%(100.0*float(downloaded)/float(total))
    )
    sys.stdout.flush()

def download(srcurl, dstfilepath, progress_callback=None, block_size=8192):
    def _download_helper(response, out_file, file_size):
        if progress_callback!=None: progress_callback(0,file_size)
        if block_size == None:
            buffer = response.read()
            out_file.write(buffer)

            if progress_callback!=None: progress_callback(file_size,file_size)
        else:
            file_size_dl = 0
            while True:
                buffer = response.read(block_size)
                if not buffer: break

                file_size_dl += len(buffer)
                out_file.write(buffer)

                if progress_callback!=None: progress_callback(file_size_dl,file_size)
    with open(dstfilepath,"wb") as out_file:
        if python3:
            with urllib.request.urlopen(srcurl) as response:
                file_size = int(response.getheader("Content-Length"))
                _download_helper(response,out_file,file_size)
        else:
            response = urllib2.urlopen(srcurl)
            meta = response.info()
            file_size = int(meta.getheaders("Content-Length")[0])
            _download_helper(response,out_file,file_size)

import traceback
try:
    download(
        "https://geometrian.com/data/programming/projects/glLib/glLib%20Reloaded%200.5.9/0.5.9.zip",
        "output.zip",
        progress_callback_simple
    )
except:
    traceback.print_exc()
    input()

Заметки:

  • Поддерживает обратный вызов "индикатор выполнения".
  • Скачайте 4 МБ теста.zip с моего сайта.

Поздний ответ, но для python>=3.6 ты можешь использовать:

import dload
dload.save(url)

Установить dload с участием:

pip3 install dload

Если скорость имеет значение для вас, я сделал небольшой тест производительности для модулей urllib а также wgetи относительно wget Я пробовал один раз со строкой состояния и один раз без. Я взял три разных файла размером 500 МБ для тестирования (разные файлы - чтобы исключить вероятность того, что под капотом происходит какое-то кэширование). Протестировано на машине Debian, с python2.

Во-первых, это результаты (они похожи в разных прогонах):

$ python wget_test.py 
urlretrive_test : starting
urlretrive_test : 6.56
==============
wget_no_bar_test : starting
wget_no_bar_test : 7.20
==============
wget_with_bar_test : starting
100% [......................................................................] 541335552 / 541335552
wget_with_bar_test : 50.49
==============

Я выполнил тест с использованием декоратора профиля. Это полный код:

import wget
import urllib
import time
from functools import wraps

def profile(func):
    @wraps(func)
    def inner(*args):
        print func.__name__, ": starting"
        start = time.time()
        ret = func(*args)
        end = time.time()
        print func.__name__, ": {:.2f}".format(end - start)
        return ret
    return inner

url1 = 'http://host.com/500a.iso'
url2 = 'http://host.com/500b.iso'
url3 = 'http://host.com/500c.iso'

def do_nothing(*args):
    pass

@profile
def urlretrive_test(url):
    return urllib.urlretrieve(url)

@profile
def wget_no_bar_test(url):
    return wget.download(url, out='/tmp/', bar=do_nothing)

@profile
def wget_with_bar_test(url):
    return wget.download(url, out='/tmp/')

urlretrive_test(url1)
print '=============='
time.sleep(1)

wget_no_bar_test(url2)
print '=============='
time.sleep(1)

wget_with_bar_test(url3)
print '=============='
time.sleep(1)

urllib кажется самым быстрым

Вы можете использовать PycURL на Python 2 и 3.

import pycurl

FILE_DEST = 'pycurl.html'
FILE_SRC = 'http://pycurl.io/'

with open(FILE_DEST, 'wb') as f:
    c = pycurl.Curl()
    c.setopt(c.URL, FILE_SRC)
    c.setopt(c.WRITEDATA, f)
    c.perform()
    c.close()

urlretrieve и запросы. получить просто, однако реальность нет. Я собрал данные для пары сайтов, включая текст и изображения, два из которых, вероятно, решают большинство задач. но для более универсального решения я предлагаю использовать урлопен. Поскольку он включен в стандартную библиотеку Python 3, ваш код может работать на любом компьютере, на котором работает Python 3, без предварительной установки site-par

import urllib.request
url_request = urllib.request.Request(url, headers=headers)
url_connect = urllib.request.urlopen(url_request)
len_content = url_content.length

#remember to open file in bytes mode
with open(filename, 'wb') as f:
    while True:
        buffer = url_connect.read(buffer_size)
        if not buffer: break

        #an integer value of size of written data
        data_wrote = f.write(buffer)

#you could probably use with-open-as manner
url_connect.close()

Этот ответ обеспечивает решение HTTP 403 Запрещено при загрузке файла через http с использованием Python. Я пробовал только запросы и модули urllib, другой модуль может обеспечить что-то лучшее, но именно этот я использовал для решения большинства проблем.

Исходный код может быть:

import urllib
sock = urllib.urlopen("http://diveintopython.org/")
htmlSource = sock.read()                            
sock.close()                                        
print htmlSource  

Это может быть немного поздно, но я видел код pabloG и не мог не добавить os.system('cls'), чтобы он выглядел УДИВИТЕЛЬНО! Проверьте это:

    import urllib2,os

    url = "http://download.thinkbroadband.com/10MB.zip"

    file_name = url.split('/')[-1]
    u = urllib2.urlopen(url)
    f = open(file_name, 'wb')
    meta = u.info()
    file_size = int(meta.getheaders("Content-Length")[0])
    print "Downloading: %s Bytes: %s" % (file_name, file_size)
    os.system('cls')
    file_size_dl = 0
    block_sz = 8192
    while True:
        buffer = u.read(block_sz)
        if not buffer:
            break

        file_size_dl += len(buffer)
        f.write(buffer)
        status = r"%10d  [%3.2f%%]" % (file_size_dl, file_size_dl * 100. / file_size)
        status = status + chr(8)*(len(status)+1)
        print status,

    f.close()

Если вы работаете в среде, отличной от Windows, вам придется использовать что-то другое, чем "cls". В MAC OS X и Linux это должно быть "ясно".

Новая реализация на основе API urllib3

      >>> import urllib3
>>> http = urllib3.PoolManager()
>>> r = http.request('GET', 'your_url_goes_here')
>>> r.status
   200
>>> r.data
   *****Response Data****

Подробнее: https://pypi.org/project/urllib3/

Другая возможность — со встроеннымhttp.client:

      from http import HTTPStatus, client
from shutil import copyfileobj

# using https
connection = client.HTTPSConnection("www.example.com")
with connection.request("GET", "/noise.mp3") as response:
    if response.status == HTTPStatus.OK:
        copyfileobj(response, open("noise.mp3")
    else:
        raise Exception("request needs work")

The HTTPConnectionобъект считается «низкоуровневым» в том смысле, что он выполняет желаемый запрос один раз и предполагает, что разработчик создаст его подкласс или сценарий для обработки нюансов HTTP. Библиотеки, такие как запросы, обычно обрабатывают более частные случаи, такие как автоматическое следование перенаправлениям и т. д.

Такие варианты:

  1. wget.download('url', file_name),
  2. urllib.urlretrieve ('url_to_file', file_name),

Есть недостаток: невозможно настроить пользовательский агент. Это может вызвать проблемы при загрузке файлов с внешних сайтов (они блокируют определенные пользовательские агенты, поэтому вам придется передать собственный).

Я нашел urllib3 очень удобным.

      import urllib3

resp = urllib3.request('GET', 'http://www.example.com/sound.mp3', preload_content=False, headers={'User-Agent': 'Customer User Agent If Needed'})

with open('sound.mp3', 'wb') as f:
    for chunk in resp.stream(65536):
        f.write(chunk)

resp.release_conn()

Я хотел загрузить все файлы с веб-страницы. Я попыталсяwget но это не удалось, поэтому я решил использовать маршрут Python и нашел эту ветку.

Прочитав его, я сделал небольшое приложение командной строки, soupget, расширяя отличные ответы PabloG и Stan и добавляя несколько полезных опций.

Он использует BeatifulSoup для сбора всех URL-адресов страницы, а затем загружает те, которые имеют желаемое расширение (я). Наконец, он может загружать несколько файлов параллельно.

Вот:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from __future__ import (division, absolute_import, print_function, unicode_literals)
import sys, os, argparse
from bs4 import BeautifulSoup

# --- insert Stan's script here ---
# if sys.version_info >= (3,): 
#...
#...
# def download_file(url, dest=None): 
#...
#...

# --- new stuff ---
def collect_all_url(page_url, extensions):
    """
    Recovers all links in page_url checking for all the desired extensions
    """
    conn = urllib2.urlopen(page_url)
    html = conn.read()
    soup = BeautifulSoup(html, 'lxml')
    links = soup.find_all('a')

    results = []    
    for tag in links:
        link = tag.get('href', None)
        if link is not None: 
            for e in extensions:
                if e in link:
                    # Fallback for badly defined links
                    # checks for missing scheme or netloc
                    if bool(urlparse.urlparse(link).scheme) and bool(urlparse.urlparse(link).netloc):
                        results.append(link)
                    else:
                        new_url=urlparse.urljoin(page_url,link)                        
                        results.append(new_url)
    return results

if __name__ == "__main__":  # Only run if this file is called directly
    # Command line arguments
    parser = argparse.ArgumentParser(
        description='Download all files from a webpage.')
    parser.add_argument(
        '-u', '--url', 
        help='Page url to request')
    parser.add_argument(
        '-e', '--ext', 
        nargs='+',
        help='Extension(s) to find')    
    parser.add_argument(
        '-d', '--dest', 
        default=None,
        help='Destination where to save the files')
    parser.add_argument(
        '-p', '--par', 
        action='store_true', default=False, 
        help="Turns on parallel download")
    args = parser.parse_args()

    # Recover files to download
    all_links = collect_all_url(args.url, args.ext)

    # Download
    if not args.par:
        for l in all_links:
            try:
                filename = download_file(l, args.dest)
                print(l)
            except Exception as e:
                print("Error while downloading: {}".format(e))
    else:
        from multiprocessing.pool import ThreadPool
        results = ThreadPool(10).imap_unordered(
            lambda x: download_file(x, args.dest), all_links)
        for p in results:
            print(p)

Пример его использования:

python3 soupget.py -p -e <list of extensions> -d <destination_folder> -u <target_webpage>

И реальный пример, если вы хотите увидеть его в действии:

python3 soupget.py -p -e .xlsx .pdf .csv -u https://healthdata.gov/dataset/chemicals-cosmetics

Вы можете использоватьkeras.utils.get_fileсделать это:

      from tensorflow import keras

path_to_downloaded_file = keras.utils.get_file(
    fname="file name",
    origin="https://www.linktofile.com/link/to/file",
    extract=True,
    archive_format="zip",  # downloaded file format
    cache_dir="/",  # cache and extract in current directory
)
Другие вопросы по тегам