كيف يمكنني تنزيل ملف عبر HTTP باستخدام Python؟

StackOverflow https://stackoverflow.com/questions/22676

  •  09-06-2019
  •  | 
  •  

سؤال

لدي أداة مساعدة صغيرة أستخدمها لتنزيل ملف MP3 من موقع ويب وفقًا لجدول زمني ثم إنشاء/تحديث ملف بودكاست XML والذي من الواضح أنني أضفته إلى iTunes.

تتم كتابة معالجة النص التي تقوم بإنشاء/تحديث ملف XML بلغة Python.أستخدم wget داخل Windows .bat ومع ذلك، يمكنك تنزيل ملف MP3 الفعلي.أفضل أن تكون الأداة المساعدة بأكملها مكتوبة بلغة بايثون.

لقد كافحت للعثور على طريقة لتنزيل الملف فعليًا في بايثون، ولهذا السبب لجأت إليه wget.

إذن، كيف يمكنني تنزيل الملف باستخدام بايثون؟

هل كانت مفيدة؟

المحلول

في 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، استخدم مكتبة طلبات بايثون

>>> 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') يفتح ملفًا (ويمحو أي ملف موجود) في الوضع الثنائي حتى تتمكن من حفظ البيانات به بدلاً من النص فقط.

بايثون 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')
    

بايثون 2

استخدم وحدة 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)

كتب wget مكتبة في بيثون خالصة لهذا الغرض فقط.يتم ضخها urlretrieve مع هذه المواصفات اعتبارا من الإصدار 2.0.

بسيطة حتى الآن Python 2 & Python 3 طريقة متوافقة تأتي مع six مكتبة:

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

وأنا أتفق مع كوري، urllib2 أكثر اكتمالا من urllib ومن المحتمل أن تكون الوحدة المستخدمة إذا كنت تريد القيام بأشياء أكثر تعقيدًا، ولكن لجعل الإجابات أكثر اكتمالاً، فإن urllib هي وحدة أبسط إذا كنت تريد الأساسيات فقط:

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

سوف تعمل بشكل جيد.أو، إذا كنت لا تريد التعامل مع كائن "الاستجابة"، يمكنك الاتصال به يقرأ() مباشرة:

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

فيما يلي الاستدعاءات الأكثر استخدامًا لتنزيل الملفات في لغة بايثون:

  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 يقوم بتخزين الملف في الذاكرة حتى اكتمال التنزيل.

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")

يمكنك الحصول على تعليقات التقدم باستخدام 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)

إذا كان wget مثبتًا لديك، فيمكنك استخدام موازي_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

هذا قوي جدًا.يمكنه تنزيل الملفات بالتوازي، وإعادة المحاولة عند الفشل، ويمكنه أيضًا تنزيل الملفات على جهاز بعيد.

في python3 يمكنك استخدام urllib3 و Shutil libraires.قم بتنزيلها باستخدام 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 في الكود

إذا كانت السرعة تهمك، فقد قمت بإجراء اختبار أداء صغير للوحدات urllib و wget, ، وفيما يتعلق wget حاولت مرة بشريط الحالة ومرة ​​بدونه.أخذت ثلاثة ملفات مختلفة بحجم 500 ميجابايت لاختبارها (ملفات مختلفة - للتخلص من احتمال وجود بعض التخزين المؤقت تحت الغطاء).تم الاختبار على جهاز دبيان باستخدام 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 يبدو أنه الأسرع

فقط من أجل الاكتمال، من الممكن أيضًا استدعاء أي برنامج لاسترداد الملفات باستخدام ملف 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

كود المصدر يمكن أن يكون:

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

لقد كتبت ما يلي، والذي يعمل في Vanilla 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 من موقع الويب الخاص بي.

يمكنك استخدام PycURL على بيثون 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()

قد يكون هذا متأخرًا بعض الشيء، لكنني رأيت كود 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، يجب أن يكون الأمر "واضحًا".

إن urlretrieve وrequests.get أمران سهلان، لكن الواقع ليس كذلك.لقد قمت بجلب بيانات لموقعين، بما في ذلك النصوص والصور، ومن المحتمل أن يحل الموقعان المذكوران أعلاه معظم المهام.ولكن للحصول على حل أكثر عالمية أقترح استخدام urlopen.نظرًا لأنه مضمن في مكتبة Python 3 القياسية، يمكن تشغيل التعليمات البرمجية الخاصة بك على أي جهاز يقوم بتشغيل Python 3 دون التثبيت المسبق لحزمة الموقع

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

#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 Forbidden عند تنزيل الملف عبر http باستخدام Python.لقد جربت الطلبات ووحدات urllib فقط، وقد توفر الوحدة الأخرى شيئًا أفضل، ولكن هذه هي الوحدة التي استخدمتها لحل معظم المشكلات.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top