Baixe e salve o arquivo PDF com o módulo de solicitações Python


86

Estou tentando baixar um arquivo PDF de um site e salvá-lo no disco. Minhas tentativas falham com erros de codificação ou resultam em PDFs em branco.

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

Eu sei que é algum tipo de problema de codec, mas não consigo fazê-lo funcionar.

Respostas:


172

Você deve usar response.contentneste caso:

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

Do documento :

Você também pode acessar o corpo da resposta como bytes, para solicitações não textuais:

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

Então, isso significa: response.textretornar a saída como um objeto de string, usá-lo quando estiver baixando um arquivo de texto . Como arquivo HTML, etc.

E response.contentretornar a saída como objeto bytes, use-o quando estiver baixando um arquivo binário . Como arquivo PDF, arquivo de áudio, imagem, etc.


Você também pode usar response.rawem vez . No entanto, use-o quando o arquivo que você está prestes a baixar for grande. Abaixo está um exemplo básico que você também pode encontrar no 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é o tamanho do bloco que você deseja usar. Se você definir como 2000, as solicitações irão baixar os primeiros 2000bytes desse arquivo , gravá-los no arquivo e fazer isso de novo e de novo, a menos que tenha terminado.

Portanto, isso pode salvar sua RAM. Mas eu prefiro usar response.contentnesse caso, já que seu arquivo é pequeno. Como você pode ver, o uso response.rawé complexo.


Relacionados:


Legal, obrigado pelas informações adicionais sobre response.raw.
Jim

22

No Python 3, acho que o pathlib é a maneira mais fácil de fazer isso. O response.content do Request combina muito bem com o write_bytes do 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
Obrigado por postar isso. A pergunta original era Python 2.7, mas eu mudei e agora uso o Python 3. Eu não sabia sobre a biblioteca pathlib [nova na versão 3.4] e vou incorporá-la em meus projetos atuais.
Jim

544e o arquivo está quebrado, alguma ideia?
ahbon

@ahbon, o que você quer dizer?
user6481870

13

Você pode usar o urllib:

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

Este é o melhor, tbh.
Dhaval Savalia

Este é o melhor
roktim

urlretrievedepende de configurações globais para determinar cabeçalhos de solicitação, tornando-o inadequado para alguns casos de uso.
Michael Crenshaw,

5

Geralmente, isso deve funcionar no Python3:

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

Lembre-se de que urllib e urllib2 não funcionam corretamente após Python2.

Se em alguns casos misteriosos as solicitações não funcionarem (aconteceu comigo), você também pode tentar usar

wget.download(url)

Relacionado:

Aqui está uma explicação / solução decente para encontrar e baixar todos os arquivos PDF em uma página da web:

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


2

Observe que sou um iniciante. Se Minha solução estiver errada, sinta-se à vontade para corrigir e / ou me avise. Posso aprender algo novo também.

Minha solução:

Altere o downloadPath de acordo com o local onde deseja que o arquivo seja salvo. Sinta-se à vontade para usar o caminho absoluto também para seu uso.

Salve o abaixo como downloadFile.py.

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

Lembre-se de adicionar uma extensão!

Exemplo de uso: 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, obrigado por sua resposta. Eu era um novato em Python quando postei esta pergunta pela primeira vez. Agora conheço muito bem a língua. Seu caso de uso de escrever um script Python para baixar um arquivo de uma linha de comando pode ser coberto por utilitários como wget ou curl. Além disso, sua função downloadFile conforme postada parece chamar a si mesma. Você pretendia recuar o segundo bloco de código? No stackoverflow, você pode corrigir isso superando-o. Também gostaria de sugerir que você dê uma olhada na biblioteca argparse do Python. Você pode usá-lo para fazer bons utilitários de linha de comando. Ele cuidará dos parâmetros para você.
Jim

Eu gosto do uso de um gerenciador de contexto (com abrir ... como arquivo :, etc) para lidar com a gravação do arquivo. Seu código está bem escrito. Você está no caminho certo para aprender Python. Boa sorte!
Jim

1
Obrigado pela resposta, @Jim! Eu editei a postagem e, na verdade, não "pretendia recuar": D a parte principal do programa. Obrigado por seus conselhos! :)
Duck Ling

-5

em relação à resposta de Kevin para escrever em uma pasta tmp, deve ser assim:

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

ele esqueceu .antes do endereço e claro que sua pasta tmpjá deveria ter sido criada


5
1- O Kevin não teve a ideia de escrever tmp, foi como na pergunta da OP. 2- o /tmpdiretório é o tmp em sistemas Unix, localizado em /tmp, no.
realUser404
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.