অনুরোধের সাথে পাইথনে বড় ফাইল ডাউনলোড করুন


398

অনুরোধগুলি একটি দুর্দান্ত লাইব্রেরি। আমি এটি বড় ফাইল (> 1 জিবি) ডাউনলোড করার জন্য ব্যবহার করতে চাই। সমস্যাটি হ'ল সম্পূর্ণ ফাইলটি মেমরির মধ্যে রাখা আমার পক্ষে এটি খণ্ডে পড়া দরকার। এবং নিম্নলিখিত কোডের সাথে এটি একটি সমস্যা

import requests

def DownloadFile(url)
    local_filename = url.split('/')[-1]
    r = requests.get(url)
    f = open(local_filename, 'wb')
    for chunk in r.iter_content(chunk_size=512 * 1024): 
        if chunk: # filter out keep-alive new chunks
            f.write(chunk)
    f.close()
    return 

কোনও কারণে এটি এইভাবে কাজ করে না। এটি কোনও ফাইলে সংরক্ষণ করার আগে প্রতিক্রিয়াটি মেমরিতে লোড করে।

হালনাগাদ

আপনার যদি কোনও ছোট ক্লায়েন্টের (পাইথন ২.x / ৩.x) দরকার হয় যা এফটিপি থেকে বড় ফাইল ডাউনলোড করতে পারে তবে আপনি এটি এখানে খুঁজে পেতে পারেন । এটি মাল্টিথ্রেডিং সমর্থন করে এবং পুনরায় সংযোগ করে (এটি সংযোগগুলি নিরীক্ষণ করে) এটি ডাউনলোড কার্যের জন্য সকেট প্যারামগুলিকেও সুর দেয়।

উত্তর:


650

নিম্নলিখিত স্ট্রিমিং কোডের সাথে পাইথন মেমরির ব্যবহার ডাউনলোড করা ফাইলের আকার নির্বিশেষে নিষিদ্ধ করা হয়েছে:

def download_file(url):
    local_filename = url.split('/')[-1]
    # NOTE the stream=True parameter below
    with requests.get(url, stream=True) as r:
        r.raise_for_status()
        with open(local_filename, 'wb') as f:
            for chunk in r.iter_content(chunk_size=8192): 
                # If you have chunk encoded response uncomment if
                # and set chunk_size parameter to None.
                #if chunk: 
                f.write(chunk)
    return local_filename

নোট করুন যে ব্যবহার করে ফিরে আসা বাইট সংখ্যাটি iter_contentঠিক তা নয় chunk_size; এটি একটি এলোমেলো সংখ্যার হিসাবে প্রত্যাশিত হয় যা প্রায়শই অনেক বেশি বড় হয় এবং প্রতিটি পুনরাবৃত্তির ক্ষেত্রে এটি আলাদা হওয়ার আশা করা হয়।

দেখুন https://requests.readthedocs.io/en/latest/user/advanced/#body-content-workflow এবং https://requests.readthedocs.io/en/latest/api/#requests.Response.iter_content আরও জন্য রেফারেন্স।


9
@ শুমন যেমন আমি দেখতে পেয়েছি আপনি http: // থেকে https: // ( github.com/kennethreitz/requests/issues/2043 ) এ পরিবর্তন করার সময় সমস্যাটি সমাধান করেছেন । আপনি কি দয়া করে আপনার মন্তব্যগুলি আপডেট বা মুছতে পারেন কারণ লোকেরা ভাবতে পারে যে বড় ফাইলগুলির জন্য কোডে সমস্যা রয়েছে 1024 এমবি
রোমান পোডলিনভ

8
chunk_sizeঅত্যন্ত গুরুত্বপূর্ণ। ডিফল্টরূপে এটি 1 (1 বাইট)। তার মানে 1MB এর জন্য এটি 1 মিলিয়ন পুনরাবৃত্তি করবে। docs.python-requests.org/en/latest/api/…
এডওয়ার্ড

