Tải xuống tệp từ web trong Python 3


331

Tôi đang tạo một chương trình sẽ tải xuống tệp .jar (java) từ máy chủ web, bằng cách đọc URL được chỉ định trong tệp .jad của cùng một trò chơi / ứng dụng. Tôi đang sử dụng Python 3.2.1

Tôi đã quản lý để trích xuất URL của tệp JAR từ tệp JAD (mọi tệp JAD chứa URL sang tệp JAR), nhưng như bạn có thể tưởng tượng, giá trị được trích xuất là chuỗi kiểu ().

Đây là chức năng có liên quan:

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

downloadFile(URL_from_file)

Tuy nhiên tôi luôn gặp lỗi khi nói rằng kiểu trong hàm trên phải là byte chứ không phải chuỗi. Tôi đã thử sử dụng URL.encode ('utf-8') và cả byte (URL, mã hóa = 'utf-8'), nhưng tôi luôn gặp lỗi tương tự hoặc tương tự.

Vì vậy, về cơ bản câu hỏi của tôi là làm thế nào để tải xuống một tệp từ máy chủ khi URL được lưu trữ trong một loại chuỗi?


4
@alvas, Tiền thưởng cho việc này? Người trả lời vẫn (và khá) hoạt động trên SO. Tại sao không chỉ thêm một bình luận và hỏi?
Bhargav Rao

8
Vì một câu trả lời tốt kéo dài bài kiểm tra thời gian là giá trị giải thưởng. Ngoài ra, chúng ta nên bắt đầu làm điều này cho rất nhiều câu hỏi khác để kiểm tra xem câu trả lời có liên quan ngày hôm nay không. Đặc biệt là khi việc sắp xếp các câu trả lời SO khá điên rồ, đôi khi câu trả lời lỗi thời hoặc thậm chí tồi tệ nhất lại đứng đầu.
alvas

Câu trả lời:


645

Nếu bạn muốn lấy nội dung của một trang web thành một biến, chỉ cần readphản hồi của 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

Cách dễ nhất để tải xuống và lưu tệp là sử dụng urllib.request.urlretrievechức năng:

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)

Nhưng hãy nhớ rằng đó urlretrieveđược coi là di sản và có thể trở nên phản đối (mặc dù không chắc tại sao).

Vì vậy, cách chính xác nhất để làm điều này là sử dụng urllib.request.urlopenhàm để trả về một đối tượng giống như tệp đại diện cho phản hồi HTTP và sao chép nó vào một tệp thực bằng cách sử dụng 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)

Nếu điều này có vẻ quá phức tạp, bạn có thể muốn đơn giản hơn và lưu trữ toàn bộ tải xuống trong một bytesđối tượng và sau đó ghi nó vào một tệp. Nhưng điều này chỉ hoạt động tốt cho các tập tin nhỏ.

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)

Có thể trích xuất .gz(và có thể các định dạng khác) dữ liệu nén một cách nhanh chóng, nhưng một hoạt động như vậy có thể yêu cầu máy chủ HTTP hỗ trợ truy cập ngẫu nhiên vào tệp.

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
bạn có thể sử dụng response.info().get_param('charset', 'utf-8')thay vì utf-8mã hóa cứng, để lấy mã hóa ký tự từ Content-Typetiêu đề
jfs

2
@OlehPrypin Tại sao outfile.write(data)chỉ hoạt động tốt cho các tệp nhỏ?
Bắt đầu

"Urlretrieve được coi là di sản và có thể trở nên phản đối" bạn lấy ý tưởng đó từ đâu?
Corey Goldberg

13
@Corey: Ngay từ các tài liệu : "21.6.24. Giao diện kế thừa Các chức năng và lớp sau đây được chuyển từ mô-đun Python 2 urllib (trái ngược với urllib2). Chúng có thể bị phản đối tại một thời điểm nào đó trong tương lai." ... và tôi đồng ý với "không chắc tại sao" của Oleh
cfi

@Oleh Prypin nếu tôi sử dụng với urllib.request.urlopen (url) làm phản hồi, mở (file_name, 'wb') dưới dạng out_file: shutil.copyfileobj (hồi đáp, out_file) thì làm cách nào tôi có thể tìm thấy mã trạng thái HTTP để biết tập tin không được tìm thấy?
Robert Achmann

145

tôi sử dụng requests gói bất cứ khi nào tôi muốn một cái gì đó liên quan đến các yêu cầu HTTP vì API của nó rất dễ bắt đầu với:

đầu tiên, cài đặt requests

$ pip install requests

sau đó là mã:

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

Tôi hy vọng tôi hiểu đúng câu hỏi, đó là: làm thế nào để tải xuống tệp từ máy chủ khi URL được lưu trữ theo kiểu chuỗi?

Tôi tải tập tin và lưu nó cục bộ bằng mã dưới đây:

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

xin chào, tôi cũng đang sử dụng cùng loại mã để tải xuống tệp nhưng đôi khi tôi phải đối mặt với ngoại lệ như - codec 'charmap' không thể mã hóa ký tự '\ u010c' ..... bạn có thể giúp tôi không
Joyson

10

Ở đây chúng ta có thể sử dụng giao diện Legacy của urllib trong Python3:

Các hàm và các lớp sau được chuyển từ urllib của mô-đun Python 2 (trái ngược với urllib2). Họ có thể trở nên chán nản vào một lúc nào đó trong tương lai.

Ví dụ (mã 2 dòng) :

import urllib.request

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

9

Bạn có thể sử dụng wget , công cụ shell phổ biến để tải về. https://pypi.python.org/pypi/wget Đây sẽ là phương pháp đơn giản nhất vì không cần phải mở tệp đích. Đây là một ví dụ.

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

Có, các yêu cầu chắc chắn là gói tuyệt vời để sử dụng trong một cái gì đó liên quan đến các yêu cầu HTTP. nhưng chúng ta cần cẩn thận với kiểu mã hóa của dữ liệu đến bên dưới là một ví dụ giải thích sự khác biệt


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

Động lực

Đôi khi, chúng tôi muốn có được hình ảnh nhưng không cần phải tải nó xuống tập tin thực,

tức là tải dữ liệu và giữ nó vào bộ nhớ.

Ví dụ: Nếu tôi sử dụng phương pháp học máy, hãy huấn luyện một mô hình có thể nhận dạng hình ảnh bằng số (mã vạch).

Khi tôi truy cập một số trang web và có những hình ảnh đó để tôi có thể sử dụng mô hình để nhận ra nó,

và tôi không muốn lưu những hình ảnh đó vào ổ đĩa của mình,

sau đó bạn có thể thử phương pháp dưới đây để giúp bạn tiếp tục tải xuống dữ liệu trên bộ nhớ.

Điểm

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)

về cơ bản, giống như @Ranvijay Kumar

Một ví dụ

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))
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.