Загрузите и сохраните файл PDF с модулем запросов Python

87

Я пытаюсь загрузить файл PDF с веб-сайта и сохранить его на диск. Мои попытки либо терпят неудачу с ошибками кодирования, либо приводят к пустым PDF-файлам.

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

Я знаю, что это какая-то проблема с кодеком, но я не могу заставить ее работать.

Джим
источник

Ответы:

173

response.contentВ этом случае следует использовать :

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

Из документа :

Вы также можете получить доступ к телу ответа в байтах для нетекстовых запросов:

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

Это означает: response.textвернуть вывод в виде строкового объекта, использовать его при загрузке текстового файла . Например, файл HTML и т. Д.

И response.contentверните результат в виде байтового объекта, используйте его при загрузке двоичного файла . Например, файл PDF, аудиофайл, изображение и т. Д.


Вы также можете использовать response.rawвместо него . Однако используйте его, когда файл, который вы собираетесь загрузить, большой. Ниже приведен базовый пример, который вы также можете найти в документе:

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- это размер блока, который вы хотите использовать. Если вы установите его как 2000, то запросы будут загружать в этот файл первые 2000байты, записывать их в файл и делать это снова, снова и снова, если это не будет завершено.

Так что это может сэкономить вашу оперативную память. Но я бы предпочел использовать response.contentвместо этого в этом случае, поскольку ваш файл небольшой. Как видите, использование response.rawсложное.


Относится:

Казимир Кристалл
источник
Круто, спасибо за дополнительную информацию о response.raw.
Джим
22

В Python 3 я считаю, что pathlib - самый простой способ сделать это. Response.content запроса прекрасно сочетается с write_bytes 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)
user6481870
источник
1
Спасибо за публикацию. Первоначальный вопрос был Python 2.7, но я перешел и теперь использую Python 3. Я не знал о библиотеке pathlib [новая в версии 3.4] и буду включать ее в свои текущие проекты.
Джим
Он дает 544и файл битый, есть идеи?
ahbon
@ahbon, что ты имеешь в виду?
user6481870
13

Вы можете использовать urllib:

import urllib.request
urllib.request.urlretrieve(url, "filename.pdf")
джуги
источник
Это лучший, tbh.
Дхавал Савалия
Этот лучший
roktim
urlretrieveполагается на глобальные настройки для определения заголовков запросов, что делает его непригодным для некоторых случаев использования.
Майкл Креншоу,
5

Как правило, это должно работать в Python3:

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

Помните, что urllib и urllib2 не работают должным образом после Python2.

Если в каких-то загадочных случаях запросы не работают (случилось со мной), вы также можете попробовать использовать

wget.download(url)

Связанный:

Вот достойное объяснение / решение для поиска и загрузки всех файлов pdf на веб-странице:

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

x89
источник
2

Обратите внимание, я новичок. Если мое решение неверно, пожалуйста, исправьте и / или дайте мне знать. Я тоже могу узнать что-то новое.

Мое решение:

Измените downloadPath в соответствии с тем, где вы хотите сохранить файл. Не стесняйтесь использовать абсолютный путь для вашего использования.

Сохраните ниже как downloadFile.py.

Применение: python downloadFile.py url-of-the-file-to-download new-file-name.extension

Не забудьте добавить расширение!

Пример использования: 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...')
Утка Линг
источник
Павел, спасибо за ответ. Я был новичком в Python, когда впервые опубликовал этот вопрос. Теперь я очень хорошо знаю язык. Ваш сценарий использования Python для загрузки файла из командной строки может быть покрыт такими утилитами, как wget или curl. Кроме того, ваша функция downloadFile, как опубликовано, похоже, вызывает сама себя. Вы намеревались сделать отступ во втором блоке кода? В stackoverflow вы можете исправить это, подавив это. Я также хотел бы предложить вам взглянуть на библиотеку Python argparse. Вы можете использовать его для создания хороших утилит командной строки. Он позаботится о параметрах за вас.
Джим
Мне нравится, что вы используете диспетчер контекста (с open ... as file: и т. Д.) Для обработки записи файла. Ваш код аккуратно написан. Вы на правильном пути к изучению Python. Удачи!
Джим
1
Спасибо за ответ, @Jim! Пост редактировал, да и вообще "отступать" не собирался: D основную часть программы. Спасибо за советы! :)
Duck Ling
-5

tmpЧто касается ответа Кевина, который нужно написать в папке , он должен быть таким:

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

он забыл .перед адресом и, конечно, ваша папка tmpдолжна была быть уже создана

Нима Саджеди
источник
5
1- Кевину не пришла в голову идея писать tmp, это было как в вопросе OP. 2- /tmpкаталог - это tmp в системах Unix, расположенный по адресу /tmp, no.
realUser404