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.

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:


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)

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

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


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

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


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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.