Python 3'te web'den dosya indirme


332

Aynı oyunun / uygulamanın .jad dosyasında belirtilen URL'yi okuyarak bir web sunucusundan .jar (java) dosyasını indirecek bir program oluşturuyorum. Python 3.2.1 kullanıyorum

JAR dosyasının URL'sini JAD dosyasından (her JAD dosyası JAR dosyasının URL'sini içerir) ayıklamayı başardım, ancak tahmin edebileceğiniz gibi ayıklanan değer type () dizesidir.

İlgili işlev şöyledir:

def downloadFile(URL=None):
    import httplib2
    h = httplib2.Http(".cache")
    resp, content = h.request(URL, "GET")
    return content

downloadFile(URL_from_file)

Ancak ben her zaman yukarıdaki işlev türü dize değil bayt olması gerektiğini söyleyen bir hata alıyorum. URL.encode ('utf-8') ve ayrıca bayt (URL, encoding = 'utf-8') kullanmayı denedim, ancak her zaman aynı veya benzer hatayı alırdım.

Temelde sorum, URL bir dize türünde saklandığında sunucudan nasıl dosya indirileceği?


4
@alvas, Bunun için bir ödül mü? Cevap veren SO'da hala (ve oldukça) aktiftir. Neden sadece bir yorum eklemek ve sormak istemiyorsunuz?
Bhargav Rao

8
Çünkü zamanın testini sürdüğü iyi bir cevap ödüllendirmeye değer. Ayrıca, bugün cevapların alakalı olup olmadığını kontrol etmek için başka birçok soru için bunu yapmaya başlamalıyız. Özellikle SO cevaplarının sıralanması oldukça çılgın olduğunda, bazen eskimiş veya en kötü cevap en üste gider.
alvas

Yanıtlar:


646

Bir web sayfasının içeriğini bir değişkene almak istiyorsanız, sadece readşu yanıtı verebilirsiniz urllib.request.urlopen:

import urllib.request
...
url = 'http://example.com/'
response = urllib.request.urlopen(url)
data = response.read()      # a `bytes` object
text = data.decode('utf-8') # a `str`; this step can't be used if data is binary

Bir dosyayı indirmenin ve kaydetmenin en kolay yolu bu urllib.request.urlretrieveişlevi kullanmaktır :

import urllib.request
...
# Download the file from `url` and save it locally under `file_name`:
urllib.request.urlretrieve(url, file_name)
import urllib.request
...
# Download the file from `url`, save it in a temporary directory and get the
# path to it (e.g. '/tmp/tmpb48zma.txt') in the `file_name` variable:
file_name, headers = urllib.request.urlretrieve(url)

Ancak, urlretrievebunun eski olarak kabul edildiğini ve kullanımdan kaldırılabileceğini unutmayın (neden olsa emin değilsiniz).

Bunu yapmanın en doğru yolu, urllib.request.urlopenişlevi HTTP yanıtını temsil eden dosya benzeri bir nesneyi döndürmek ve bunu kullanarak gerçek bir dosyaya kopyalamaktır shutil.copyfileobj.

import urllib.request
import shutil
...
# Download the file from `url` and save it locally under `file_name`:
with urllib.request.urlopen(url) as response, open(file_name, 'wb') as out_file:
    shutil.copyfileobj(response, out_file)

Bu çok karmaşık görünüyorsa, daha basit gitmek ve tüm indirmeyi bir bytesnesnede saklamak ve daha sonra bir dosyaya yazmak isteyebilirsiniz. Ancak bu yalnızca küçük dosyalar için iyi çalışır.

import urllib.request
...
# Download the file from `url` and save it locally under `file_name`:
with urllib.request.urlopen(url) as response, open(file_name, 'wb') as out_file:
    data = response.read() # a `bytes` object
    out_file.write(data)

