Per ragioni di semplicità di utilizzo e immediatezza (e anche per imparare qualcosa di nuovo), la settimana scorsa ho dovuto scrivere un downloader da linea di comando simile a GNU wget, ma con alcuni requisiti personalizzati:
- l’accesso alla pagina di download è protetto da userid e password (autenticazione HTTPDigestAuth);
- il protocollo di accesso è HTTPS;
- una volta ottenuto l’accesso, è necessario fare il GET di alcuni files ben particolari e crearli su hard disk.
Per realizzare questo wget-like in Python, usiamo il modulo urllib2 che ci permette di accedere a risorse esterne tramite i protocolli più svariati. Per aprire una connessione HTTPS e autenticarci correttamente usiamo i seguenti statement:
passman.add_password(None, url, username, password)
authhandler = urllib2.HTTPDigestAuthHandler(passman)
opener = urllib2.build_opener(authhandler)
Affinché la gestione delle varie opzioni sia più semplice possibile, usiamo anche il modulo optparse (che permette di passare le opzioni al programma in modo simile alle utility GNU):
parser = optparse.OptionParser()
parser.add_option("-u", "–username")
parser.add_option("-d", "–date")
parser.add_option("-t", "–target", default="logs")
options, arguments = parser.parse_args()
Visto che dovremo gestire anche l’autenticazione tramite username e password, la scelta di passare la password come argomento del programma è una scelta non ottimale: una semplice ricerca nella history della shell utilizzata potrebbe permettere ad un attaccante di risalire alla password. Per questo ci viene incontro il pacchetto getpass che, in modo simile ai programmi UNIX che si rispettino, chiede interattivamente la password (senza stamparla, ovviamente!). Per richiedere l’inserimento della password all’utente è sufficiente:
password = getpass.getpass()
Torniamo ora all’inizio: abbiamo ottenuto dall’utente username (passato come argomento) e password (richiesta interattivamente): ora possiamo aprire una connessione verso l’hostname di riferimento e richiedere il download del file (salvando su file lo stream del file in ricezione tramite i metodi standard di Python):
passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
passman.add_password(None, url, options.username, password)
authhandler = urllib2.HTTPDigestAuthHandler(passman)
opener = urllib2.build_opener(authhandler)
download = opener.open(file)
logging.debug('downloading %s ...' % file)
fo = open(options.target + os.sep + targetFileName, 'w')
fo.write(download.read())
fo.close()
Abbiamo quindi ottenuto un semplice downloader di file scritto in Python, molto più semplice di wget ma al tempo stesso molto più customizzabile.
Stupendo! Una perfetta dimostrazione di quanto siano utili le ottime librerie di Python.
urllib2 è potente, ma sono rimasto piacevolmente stupito da optparse: sai com’è, per uno sfegatato della CLI come me…