humans - ¿Cómo descargar un archivo usando Python de una manera "más inteligente"?
python wget (5)
Basándome en los comentarios y en el informe de @ Oli, hice una solución como esta:
from os.path import basename
from urlparse import urlsplit
def url2name(url):
return basename(urlsplit(url)[2])
def download(url, localFileName = None):
localName = url2name(url)
req = urllib2.Request(url)
r = urllib2.urlopen(req)
if r.info().has_key(''Content-Disposition''):
# If the response has Content-Disposition, we take file name from it
localName = r.info()[''Content-Disposition''].split(''filename='')[1]
if localName[0] == ''"'' or localName[0] == "''":
localName = localName[1:-1]
elif r.url != url:
# if we were redirected, the real file name we take from the final URL
localName = url2name(r.url)
if localFileName:
# we can force to save the file as specified name
localName = localFileName
f = open(localName, ''wb'')
f.write(r.read())
f.close()
Toma el nombre de archivo de Content-Disposition; si no está presente, utiliza el nombre de archivo de la URL (si se produjo una redirección, se tiene en cuenta la URL final).
Necesito descargar varios archivos a través de http en Python.
La forma más obvia de hacerlo es simplemente usar urllib2:
import urllib2
u = urllib2.urlopen(''http://server.com/file.html'')
localFile = open(''file.html'', ''w'')
localFile.write(u.read())
localFile.close()
Pero tendré que lidiar con las URL que son desagradables de alguna manera, digamos así: http://server.com/!Run.aspx/someoddtext/somemore?id=121&m=pdf
. Cuando se descarga a través del navegador, el archivo tiene un nombre legible para los humanos, es decir. accounts.pdf
.
¿Hay alguna forma de manejar eso en Python, así que no necesito saber los nombres de los archivos y codificarlos en mi script?
Combinando gran parte de lo anterior, aquí hay una solución más pitonica:
import urllib2
import shutil
import urlparse
import os
def download(url, fileName=None):
def getFileName(url,openUrl):
if ''Content-Disposition'' in openUrl.info():
# If the response has Content-Disposition, try to get filename from it
cd = dict(map(
lambda x: x.strip().split(''='') if ''='' in x else (x.strip(),''''),
openUrl.info()[''Content-Disposition''].split('';'')))
if ''filename'' in cd:
filename = cd[''filename''].strip("/"''")
if filename: return filename
# if no filename was found above, parse it out of the final URL.
return os.path.basename(urlparse.urlsplit(openUrl.url)[2])
r = urllib2.urlopen(urllib2.Request(url))
try:
fileName = fileName or getFileName(url,r)
with open(fileName, ''wb'') as f:
shutil.copyfileobj(r,f)
finally:
r.close()
Los scripts de descarga de ese tipo tienden a presionar un encabezado que le dice al agente de usuario qué nombre debe darle al archivo:
Content-Disposition: attachment; filename="the filename.ext"
Si puedes agarrar ese encabezado, puedes obtener el nombre de archivo correcto.
Hay otro hilo que tiene un poco de código que ofrecer para Content-Disposition
-grabbing.
remotefile = urllib2.urlopen(''http://example.com/somefile.zip'')
remotefile.info()[''Content-Disposition'']
Usando wget
:
custom_file_name = "/custom/path/custom_name.ext"
wget.download(url, custom_file_name)
Usando urlretrieve:
urllib.urlretrieve(url, custom_file_name)
urlretrieve también crea la estructura de directorios si no existe.
2 Kender :
if localName[0] == ''"'' or localName[0] == "''":
localName = localName[1:-1]
no es seguro: el servidor web puede pasar un nombre mal formateado como ["file.ext] o [file.ext ''] o incluso estar vacío y localName [0] aumentará la excepción. El código correcto puede verse así:
localName = localName.replace(''"'', '''').replace("''", "")
if localName == '''':
localName = SOME_DEFAULT_FILE_NAME