.gzSıkıştırılmış verileri anında ayıklamak (ve belki de diğer formatları) mümkündür , ancak böyle bir işlem muhtemelen HTTP sunucusunun dosyaya rastgele erişimi desteklemesini gerektirir.

import urllib.request
import gzip
...
# Read the first 64 bytes of the file inside the .gz archive located at `url`
url = 'http://example.com/something.gz'
with urllib.request.urlopen(url) as response:
    with gzip.GzipFile(fileobj=response) as uncompressed:
        file_header = uncompressed.read(64) # a `bytes` object
        # Or do anything shown above using `uncompressed` instead of `response`.

7
karakter kodlamasını başlıktan almak için response.info().get_param('charset', 'utf-8')hardcoding yerine kullanabilirsinizutf-8Content-Type
jfs

2
@OlehPrypin Neden outfile.write(data)yalnızca küçük dosyalar için iyi çalışıyor?
Startec

"urlretrieve miras olarak kabul edilir ve kullanımdan kaldırılabilir" bu fikri nereden buldunuz?
Corey Goldberg

13
@Corey: Doğrudan dokümanlardan : "21.6.24. Eski arayüz Python 2 modülü urllib'den (urllib2 yerine) aşağıdaki işlevler ve sınıflar taşınır. Gelecekte bir noktada kullanımdan kaldırılabilirler." ... ve
Oleh'in

@Oleh Prypin yanıt olarak urllib.request.urlopen (url) ile kullanırsam, out_file: shutil.copyfileobj (yanıt, out_file) olarak aç (dosya_adı, 'wb') sonra catch deyiminde HTTP durum kodunu nasıl bulabilirim bulunamadı bulunamadı?
Robert Achmann

146

requestsAPI'si ile başlamak çok kolay olduğu için HTTP istekleriyle ilgili bir şey istediğimde paketi kullanıyorum :

ilk olarak, yükleyin requests

$ pip install requests

sonra kod:

from requests import get  # to make GET request


def download(url, file_name):
    # open in binary mode
    with open(file_name, "wb") as file:
        # get request
        response = get(url)
        # write to file
        file.write(response.content)

16

Umarım soruyu doğru anladım, ki: URL bir dize türünde saklandığında sunucudan nasıl dosya indirilir?

Aşağıdaki kodu kullanarak dosyaları indiriyorum ve yerel olarak kaydediyorum:

import requests

url = 'https://www.python.org/static/img/python-logo.png'
fileName = 'D:\Python\dwnldPythonLogo.png'
req = requests.get(url)
file = open(fileName, 'wb')
for chunk in req.iter_content(100000):
    file.write(chunk)
file.close()

merhaba, ben de dosyayı indirmek için kod aynı tür kullanıyorum ama biraz zaman ben gibi istisna karşı karşıya gelmiş - 'charmap' codec'i kodlamak karakteri '\ u010c' ..... Bana bu konuda yardımcı olabilir olamaz
Joyson

10

Burada urllib'in Eski arayüzünü Python3'te kullanabiliriz:

Aşağıdaki işlevler ve sınıflar Python 2 modülü urllib'den (urllib2 yerine) taşınır. Gelecekte bir noktada itiraz edilebilirler.

Örnek (2 satır kodu) :

import urllib.request

url = 'https://www.python.org/static/img/python-logo.png'
urllib.request.urlretrieve(url, "logo.png")

9

Bunun için popüler indirme kabuk aracı olan wget kullanabilirsiniz . https://pypi.python.org/pypi/wget Hedef dosyayı açması gerekmediği için bu en basit yöntem olacaktır. İşte bir örnek.

import wget
url = 'https://i1.wp.com/python3.codes/wp-content/uploads/2015/06/Python3-powered.png?fit=650%2C350'  
wget.download(url, '/Users/scott/Downloads/cat4.jpg') 

0

Evet, kesinlikle talepler HTTP istekleriyle ilgili bir şeyde kullanmak için harika bir pakettir. ancak gelen verilerin kodlama türüne dikkat etmemiz gerekiyor, ayrıca aşağıdaki farkı açıklayan bir örnek


