Pobierz i zapisz plik PDF z modułem żądań Pythona


86

Próbuję pobrać plik PDF ze strony internetowej i zapisać go na dysku. Moje próby kończą się niepowodzeniem z błędami kodowania lub kończą się pustymi plikami 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)
   ...: 

Wiem, że to jakiś problem z kodekiem, ale nie mogę go uruchomić.

Odpowiedzi:


172

Powinieneś użyć response.contentw tym przypadku:

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

Z dokumentu :

Możesz również uzyskać dostęp do treści odpowiedzi jako bajty, dla żądań nietekstowych:

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

To znaczy: response.textzwróć dane wyjściowe jako obiekt łańcuchowy, użyj go podczas pobierania pliku tekstowego . Takich jak plik HTML itp.

I response.contentzwróć dane wyjściowe jako obiekt bajtów, użyj go podczas pobierania pliku binarnego . Takich jak plik PDF, plik audio, obraz itp.


Możesz także użyć response.rawzamiast tego . Jednak użyj go, gdy plik, który chcesz pobrać, jest duży. Poniżej znajduje się podstawowy przykład, który można również znaleźć w dokumencie:

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_sizeto rozmiar fragmentu, którego chcesz użyć. Jeśli ustawisz ją jako 2000, żądania będą pobierać pierwsze 2000bajty tego pliku , zapisywać je do pliku i robić to ponownie, wielokrotnie, chyba że zakończy się.

Więc to może zaoszczędzić pamięć RAM. Ale wolałbym użyć response.contentzamiast tego w tym przypadku, ponieważ twój plik jest mały. Jak widać, użycie response.rawjest skomplikowane.


Dotyczy:


Super, dziękuję za dodatkowe informacje o response.raw.
Jim

22

W Pythonie 3 uważam, że pathlib jest najłatwiejszym sposobem na zrobienie tego. Request's response.content ładnie łączy się z wartością 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
Dziękuję za opublikowanie tego. Pierwotne pytanie brzmiało Python 2.7, ale przeszedłem dalej i teraz używam Pythona 3. Nie wiedziałem o bibliotece pathlib [nowość w wersji 3.4] i włączę ją do moich obecnych projektów.
Jim

Daje 544i plik jest uszkodzony, jakieś pomysły?
ahbon

@ahbon, co masz na myśli?
user6481870

13

Możesz użyć urllib:

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

To jest najlepsze, tbh.
Dhaval Savalia

Ten jest najlepszy
roktim

urlretrieveopiera się na ustawieniach globalnych w celu określenia nagłówków żądań, co czyni go nieodpowiednim w niektórych przypadkach użycia.
Michael Crenshaw

5

Generalnie powinno to działać w Pythonie3:

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

Pamiętaj, że urllib i urllib2 nie działają poprawnie po Pythonie2.

Jeśli w jakichś tajemniczych przypadkach prośby nie działają (zdarzyło mi się), możesz również spróbować użyć

wget.download(url)

Związane z:

Oto przyzwoite wyjaśnienie / rozwiązanie, aby znaleźć i pobrać wszystkie pliki PDF na stronie internetowej:

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


2

Pamiętaj, że jestem początkującym. Jeśli moje rozwiązanie jest błędne, popraw je i / lub daj mi znać. Mogę się też nauczyć czegoś nowego.

Moje rozwiązanie:

Zmień ścieżkę pobierania odpowiednio do miejsca, w którym chcesz zapisać plik. Nie krępuj się używać ścieżki bezwzględnej również do użytku.

Zapisz poniższy plik jako downloadFile.py.

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

Pamiętaj, aby dodać rozszerzenie!

Przykładowe użycie: 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 dziekuje za odpowiedz. Byłem nowicjuszem w Pythonie, kiedy po raz pierwszy opublikowałem to pytanie. Teraz znam bardzo dobrze język. Twój przypadek użycia pisania skryptu Pythona w celu pobrania pliku z wiersza poleceń może być uwzględniony przez narzędzia takie jak wget lub curl. Wydaje się również, że twoja funkcja downloadFile, jak została opublikowana, wywołuje samą siebie. Czy zamierzałeś wciąć drugi blok kodu? W przepełnieniu stosu możesz to naprawić, pokonując to. Chciałbym również zasugerować, abyś zajrzał do biblioteki argparse w języku Python. Możesz go użyć do stworzenia ładnych narzędzi wiersza poleceń. Zadba o parametry za Ciebie.
Jim

Podoba mi się, że używasz menedżera kontekstu (z open ... as file: itd.) Do obsługi zapisu pliku. Twój kod jest starannie napisany. Jesteś na dobrej drodze do nauki języka Python. Powodzenia!
Jim

1
Dzięki za odpowiedź, @Jim! Zredagowałem post i rzeczywiście nie zamierzałem "wciskać": D głównej części programu. Dzięki za rady! :)
Duck Ling

-5

jeśli chodzi o odpowiedź Kevina na pisanie w folderze tmp, powinno wyglądać tak:

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

Zapomniał .wcześniej o adresie i oczywiście twój folder tmppowinien już zostać utworzony


5
1- Kevin nie wpadł na pomysł, żeby napisać tmp, to było jak w pytaniu OP. 2- /tmpkatalog to tmp w systemach uniksowych, zlokalizowany pod adresem /tmp, no.
realUser404
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.