4
f.flush()অপ্রয়োজনীয় মনে হচ্ছে আপনি এটি ব্যবহার করে কী সম্পাদনের চেষ্টা করছেন? (আপনি যদি এটিকে ফেলে দেন তবে আপনার স্মৃতি ব্যবহার 1.5gb হবে না) won't f.write(b'')(যদি iter_content()একটি খালি স্ট্রিং ফিরে আসতে পারে) নিরীহ if chunkহতে হবে এবং তাই খুব বাদ দেওয়া যেতে পারে।
jfs

11
@ রোমানপডলিনভ: f.flush()দৈহিক ডিস্কে ডেটা ফ্লাশ করবেন না। এটি ওএসে ডেটা স্থানান্তর করে। সাধারণত, বিদ্যুতের ব্যর্থতা না থাকলে এটি যথেষ্ট। f.flush()বিনা কারণে কোডটি এখানে ধীর করে দেয়। ফ্ল্যাশ ঘটে যখন সংশোধনকারী ফাইল বাফার (অভ্যন্তরীণ অ্যাপ্লিকেশন) পূর্ণ হয়। আপনার যদি আরও ঘন ঘন লেখার প্রয়োজন হয়; বুফ.সাইজ প্যারামিটারটি পাস করুন open()
jfs

9
r.close()
0xcaff

271

এটা অনেক সহজ যদি আপনি ব্যবহার করেন Response.rawএবং shutil.copyfileobj():

import requests
import shutil

def download_file(url):
    local_filename = url.split('/')[-1]
    with requests.get(url, stream=True) as r:
        with open(local_filename, 'wb') as f:
            shutil.copyfileobj(r.raw, f)

    return local_filename

এটি অতিরিক্ত মেমরি ব্যবহার না করেই ডিস্কে ফাইলটি প্রবাহিত করে এবং কোডটি সহজ।


10
নোট করুন যে 2155 প্রতি ইস্যুতে জিপিড প্রতিক্রিয়াগুলি স্ট্রিম করার সময় আপনার সামঞ্জস্য হতে হতে পারে
ক্রিসপ

32
এটি সঠিক উত্তর হওয়া উচিত! গৃহীত উত্তর 2-3MB / সেকেন্ড আপনি আপ পায়। Copyfileobj ব্যবহার করে আপনাকে 40MB / s করতে হয়। L 50-55 এমবি / সেকেন্ডের সাথে কার্ল ডাউনলোড (একই মেশিন, একই ইউআরএল) ইত্যাদি।
ভিসোফট

24
অনুরোধগুলি সংযোগটি মুক্তি পেয়েছে তা নিশ্চিত করার জন্য, আপনি withঅনুরোধটি করতে দ্বিতীয় (নেস্টেড) ব্লকটি ব্যবহার করতে পারেন :with requests.get(url, stream=True) as r:
ক্রিশ্চিয়ান লং

7
with requests.get()@ ক্রিশ্চিয়ানলং : এটি সত্য, তবে খুব সম্প্রতি, সমর্থন করার বৈশিষ্ট্যটি কেবলমাত্র 2017-06-07 এ একত্রিত করা হয়েছিল! আপনার পরামর্শটি সেই সকল ব্যক্তির পক্ষে যুক্তিসঙ্গত যাঁদের কাছে 2.18.0 বা তার পরে অনুরোধ রয়েছে। রেফ: গিথুব.আর.কোয়েস্টস
জন জুইনক


54

ওপি যা বলছিল ঠিক তা নয়, তবে ... এটি করা হাস্যকরভাবে সহজ urllib:

from urllib.request import urlretrieve
url = 'http://mirror.pnl.gov/releases/16.04.2/ubuntu-16.04.2-desktop-amd64.iso'
dst = 'ubuntu-16.04.2-desktop-amd64.iso'
urlretrieve(url, dst)

