ดาวน์โหลดไฟล์จากเว็บใน Python 3


332

ฉันกำลังสร้างโปรแกรมที่จะดาวน์โหลดไฟล์. jar (java) จากเว็บเซิร์ฟเวอร์โดยอ่าน URL ที่ระบุในไฟล์. jad ของเกม / แอปพลิเคชันเดียวกัน ฉันใช้ Python 3.2.1

ฉันจัดการเพื่อแยก URL ของไฟล์ JAR จากไฟล์ JAD (ไฟล์ JAD ทุกไฟล์มี URL ไปยังไฟล์ JAR) แต่อย่างที่คุณอาจจินตนาการว่าสตริงที่แยกออกมาเป็น type ()

นี่คือฟังก์ชั่นที่เกี่ยวข้อง:

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

downloadFile(URL_from_file)

อย่างไรก็ตามฉันมักจะได้รับข้อผิดพลาดบอกว่าประเภทในฟังก์ชั่นข้างต้นจะต้องมีไบต์และไม่ใช่สตริง ฉันได้ลองใช้ URL.encode ('utf-8') และไบต์ (URL, encoding = 'utf-8') แต่ฉันมักจะได้รับข้อผิดพลาดที่คล้ายกันหรือคล้ายกัน

ดังนั้นโดยทั่วไปคำถามของฉันคือวิธีการดาวน์โหลดไฟล์จากเซิร์ฟเวอร์เมื่อ URL ถูกจัดเก็บในรูปแบบสตริง?


4
@alvas รางวัลสำหรับสิ่งนี้? ผู้ตอบยังใช้งานอยู่ (และค่อนข้าง) ใน SO ทำไมไม่เพียงแค่เพิ่มความคิดเห็นและถาม?
Bhargav Rao

8
เพราะคำตอบที่ดีที่การทดสอบเวลามีค่าควรแก่การตัดสิน นอกจากนี้เราควรเริ่มทำสิ่งนี้เพื่อคำถามอื่น ๆ มากมายเพื่อตรวจสอบว่าคำตอบมีความเกี่ยวข้องในวันนี้หรือไม่ โดยเฉพาะอย่างยิ่งเมื่อการเรียงลำดับของคำตอบ SO ค่อนข้างบ้าบางครั้งคำตอบที่ล้าสมัยหรือเลวร้ายที่สุดก็ขึ้นไปข้างบน
alvas

คำตอบ:


646

หากคุณต้องการได้รับเนื้อหาของหน้าเว็บเป็นตัวแปรเพียงแค่readการตอบสนองของ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

วิธีที่ง่ายที่สุดในการดาวน์โหลดและบันทึกไฟล์คือการใช้urllib.request.urlretrieveฟังก์ชั่น:

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)

แต่โปรดจำไว้ว่าurlretrieveถือเป็นมรดกและอาจเลิกใช้แล้ว (ไม่แน่ใจว่าเพราะอะไร)

ดังนั้นส่วนใหญ่ที่ถูกต้องวิธีการทำเช่นนี้จะเป็นที่จะใช้urllib.request.urlopenฟังก์ชั่นที่จะกลับมาเป็นไฟล์เหมือนวัตถุที่แสดงถึงการตอบสนอง HTTP 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)

หากดูเหมือนซับซ้อนเกินไปคุณอาจต้องการที่จะง่ายขึ้นและเก็บการดาวน์โหลดทั้งหมดไว้ในbytesวัตถุแล้วเขียนลงในไฟล์ แต่วิธีนี้ใช้งานได้ดีสำหรับไฟล์ขนาดเล็กเท่านั้น

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)

เป็นไปได้ที่จะดึง.gz(และรูปแบบอื่น ๆ ) บีบอัดข้อมูลได้ทันที แต่การดำเนินการดังกล่าวอาจต้องใช้เซิร์ฟเวอร์ HTTP เพื่อสนับสนุนการเข้าถึงไฟล์แบบสุ่ม

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
คุณสามารถใช้response.info().get_param('charset', 'utf-8')แทนutf-8การเข้ารหัสแบบแข็งเพื่อให้ได้การเข้ารหัสอักขระจากContent-Typeส่วนหัว
jfs

2
@OlehPrypin เหตุใดจึงoutfile.write(data)ทำงานได้ดีกับไฟล์ขนาดเล็กเท่านั้น
Startec

"urlretrieve ถือเป็นมรดกและอาจเลิกใช้แล้ว" คุณคิดอย่างไร
Corey Goldberg

