Ammetto che questa soluzione è un po 'più "hacky" dell'alternativa saveToDisk di Firefox Profile, ma funziona su Chrome e Firefox e non si basa su una funzionalità specifica del browser che potrebbe cambiare in qualsiasi momento . E se non altro, forse questo darà a qualcuno una prospettiva leggermente diversa su come risolvere le sfide future.
Prerequisiti: Assicurarsi di avere selenio e pyvirtualdisplay installato ...
- Python 2:
sudo pip install selenium pyvirtualdisplay
- Python 3:
sudo pip3 install selenium pyvirtualdisplay
The Magic
import pyvirtualdisplay
import selenium
import selenium.webdriver
import time
import base64
import json
root_url = 'https://www.google.com'
download_url = 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png'
print('Opening virtual display')
display = pyvirtualdisplay.Display(visible=0, size=(1280, 1024,))
display.start()
print('\tDone')
print('Opening web browser')
driver = selenium.webdriver.Firefox()
#driver = selenium.webdriver.Chrome() # Alternately, give Chrome a try
print('\tDone')
print('Retrieving initial web page')
driver.get(root_url)
print('\tDone')
print('Injecting retrieval code into web page')
driver.execute_script("""
window.file_contents = null;
var xhr = new XMLHttpRequest();
xhr.responseType = 'blob';
xhr.onload = function() {
var reader = new FileReader();
reader.onloadend = function() {
window.file_contents = reader.result;
};
reader.readAsDataURL(xhr.response);
};
xhr.open('GET', %(download_url)s);
xhr.send();
""".replace('\r\n', ' ').replace('\r', ' ').replace('\n', ' ') % {
'download_url': json.dumps(download_url),
})
print('Looping until file is retrieved')
downloaded_file = None
while downloaded_file is None:
# Returns the file retrieved base64 encoded (perfect for downloading binary)
downloaded_file = driver.execute_script('return (window.file_contents !== null ? window.file_contents.split(\',\')[1] : null);')
print(downloaded_file)
if not downloaded_file:
print('\tNot downloaded, waiting...')
time.sleep(0.5)
print('\tDone')
print('Writing file to disk')
fp = open('google-logo.png', 'wb')
fp.write(base64.b64decode(downloaded_file))
fp.close()
print('\tDone')
driver.close() # close web browser, or it'll persist after python exits.
display.popen.kill() # close virtual display, or it'll persist after python exits.
Explaination
Abbiamo primo carico di un URL sul dominio stiamo mira un download di file da. Questo ci consente di eseguire una richiesta AJAX su quel dominio, senza incorrere nei problemi cross site scripting.
Avanti, stiamo iniettando un javascript nel DOM, che spara una richiesta AJAX. Una volta che la richiesta AJAX restituisce una risposta, prendiamo la risposta e la cariciamo in un oggetto FileReader. Da lì possiamo estrarre il contenuto codificato base64 del file chiamando readAsDataUrl(). Stiamo quindi prendendo il contenuto codificato Base64 e aggiungerlo alla window
, una variabile gobally accessibile.
Infine, poiché la richiesta AJAX è asincrona, si entra in un ciclo di attesa pitone mentre per il contenuto da aggiungere alla finestra. Una volta aggiunto, decodifichiamo il contenuto di base64 recuperato dalla finestra e lo salviamo in un file.
Questa soluzione dovrebbe funzionare su tutti i browser moderni supportati da Selenium e funziona sia con testo che binari e con tutti i tipi di mime.
approccio alternativo
Anche se non ho testato questo, selenio fa permettersi la capacità di aspettare fino a quando un elemento è presente nel DOM. Anziché eseguire il ciclo finché non viene popolata una variabile accessibile a livello globale, è possibile creare un elemento con un ID particolare nel DOM e utilizzare l'associazione di tale elemento come trigger per recuperare il file scaricato.
Io consiglio l'uso di 'urllib' e uso' urllib.urlretrieve (url) 'per ottenere il download dove' url' è l'url che il link invia a – Serial
no perché funziona solo con l'evento click. – sam
ma se si analizza il codice HTML della pagina è possibile ottenere il collegamento che l'evento click invia al browser e utilizzare quello – Serial