Tải xuống và lưu tệp PDF với mô-đun yêu cầu Python


87

Tôi đang cố tải xuống tệp PDF từ một trang web và lưu vào đĩa. Nỗ lực của tôi không thành công với lỗi mã hóa hoặc dẫn đến các tệp PDF trống.

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

Tôi biết đó là một vấn đề codec nào đó nhưng dường như tôi không thể làm cho nó hoạt động được.

Câu trả lời:


173

Bạn nên sử dụng response.contenttrong trường hợp này:

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

Từ tài liệu :

Bạn cũng có thể truy cập nội dung phản hồi dưới dạng byte, đối với các yêu cầu không phải văn bản:

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

Vì vậy, điều đó có nghĩa là: response.texttrả về đầu ra dưới dạng đối tượng chuỗi, sử dụng nó khi bạn đang tải xuống tệp văn bản . Chẳng hạn như tệp HTML, v.v.

response.contenttrả về đầu ra dưới dạng đối tượng byte, sử dụng nó khi bạn đang tải xuống tệp nhị phân . Chẳng hạn như tệp PDF, tệp âm thanh, hình ảnh, v.v.


Bạn cũng có thể sử dụng response.rawthay thế . Tuy nhiên, hãy sử dụng nó khi tệp bạn sắp tải xuống có dung lượng lớn. Dưới đây là một ví dụ cơ bản mà bạn cũng có thể tìm thấy trong tài liệu:

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_sizelà kích thước đoạn mà bạn muốn sử dụng. Nếu bạn đặt nó là 2000, thì các yêu cầu sẽ tải xuống tệp đó theo các 2000byte đầu tiên , ghi chúng vào tệp và thực hiện việc này lặp đi lặp lại, trừ khi hoàn thành.

Vì vậy, điều này có thể tiết kiệm RAM của bạn. Nhưng tôi muốn sử dụng response.contentthay thế trong trường hợp này vì tệp của bạn nhỏ. Như bạn có thể thấy việc sử dụng response.rawrất phức tạp.


Họ hàng:


Tuyệt vời, cảm ơn bạn đã cung cấp thông tin bổ sung về response.raw.
Jim

22

Trong Python 3, tôi thấy pathlib là cách dễ nhất để làm điều này. Response.content của Request kết hợp độc đáo với write_byte của 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
Cảm ơn vì đã đăng tải điều này. Câu hỏi ban đầu là Python 2.7 nhưng tôi đã chuyển sang và bây giờ sử dụng Python 3. Tôi không biết về thư viện pathlib [mới trong phiên bản 3.4] và sẽ kết hợp nó vào các dự án hiện tại của tôi.
Jim,

Nó cung cấp 544và các tập tin bị hỏng, bất kỳ ý tưởng?
ahbon

@ahbon, ý bạn là gì?
user6481870

13

Bạn có thể sử dụng urllib:

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

Đây là một trong những tốt nhất, tbh.
Dhaval Savalia

Cái này là tốt nhất
roktim

urlretrievedựa vào cài đặt chung để xác định tiêu đề yêu cầu, khiến nó không phù hợp với một số trường hợp sử dụng.
Michael Crenshaw

5

Nói chung, điều này sẽ hoạt động trong Python3:

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

Hãy nhớ rằng urllib và urllib2 không hoạt động bình thường sau Python2.

Nếu trong một số trường hợp bí ẩn, các yêu cầu không hoạt động (xảy ra với tôi), bạn cũng có thể thử sử dụng

wget.download(url)

Có liên quan:

Đây là một giải thích / giải pháp phù hợp để tìm và tải xuống tất cả các tệp pdf trên một trang web:

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


2

Xin lưu ý rằng tôi là người mới bắt đầu. Nếu giải pháp của tôi sai, vui lòng sửa và / hoặc cho tôi biết. Tôi cũng có thể học một cái gì đó mới.

Giải pháp của tôi:

Thay đổi downloadPath cho phù hợp với nơi bạn muốn lưu tệp của mình. Hãy thoải mái sử dụng đường dẫn tuyệt đối cho việc sử dụng của bạn.

Lưu bên dưới dưới dạng downloadFile.py.

Sử dụng: python downloadFile.py url-of-the-file-to-download new-file-name.extension

Hãy nhớ thêm một phần mở rộng!

Ví dụ sử dụng: 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, cảm ơn vì câu trả lời của bạn. Tôi là một người mới làm quen với Python khi lần đầu tiên tôi đăng câu hỏi này. Bây giờ tôi biết ngôn ngữ rất tốt. Trường hợp sử dụng của bạn khi viết một tập lệnh Python để tải xuống tệp từ một dòng lệnh có thể được bao phủ bởi các tiện ích như wget hoặc curl. Ngoài ra, hàm downloadFile của bạn như đã đăng dường như tự gọi nó. Bạn có ý định thụt lề khối mã thứ hai không? Trong stackoverflow, bạn có thể sửa lỗi đó bằng cách cắt bỏ nó. Tôi cũng muốn đề nghị bạn xem qua thư viện argparse của Python. Bạn có thể sử dụng nó để tạo các tiện ích dòng lệnh đẹp mắt. Nó sẽ chăm sóc các thông số cho bạn.
Jim,

Tôi thực sự thích việc bạn sử dụng trình quản lý ngữ cảnh (với mở ... dưới dạng tệp :, v.v.) để xử lý việc ghi tệp. Mã của bạn được viết gọn gàng. Bạn đang trên con đường tốt để học Python. Chúc may mắn!
Jim,

1
Cảm ơn vì đã trả lời, @Jim! Tôi đã chỉnh sửa bài đăng, và thực sự tôi không có ý định "thụt lề": D phần chính của chương trình. Cảm ơn lời khuyên của bạn! :)
Duck Ling

-5

Về câu trả lời của Kevin để viết trong một thư mục tmp, nó sẽ như thế này:

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

anh ấy quên .trước địa chỉ và tất nhiên thư mục của bạn tmplẽ ra đã được tạo rồi


5
1- Kevin không nghĩ ra ý tưởng để viết vào tmp, nó giống như trong câu hỏi của OP. 2- các /tmpthư mục là tmp trong các hệ thống Unix, tọa lạc tại /tmp, không có.
realUser404
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.