Scarica e salva il file PDF con il modulo delle richieste Python


87

Sto cercando di scaricare un file PDF da un sito Web e di salvarlo su disco. I miei tentativi falliscono con errori di codifica o risultano in PDF vuoti.

In [1]: import requests

In [2]: url = 'http://www.hrecos.org//images/Data/forweb/HRTVBSH.Metadata.pdf'

In [3]: response = requests.get(url)

In [4]: with open('/tmp/metadata.pdf', 'wb') as f:
   ...:     f.write(response.text)
---------------------------------------------------------------------------
UnicodeEncodeError                        Traceback (most recent call last)
<ipython-input-4-4be915a4f032> in <module>()
      1 with open('/tmp/metadata.pdf', 'wb') as f:
----> 2     f.write(response.text)
      3 

UnicodeEncodeError: 'ascii' codec can't encode characters in position 11-14: ordinal not in range(128)

In [5]: import codecs

In [6]: with codecs.open('/tmp/metadata.pdf', 'wb', encoding='utf8') as f:
   ...:     f.write(response.text)
   ...: 

So che è un problema di codec di qualche tipo, ma non riesco a farlo funzionare.

Risposte:


176

Dovresti usare response.contentin questo caso:

with open('/tmp/metadata.pdf', 'wb') as f:
    f.write(response.content)

Dal documento :

È inoltre possibile accedere al corpo della risposta come byte, per le richieste non di testo:

>>> r.content
b'[{"repository":{"open_issues":0,"url":"https://github.com/...

Quindi questo significa: response.textrestituisci l'output come oggetto stringa, usalo quando scarichi un file di testo . Come file HTML, ecc.

E response.contentrestituisci l'output come oggetto byte, usalo quando scarichi un file binario . Come file PDF, file audio, immagini, ecc.


Puoi anche usare response.rawinvece . Tuttavia, utilizzalo quando il file che stai per scaricare è di grandi dimensioni. Di seguito è riportato un esempio di base che puoi trovare anche nel documento:

import requests

url = 'http://www.hrecos.org//images/Data/forweb/HRTVBSH.Metadata.pdf'
r = requests.get(url, stream=True)

with open('/tmp/metadata.pdf', 'wb') as fd:
    for chunk in r.iter_content(chunk_size):
        fd.write(chunk)

chunk_sizeè la dimensione del blocco che desideri utilizzare. Se lo imposti come 2000, le richieste scaricheranno quel file i primi 2000byte, li scriveranno nel file e lo faranno ancora, ancora e ancora, a meno che non sia finito.

Quindi questo può salvare la tua RAM. Ma preferisco usarlo response.contentin questo caso poiché il tuo file è piccolo. Come puoi vedere l'uso response.rawè complesso.


Riguarda:


Fantastico, grazie per le informazioni aggiuntive su response.raw.
Jim

23

In Python 3, trovo che pathlib sia il modo più semplice per farlo. Request's response.content si sposa bene con i write_bytes di pathlib.

from pathlib import Path
import requests
filename = Path('metadata.pdf')
url = 'http://www.hrecos.org//images/Data/forweb/HRTVBSH.Metadata.pdf'
response = requests.get(url)
filename.write_bytes(response.content)

1
grazie per aver postato questo. La domanda originale era Python 2.7 ma sono andato avanti e ora uso Python 3. Non sapevo della libreria pathlib [nuova nella versione 3.4] e la incorporerò nei miei progetti attuali.
Jim

544e il file è rotto, qualche idea?
ahbon

@ahbon, cosa intendi?
user6481870

14

Puoi usare urllib:

import urllib.request
urllib.request.urlretrieve(url, "filename.pdf")

1
Questo è il migliore, tbh.
Dhaval Savalia

Questo è il migliore
roktim

1
urlretrievesi basa su impostazioni globali per determinare le intestazioni delle richieste, rendendolo inadatto per alcuni casi d'uso.
Michael Crenshaw

5

In generale, questo dovrebbe funzionare in Python3:

import urllib.request 
..
urllib.request.get(url)

Ricorda che urllib e urllib2 non funzionano correttamente dopo Python2.

Se in alcuni casi misteriosi le richieste non funzionano (è successo con me), puoi anche provare a utilizzare

wget.download(url)

Relazionato:

Ecco una spiegazione / soluzione decente per trovare e scaricare tutti i file pdf su una pagina web:

https://medium.com/@dementorwriter/notesdownloader-use-web-scraping-to-download-all-pdfs-with-python-511ea9f55e48


2

Tieni presente che sono un principiante. Se la mia soluzione è sbagliata, sentiti libero di correggerla e / o fammelo sapere. Potrei anche imparare qualcosa di nuovo.

La mia soluzione:

Modificare il downloadPath di conseguenza nella posizione in cui si desidera salvare il file. Sentiti libero di usare anche il percorso assoluto per il tuo utilizzo.

Salva quanto segue come downloadFile.py.

Utilizzo: python downloadFile.py url-of-the-file-to-download new-file-name.extension

Ricordati di aggiungere un'estensione!

Utilizzo di esempio: python downloadFile.py http://www.google.co.uk google.html

import requests
import sys
import os

def downloadFile(url, fileName):
    with open(fileName, "wb") as file:
        response = requests.get(url)
        file.write(response.content)


scriptPath = sys.path[0]
downloadPath = os.path.join(scriptPath, '../Downloads/')
url = sys.argv[1]
fileName = sys.argv[2]      
print('path of the script: ' + scriptPath)
print('downloading file to: ' + downloadPath)
downloadFile(url, downloadPath + fileName)
print('file downloaded...')
print('exiting program...')

Pawel, grazie per la tua risposta. Ero un principiante di Python quando ho postato per la prima volta questa domanda. Adesso conosco molto bene la lingua. Il tuo caso d'uso di scrivere uno script Python per scaricare un file da una riga di comando può essere coperto da utilità come wget o curl. Inoltre, la funzione downloadFile come pubblicata sembra chiamare se stessa. Avevi intenzione di indentare il secondo blocco di codice? In StackOverflow puoi correggerlo superandolo. Vorrei anche suggerirti di dare un'occhiata alla libreria argparse di Python. Puoi usarlo per creare delle belle utilità da riga di comando. Si prenderà cura dei parametri per te.
Jim

Mi piace il tuo utilizzo di un gestore di contesto (con open ... as file :, ecc) per gestire la scrittura del file. Il tuo codice è scritto in modo ordinato. Sei sulla buona strada per imparare Python. In bocca al lupo!
Jim

1
Grazie per la risposta, @ Jim! Ho modificato il post, e infatti non avevo "intenzione di rientrare": D la parte principale del programma. Grazie per i tuoi consigli! :)
Duck Ling

-4

per quanto riguarda la risposta di Kevin da scrivere in una cartella tmp, dovrebbe essere così:

with open('./tmp/metadata.pdf', 'wb') as f:
    f.write(response.content)

si è dimenticato .prima dell'indirizzo e ovviamente la tua cartella tmpavrebbe dovuto essere già stata creata


5
1- Kevin non ha avuto l'idea di scrivere tmp, era come nella domanda di OP. 2- la /tmpdirectory è il tmp nei sistemi Unix, che si trova in /tmp, no.
realUser404
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.