ดาวน์โหลดและบันทึกไฟล์ 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)
   ...: 

ฉันรู้ว่ามันเป็นปัญหาบางอย่างของตัวแปลงสัญญาณ แต่ดูเหมือนจะไม่สามารถใช้งานได้

คำตอบ:


176

คุณควรใช้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ไบต์แรกเขียนลงในไฟล์และทำสิ่งนี้ซ้ำแล้วซ้ำอีกเว้นแต่จะเสร็จสิ้น

สิ่งนี้สามารถประหยัด RAM ของคุณได้ แต่ฉันต้องการใช้response.contentแทนในกรณีนี้เนื่องจากไฟล์ของคุณมีขนาดเล็ก อย่างที่คุณเห็นการใช้งานresponse.rawมีความซับซ้อน


เกี่ยวข้อง:


ขอขอบคุณสำหรับข้อมูลเพิ่มเติมเกี่ยวกับ response.raw
จิม

23

ใน Python 3 ฉันพบว่า pathlib เป็นวิธีที่ง่ายที่สุดในการทำเช่นนี้ การตอบสนองของคำขอเนื้อหาเข้ากันได้ดีกับ 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)

1
ขอขอบคุณสำหรับการโพสต์นี้. คำถามเดิมคือ Python 2.7 แต่ฉันได้ย้ายไปและตอนนี้ใช้ Python 3 ฉันไม่รู้เกี่ยวกับไลบรารี pathlib [ใหม่ในเวอร์ชัน 3.4] และจะรวมเข้ากับโปรเจ็กต์ปัจจุบันของฉัน
จิม

มันให้544และไฟล์เสียความคิดใด ๆ ?
ahbon

@ahbon คุณหมายถึงอะไร?
user6481870

14

คุณสามารถใช้ urllib:

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

1
นี่คือสิ่งที่ดีที่สุด tbh
Dhaval Savalia

อันนี้ดีที่สุด
roktim

1
urlretrieveอาศัยการตั้งค่าส่วนกลางเพื่อกำหนดส่วนหัวของคำขอทำให้ไม่เหมาะสำหรับการใช้งานบางกรณี
Michael Crenshaw

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


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

Pawel ขอบคุณสำหรับคำตอบ ฉันเป็นมือใหม่ Python เมื่อฉันโพสต์คำถามนี้ครั้งแรก ตอนนี้ฉันรู้ภาษาเป็นอย่างดี กรณีการใช้งานของคุณในการเขียนสคริปต์ Python เพื่อดาวน์โหลดไฟล์จากบรรทัดคำสั่งสามารถครอบคลุมโดยยูทิลิตี้เช่น wget หรือ curl นอกจากนี้ฟังก์ชัน downloadFile ของคุณตามที่โพสต์ดูเหมือนจะเรียกตัวเอง คุณตั้งใจจะเยื้องบล็อกที่สองของโค้ดหรือไม่ ใน stackoverflow คุณสามารถแก้ไขได้โดยการลบฟันออก ฉันอยากแนะนำให้คุณดูห้องสมุด argparse ของ Python คุณสามารถใช้เพื่อสร้างยูทิลิตี้บรรทัดคำสั่งที่ดี มันจะดูแลพารามิเตอร์ให้คุณ
จิม

ฉันชอบที่คุณใช้ตัวจัดการบริบท (เปิด ... เป็นไฟล์: ฯลฯ ) เพื่อจัดการการเขียนไฟล์ รหัสของคุณเขียนอย่างเรียบร้อย คุณอยู่ในเส้นทางที่ดีในการเรียนรู้ Python โชคดี!
จิม

1
ขอบคุณสำหรับคำตอบ @Jim! ฉันได้แก้ไขโพสต์แล้วและฉันไม่ได้ "ตั้งใจจะเยื้อง": D ส่วนหลักของโปรแกรม ขอบคุณสำหรับคำแนะนำ! :)
Duck Ling

-4

เกี่ยวกับคำตอบของ Kevin ในการเขียนในโฟลเดอร์tmpควรเป็นดังนี้:

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

เขาลืม.ก่อนที่อยู่และแน่นอนว่าโฟลเดอร์ของคุณtmpควรถูกสร้างขึ้นแล้ว


5
1- เควินไม่ได้คิดที่จะเขียนtmpมันเหมือนกับคำถามของ OP 2- /tmpไดเร็กทอรีคือ tmp ในระบบ Unix ซึ่งอยู่ที่/tmpno.
realUser404
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.