Unduh file dari web dengan Python 3

333

Saya membuat program yang akan mengunduh file .jar (java) dari server web, dengan membaca URL yang ditentukan dalam file .jad dari game / aplikasi yang sama. Saya menggunakan Python 3.2.1

Saya telah berhasil mengekstrak URL file JAR dari file JAD (setiap file JAD berisi URL ke file JAR), tetapi seperti yang Anda bayangkan, nilai yang diekstraksi adalah string type ().

Inilah fungsi yang relevan:

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

downloadFile(URL_from_file)

Namun saya selalu mendapatkan pesan kesalahan yang mengatakan bahwa jenis fungsi di atas harus byte, dan bukan string. Saya sudah mencoba menggunakan URL.encode ('utf-8'), dan juga byte (URL, encoding = 'utf-8'), tetapi saya selalu mendapatkan kesalahan yang sama atau mirip.

Jadi pada dasarnya pertanyaan saya adalah bagaimana cara mengunduh file dari server ketika URL disimpan dalam tipe string?

Bo Milanovich
sumber
4
@alvas, Hadiah untuk ini? Penjawabnya masih (dan cukup) aktif di SO. Mengapa tidak menambahkan komentar saja dan bertanya?
Bhargav Rao
8
Karena jawaban yang bagus yang bertahan dalam ujian waktu layak diberikan. Juga, kita harus mulai melakukan ini untuk banyak pertanyaan lain untuk memeriksa apakah jawaban itu relevan hari ini. Terutama ketika pengurutan jawaban SO agak gila, kadang-kadang jawaban yang ketinggalan jaman atau bahkan terburuk naik ke atas.
alvas

Jawaban:

647

Jika Anda ingin memperoleh konten halaman web menjadi variabel, cukup readrespons dari 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

Cara termudah untuk mengunduh dan menyimpan file adalah dengan menggunakan urllib.request.urlretrievefungsi ini:

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)

Namun perlu diingat bahwa urlretrieveitu dianggap sebagai warisan dan mungkin menjadi usang (meskipun tidak yakin mengapa).

Jadi cara yang paling benar untuk melakukan ini adalah dengan menggunakan urllib.request.urlopenfungsi untuk mengembalikan objek seperti file yang mewakili respons HTTP dan menyalinnya ke file nyata menggunakan 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)

Jika ini tampaknya terlalu rumit, Anda mungkin ingin lebih sederhana dan menyimpan seluruh unduhan dalam suatu bytesobjek dan kemudian menulisnya ke file. Tetapi ini hanya berfungsi dengan baik untuk file kecil.

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)

Dimungkinkan untuk mengekstraksi .gz(dan mungkin format lain) dengan mengompresi data dengan cepat, tetapi operasi seperti itu mungkin memerlukan server HTTP untuk mendukung akses acak ke file tersebut.

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`.
Oleh Prypin
sumber
7
Anda bisa menggunakan response.info().get_param('charset', 'utf-8')alih-alih hardcoding utf-8, untuk mendapatkan pengkodean karakter dari Content-Typeheader
jfs
2
@ OlehPrypin Mengapa outfile.write(data)hanya berfungsi dengan baik untuk file kecil?
Startec
"urlretrieve dianggap sebagai warisan dan mungkin menjadi usang" dari mana Anda mendapatkan gagasan itu?
Corey Goldberg
13
@Corey: Langsung dari dokumen : "21.6.24. Antarmuka lama Fungsi dan kelas berikut porting dari modul Python 2 urllib (sebagai lawan dari urllib2). Mereka mungkin menjadi usang di beberapa titik di masa depan." ... dan saya setuju dengan Oleh "tidak yakin mengapa"
cfi
@Oleh Prypin jika saya gunakan dengan urllib.request.urlopen (url) sebagai respons, buka (file_name, 'wb') sebagai out_file: shutil.copyfileobj (response, out_file) lalu bagaimana saya dapat menemukan kode status HTTP dalam pernyataan tangkap untuk mengetahui file tidak ditemukan?
Robert Achmann
146

Saya menggunakan requestspaket kapan pun saya menginginkan sesuatu yang terkait dengan permintaan HTTP karena API-nya sangat mudah untuk memulai dengan:

pertama, instal requests

$ pip install requests

lalu kodenya:

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)
Ali Faki
sumber
16

Saya harap saya mengerti pertanyaan yang benar, yaitu: bagaimana cara mengunduh file dari server ketika URL disimpan dalam tipe string?

Saya mengunduh file dan menyimpannya secara lokal menggunakan kode di bawah ini:

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()
Ranvijay Kumar
sumber
hai, saya juga menggunakan jenis kode yang sama untuk mengunduh file tetapi beberapa waktu saya menghadapi pengecualian seperti - 'charmap' codec tidak dapat menyandikan karakter '\ u010c' ..... bisakah Anda membantu saya dengan hal itu
Joyson
10

Di sini kita dapat menggunakan antarmuka Legacy urllib di Python3:

Fungsi dan kelas berikut ini porting dari modul Python 2 urllib (sebagai lawan dari urllib2). Mereka mungkin menjadi usang di beberapa titik di masa depan.

Contoh (2 kode baris) :

import urllib.request

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

Anda dapat menggunakan wget yang populer untuk mengunduh alat shell untuk itu. https://pypi.python.org/pypi/wget Ini akan menjadi metode paling sederhana karena tidak perlu membuka file tujuan. Berikut ini sebuah contoh.

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') 
Lasith Niroshan
sumber
0

Ya, permintaan definietly adalah paket bagus untuk digunakan dalam sesuatu yang terkait dengan permintaan HTTP. tetapi kita perlu berhati-hati dengan jenis penyandian data yang masuk juga di bawah ini adalah contoh yang menjelaskan perbedaannya


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)
Kaushal
sumber
0

Motivasi

Terkadang, kami ingin mendapatkan gambar tetapi tidak perlu mengunduhnya ke file nyata,

yaitu mengunduh data dan menyimpannya di memori.

Misalnya, Jika saya menggunakan metode pembelajaran mesin, latih model yang dapat mengenali gambar dengan angka (kode batang).

Ketika saya laba-laba beberapa situs web dan yang memiliki gambar-gambar itu sehingga saya dapat menggunakan model untuk mengenalinya,

dan saya tidak ingin menyimpan gambar-gambar itu di drive disk saya,

maka Anda dapat mencoba metode di bawah ini untuk membantu Anda menyimpan data unduhan di memori.

Poin

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)

pada dasarnya, seperti @Ranvijay Kumar

Sebuah contoh

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()
Carson
sumber
-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))
pengguna7726287
sumber