Descargue y guarde el archivo PDF con el módulo de solicitudes de Python

86

Estoy intentando descargar un archivo PDF de un sitio web y guardarlo en el disco. Mis intentos fallan con errores de codificación o dan como resultado archivos PDF en blanco.

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)
   ...: 

Sé que es un problema de códec de algún tipo, pero parece que no puedo hacer que funcione.

Jim
fuente

Respuestas:

172

Deberías usar response.contenten este caso:

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

Del documento :

También puede acceder al cuerpo de la respuesta en bytes, para solicitudes que no sean de texto:

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

Eso significa: response.textdevuelva la salida como un objeto de cadena, úselo cuando esté descargando un archivo de texto . Como archivos HTML, etc.

Y response.contentdevuelva la salida como un objeto de bytes, utilícelo cuando esté descargando un archivo binario . Como archivos PDF, archivos de audio, imágenes, etc.


También puede usar response.rawen su lugar . Sin embargo, utilícelo cuando el archivo que está a punto de descargar sea grande. A continuación se muestra un ejemplo básico que también puede encontrar en el 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_sizees el tamaño del fragmento que desea utilizar. Si lo configura como 2000, las solicitudes descargarán los primeros 2000bytes de ese archivo , los escribirán en el archivo y volverán a hacer esto, una y otra vez, a menos que haya terminado.

Entonces esto puede ahorrar tu RAM. Pero preferiría usarlo response.contenten este caso ya que su archivo es pequeño. Como puede ver, el uso response.rawes complejo.


Se relaciona:

Casimir Crystal
fuente
Genial, gracias por la información adicional sobre response.raw.
Jim
22

En Python 3, encuentro que pathlib es la forma más fácil de hacer esto. Request's response.content se casa muy bien con write_bytes de 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)
usuario6481870
fuente
1
Gracias por publicar esto. La pregunta original era Python 2.7, pero he seguido adelante y ahora uso Python 3. No sabía nada de la biblioteca pathlib [nueva en la versión 3.4] y la incorporaré a mis proyectos actuales.
Jim
Da 544y el archivo está roto, ¿alguna idea?
Ahbon
@ahbon, ¿qué quieres decir?
user6481870
13

Puede utilizar urllib:

import urllib.request
urllib.request.urlretrieve(url, "filename.pdf")
jugi
fuente
Este es el mejor, tbh.
Dhaval Savalia
Este es el mejor
roktim
urlretrievese basa en la configuración global para determinar los encabezados de las solicitudes, por lo que no es adecuado para algunos casos de uso.
Michael Crenshaw
5

Generalmente, esto debería funcionar en Python3:

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

Recuerde que urllib y urllib2 no funcionan correctamente después de Python2.

Si en algunos casos misteriosos las solicitudes no funcionan (sucedió conmigo), también puede intentar usar

wget.download(url)

Relacionado:

Aquí hay una explicación / solución decente para encontrar y descargar todos los archivos pdf en una página web:

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

x89
fuente
2

Tenga en cuenta que soy un principiante. Si mi solución es incorrecta, no dude en corregirla y / o informarme. También puedo aprender algo nuevo.

Mi solución:

Cambie la ruta de descarga en consecuencia con el lugar donde desea que se guarde su archivo. Siéntase libre de usar la ruta absoluta también para su uso.

Guarde lo siguiente como downloadFile.py.

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

¡Recuerde agregar una extensión!

Uso de ejemplo: 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...')
Ling de pato
fuente
Pawel, gracias por tu respuesta. Era un novato en Python cuando publiqué esta pregunta por primera vez. Ahora conozco muy bien el idioma. Su caso de uso de escribir una secuencia de comandos Python para descargar un archivo desde una línea de comando puede ser cubierto por utilidades como wget o curl. Además, su función downloadFile como se publicó parece llamarse a sí misma. ¿Tenía la intención de sangrar el segundo bloque de código? En stackoverflow, puede corregir eso superando eso. También me gustaría sugerirle que eche un vistazo a la biblioteca argparse de Python. Puede usarlo para hacer buenas utilidades de línea de comandos. Se encargará de los parámetros por usted.
Jim
Me gusta su uso de un administrador de contexto (con abrir ... como archivo :, etc.) para manejar la escritura del archivo. Su código está cuidadosamente escrito. Estás en un buen camino para aprender Python. ¡Buena suerte!
Jim
1
¡Gracias por la respuesta, @Jim! He editado la publicación y, de hecho, no tenía "la intención de sangrar": D la parte principal del programa. ¡Gracias por tus consejos! :)
Duck Ling
-5

con respecto a la respuesta de Kevin para escribir en una carpeta tmp, debería ser así:

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

se olvidó .antes de la dirección y, por supuesto, su carpeta ya tmpdebería haber sido creada

Nima Sajedi
fuente
5
1- A Kevin no se le ocurrió la idea de escribir tmp, fue como en la pregunta de OP. 2- el /tmpdirectorio es el tmp en sistemas Unix, ubicado en /tmp, no.
realUser404