Pobierz plik z Internetu w Python 3


332

Tworzę program, który pobierze plik .jar (java) z serwera WWW, czytając adres URL podany w pliku .jad tej samej gry / aplikacji. Używam Python 3.2.1

Udało mi się wyodrębnić adres URL pliku JAR z pliku JAD (każdy plik JAD zawiera adres URL do pliku JAR), ale jak można sobie wyobrazić, wyodrębniona wartość to ciąg typu ().

Oto odpowiednia funkcja:

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

downloadFile(URL_from_file)

Jednak zawsze pojawia się błąd informujący, że typ w powyższej funkcji musi być bajtami, a nie ciągiem. Próbowałem użyć URL.encode ('utf-8'), a także bajtów (URL, kodowanie = 'utf-8'), ale zawsze otrzymywałbym ten sam lub podobny błąd.

Więc w zasadzie moje pytanie brzmi: jak pobrać plik z serwera, gdy adres URL jest przechowywany w typie ciągu?


4
@alvas, Nagroda za to? Odpowiadający jest nadal (i całkiem) aktywny na SO. Dlaczego nie dodać komentarza i zapytać?
Bhargav Rao

8
Dobra odpowiedź, która trwa próbę czasu, jest warta uznania. Powinniśmy też zacząć to robić dla wielu innych pytań, aby sprawdzić, czy odpowiedzi są dziś aktualne. Zwłaszcza, gdy sortowanie odpowiedzi SO jest dość szalone, czasem przestarzała, a nawet najgorsza odpowiedź trafia na szczyt.
alvas

Odpowiedzi:


646

Jeśli chcesz uzyskać zawartość strony internetowej w zmiennej, wystarczy readodpowiedź 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

Najłatwiejszym sposobem pobrania i zapisania pliku jest użycie urllib.request.urlretrievefunkcji:

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)

Pamiętaj jednak, że urlretrievejest to uważane za dziedzictwo i może stać się przestarzałe (choć nie jestem pewien, dlaczego).

Zatem najbardziej poprawnym sposobem jest użycie urllib.request.urlopenfunkcji do zwrócenia obiektu podobnego do pliku, który reprezentuje odpowiedź HTTP i skopiowanie go do prawdziwego pliku za pomocą 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)

Jeśli wydaje się to zbyt skomplikowane, możesz uprościć i zapisać cały plik do pobrania w bytesobiekcie, a następnie zapisać go w pliku. Ale działa to dobrze tylko w przypadku małych plików.

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)

Możliwe jest wyodrębnianie .gz(i być może innych formatów) skompresowanych danych w locie, ale taka operacja prawdopodobnie wymaga, aby serwer HTTP obsługiwał losowy dostęp do pliku.

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')zamiast utf-8kodowania na Content-Type
twardo

2
@OlehPrypin Dlaczego outfile.write(data)działa dobrze tylko w przypadku małych plików?
Startec

„urlretrieve jest uważane za dziedzictwo i może stać się przestarzałe” skąd pomysł?
Corey Goldberg,

13
@Corey: Bezpośrednio z dokumentacji : „21.6.24. Starszy interfejs Poniższe funkcje i klasy są przeniesione z urllib modułu Pythona 2 (w przeciwieństwie do urllib2). W pewnym momencie mogą stać się przestarzałe.” ... i zgadzam się co do „nie wiem dlaczego”
Oleha

@Oleh Prypin, jeśli używam z urllib.request.urlopen (url) jako odpowiedz, otwórz (nazwa_pliku, 'wb') jako plik_wyjściowy: shutil.copyfileobj (odpowiedź, plik_wyjściowy), to jak mogę znaleźć kod statusu HTTP w instrukcji catch wiedzieć, że plik nie został znaleziony?
Robert Achmann

145

Korzystam z requestspakietu, gdy chcę coś związanego z żądaniami HTTP, ponieważ jego interfejs API jest bardzo łatwy do uruchomienia od:

najpierw zainstaluj requests

$ pip install requests

następnie 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

Mam nadzieję, że dobrze zrozumiałem pytanie: jak pobrać plik z serwera, gdy adres URL jest zapisany jako ciąg?

Pobieram pliki i zapisuję je lokalnie za pomocą poniższego kodu:

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

cześć, używam również tego samego typu kodu do pobierania pliku, ale od pewnego czasu mam wyjątek, taki jak - kodek „charmap” nie może zakodować znaku „\ u010c” ..... możesz mi w tym pomóc
Joyson

10

Tutaj możemy użyć starszego interfejsu urllib w Python3:

Następujące funkcje i klasy są przeniesione z urllib modułu Python 2 (w przeciwieństwie do urllib2). W pewnym momencie mogą stać się przestarzałe.

Przykład (kod 2-liniowy) :

import urllib.request

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

9

Możesz do tego użyć wget, popularnego narzędzia do pobierania powłoki. https://pypi.python.org/pypi/wget To będzie najprostsza metoda, ponieważ nie trzeba otwierać pliku docelowego. Oto przykład.

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

Tak, zdecydowanie żądania to świetny pakiet do użycia w czymś związanym z żądaniami HTTP. ale musimy uważać na typ kodowania przychodzących danych, a poniżej znajduje się przykład wyjaśniający różnicę


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

Motywacja

Czasami chcemy uzyskać zdjęcie, ale nie musimy pobierać go do prawdziwych plików,

tzn. pobierz dane i zachowaj je w pamięci.

Na przykład, jeśli korzystam z metody uczenia maszynowego, wytrenuj model, który rozpozna obraz z numerem (kod kreskowy).

Gdy przeglądam niektóre witryny internetowe z tymi obrazami, dzięki czemu mogę rozpoznać model,

i nie chcę zapisywać tych zdjęć na moim dysku,

możesz wypróbować poniższą metodę, która pomoże ci zachować dane w pamięci.

Zwrotnica

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)

w zasadzie jest jak @Ranvijay Kumar

Przykład

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))
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.