from requests import get

# case when the response is byte array
url = 'some_image_url'

response = get(url)
with open('output', 'wb') as file:
    file.write(response.content)


# case when the response is text
# Here unlikely if the reponse content is of type **iso-8859-1** we will have to override the response encoding
url = 'some_page_url'

response = get(url)
# override encoding by real educated guess as provided by chardet
r.encoding = r.apparent_encoding

with open('output', 'w', encoding='utf-8') as file:
    file.write(response.content)

0

Motivasyon

Bazen, resmi almak istiyoruz ancak gerçek dosyalara indirmeye gerek yok,

yani, verileri indirin ve bellekte tutun.

Örneğin, makine öğrenme yöntemini kullanırsam, bir görüntüyü numarayla (barkod) tanıyabilen bir model eğitin.

Bazı web sitelerini örttüğümde ve bu görüntülere sahip olduğumda, modeli tanımak için kullanabilirim,

ve bu resimleri disk sürücüme kaydetmek istemiyorum,

verileri bellekte indirmenize yardımcı olmak için aşağıdaki yöntemi deneyebilirsiniz.

makas

import requests
from io import BytesIO
response = requests.get(url)
with BytesIO as io_obj:
    for chunk in response.iter_content(chunk_size=4096):
        io_obj.write(chunk)

temelde, @Ranvijay Kumar gibi

Bir örnek

import requests
from typing import NewType, TypeVar
from io import StringIO, BytesIO
import matplotlib.pyplot as plt
import imageio

URL = NewType('URL', str)
T_IO = TypeVar('T_IO', StringIO, BytesIO)


def download_and_keep_on_memory(url: URL, headers=None, timeout=None, **option) -> T_IO:
    chunk_size = option.get('chunk_size', 4096)  # default 4KB
    max_size = 1024 ** 2 * option.get('max_size', -1)  # MB, default will ignore.
    response = requests.get(url, headers=headers, timeout=timeout)
    if response.status_code != 200:
        raise requests.ConnectionError(f'{response.status_code}')

    instance_io = StringIO if isinstance(next(response.iter_content(chunk_size=1)), str) else BytesIO
    io_obj = instance_io()
    cur_size = 0
    for chunk in response.iter_content(chunk_size=chunk_size):
        cur_size += chunk_size
        if 0 < max_size < cur_size:
            break
        io_obj.write(chunk)
    io_obj.seek(0)
    """ save it to real file.
    with open('temp.png', mode='wb') as out_f:
        out_f.write(io_obj.read())
    """
    return io_obj


def main():
    headers = {
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3',
        'Accept-Encoding': 'gzip, deflate',
        'Accept-Language': 'zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7',
        'Cache-Control': 'max-age=0',
        'Connection': 'keep-alive',
        'Host': 'statics.591.com.tw',
        'Upgrade-Insecure-Requests': '1',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.36'
    }
    io_img = download_and_keep_on_memory(URL('http://statics.591.com.tw/tools/showPhone.php?info_data=rLsGZe4U%2FbphHOimi2PT%2FhxTPqI&type=rLEFMu4XrrpgEw'),
                                         headers,  # You may need this. Otherwise, some websites will send the 404 error to you.
                                         max_size=4)  # max loading < 4MB
    with io_img:
        plt.rc('axes.spines', top=False, bottom=False, left=False, right=False)
        plt.rc(('xtick', 'ytick'), color=(1, 1, 1, 0))  # same of plt.axis('off')
        plt.imshow(imageio.imread(io_img, as_gray=False, pilmode="RGB"))
        plt.show()


if __name__ == '__main__':
    main()

-3
from urllib import request

def get(url):
    with request.urlopen(url) as r:
        return r.read()


def download(url, file=None):
    if not file:
        file = url.split('/')[-1]
    with open(file, 'wb') as f:
        f.write(get(url))
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.