13
@Corey: จากdocs : "21.6.24. อินเตอร์เฟสแบบดั้งเดิมฟังก์ชันและคลาสต่อไปนี้ได้รับการพอร์ตจากโมดูล urlib ของ Python 2 (ตรงข้ามกับ urllib2) พวกมันอาจเลิกใช้แล้วในอนาคต" ... และฉันเห็นด้วยกับ "ไม่แน่ใจว่าทำไม" ของเลห์
cfi

@Oleh Prypin ถ้าฉันใช้กับ urllib.request.urlopen (url) เป็นการตอบสนองเปิด (file_name, 'wb') เป็น out_file: shutil.copyfileobj (การตอบกลับ out_file) ฉันจะหารหัสสถานะ HTTP ได้อย่างไรในคำสั่ง catch จะรู้ไฟล์ไม่พบ?
Robert Achmann

146

ฉันใช้requestsแพคเกจเมื่อใดก็ตามที่ฉันต้องการสิ่งที่เกี่ยวข้องกับการร้องขอ HTTP เพราะ API ของมันง่ายมากที่จะเริ่มต้นด้วย:

ก่อนติดตั้ง requests

$ pip install requests

จากนั้นรหัส:

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

ฉันหวังว่าฉันเข้าใจคำถามถูกต้องซึ่งก็คือวิธีการดาวน์โหลดไฟล์จากเซิร์ฟเวอร์เมื่อ URL ถูกจัดเก็บในรูปแบบสตริง?

ฉันดาวน์โหลดไฟล์และบันทึกไว้ในเครื่องโดยใช้รหัสด้านล่าง:

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

สวัสดีฉันกำลังใช้รหัสประเภทเดียวกันสำหรับการดาวน์โหลดไฟล์ แต่บางครั้งฉันก็ประสบข้อยกเว้นเช่น - ตัวแปลงสัญญาณ 'charmap' ไม่สามารถเข้ารหัสอักขระ '\ u010c' ..... คุณช่วยฉันได้ไหม
Joyson

10

ที่นี่เราสามารถใช้ส่วนต่อประสานมรดกของ urllib ใน Python3:

ฟังก์ชั่นและคลาสต่อไปนี้ถูกย้ายจากโมดูล Python 2 urllib (ตรงข้ามกับ urllib2) พวกเขาอาจเลิกใช้ในบางจุดในอนาคต

ตัวอย่าง(รหัส 2 บรรทัด) :

import urllib.request

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

9

คุณสามารถใช้wgetซึ่งเป็นเครื่องมือดาวน์โหลดเชลล์ยอดนิยมสำหรับสิ่งนั้น https://pypi.python.org/pypi/wget วิธีนี้เป็นวิธีที่ง่ายที่สุดเนื่องจากไม่จำเป็นต้องเปิดไฟล์ปลายทาง นี่คือตัวอย่าง

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

ใช่คำขออย่างแน่นอนเป็นแพ็คเกจที่ยอดเยี่ยมสำหรับใช้ในบางสิ่งที่เกี่ยวข้องกับคำขอ HTTP แต่เราต้องระวังประเภทการเข้ารหัสของข้อมูลขาเข้าและด้านล่างเป็นตัวอย่างที่อธิบายความแตกต่าง


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

แรงจูงใจ

บางครั้งเราต้องการรับภาพ แต่ไม่จำเป็นต้องดาวน์โหลดเป็นไฟล์จริง

เช่นดาวน์โหลดข้อมูลและเก็บไว้ในหน่วยความจำ

ตัวอย่างเช่นถ้าฉันใช้วิธีการเรียนรู้ของเครื่องฝึกโมเดลที่สามารถจดจำรูปภาพด้วยหมายเลข (บาร์โค้ด)

เมื่อฉันไปเดอร์บางเว็บไซต์และที่มีภาพเหล่านั้นเพื่อให้ฉันสามารถใช้รูปแบบการรับรู้มัน

และฉันไม่ต้องการบันทึกรูปภาพเหล่านั้นในดิสก์ไดรฟ์ของฉัน

จากนั้นคุณสามารถลองวิธีการด้านล่างเพื่อช่วยให้คุณเก็บข้อมูลดาวน์โหลดไว้ในหน่วยความจำ

จุด

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)

โดยพื้นฐานแล้วก็เหมือนกับ @Ranvijay Kumar

ตัวอย่าง

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))
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.