বা এইভাবে, যদি আপনি এটি একটি অস্থায়ী ফাইলে সংরক্ষণ করতে চান:

from urllib.request import urlopen
from shutil import copyfileobj
from tempfile import NamedTemporaryFile
url = 'http://mirror.pnl.gov/releases/16.04.2/ubuntu-16.04.2-desktop-amd64.iso'
with urlopen(url) as fsrc, NamedTemporaryFile(delete=False) as fdst:
    copyfileobj(fsrc, fdst)

আমি প্রক্রিয়াটি দেখেছি:

watch 'ps -p 18647 -o pid,ppid,pmem,rsz,vsz,comm,args; ls -al *.iso'

এবং আমি ফাইলটি বাড়তে দেখেছি, তবে মেমরির ব্যবহার 17 মেগাবাইটে থেকে যায়। আমি কিছু অনুপস্থিত করছি?


2
পাইথন ২.x এর জন্য, ব্যবহার করুনfrom urllib import urlretrieve
ভাদিম কোতোভ

এটি ধীর গতির ডাউনলোডের গতিবেগের
ফলস্বরূপ

@ সিটিএনরম্যান আপনি কি বিস্তারিত বলতে পারবেন? কোন সমাধানের তুলনায়? কেন?
এক্স-ইউরি

@ এক্স-ইউরি shutil.copyfileobjসর্বাধিক ভোট দিয়ে সমাধানটি সমাধান করুন , আমার এবং অন্যদের মন্তব্য দেখুন
সিটিএনরম্যান

41

আপনার খণ্ডের আকারটি খুব বড় হতে পারে, আপনি কি এটি বাদ দেওয়ার চেষ্টা করেছেন - সম্ভবত এক সাথে 1024 বাইট? (এছাড়াও, আপনি withসিনট্যাক্স পরিপাটি করতে ব্যবহার করতে পারেন )

def DownloadFile(url):
    local_filename = url.split('/')[-1]
    r = requests.get(url)
    with open(local_filename, 'wb') as f:
        for chunk in r.iter_content(chunk_size=1024): 
            if chunk: # filter out keep-alive new chunks
                f.write(chunk)
    return 

ঘটনাচক্রে, আপনি কীভাবে ছাড় দিচ্ছেন যে প্রতিক্রিয়াটি মেমরিতে লোড হয়েছে?

মনে হচ্ছে যেন পাইথন ফাইলের জন্য ডেটা ফ্লাশ করছে না, অন্য যে কোনও প্রশ্ন থেকে আপনি চেষ্টা করতে পারেন f.flush()এবং os.fsync()ফাইলটি লিখতে এবং ফ্রি মেমোরিতে জোর করতে পারেন;

    with open(local_filename, 'wb') as f:
        for chunk in r.iter_content(chunk_size=1024): 
            if chunk: # filter out keep-alive new chunks
                f.write(chunk)
                f.flush()
                os.fsync(f.fileno())

1
আমি কুবুন্টুতে সিস্টেম মনিটর ব্যবহার করি। এটি আমাকে দেখায় যে পাইথন প্রক্রিয়াটির স্মৃতিশক্তি বৃদ্ধি পায় (25 কেবি থেকে 1.5 গিগাবাইট পর্যন্ত)।
রোমান পোডলিনভ

এই মেমরিটি ব্লাট চুষে ফেলে, সম্ভবত f.flush(); os.fsync()কোনও স্মৃতি মুক্ত লেখার জন্য চাপ দিতে পারে।
ড্যানোডোনভান

2
এটিos.fsync(f.fileno())
sebdelsol

29
অনুরোধ.জেট () কলটিতে আপনাকে প্রবাহ = সত্য ব্যবহার করতে হবে। স্মৃতি ফেটে যাওয়ার কারণেই।
Hut8

1
ছোটখাটো টাইপো: আপনি কোনও কলোন (':') এর পরে মিস করবেনdef DownloadFile(url)
অব্রে
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.