2013-08-26 19 views
27

Sto lavorando su pitone e selenio. Voglio scaricare il file cliccando sull'evento usando il selenio. Ho scritto il seguente codice.il download di file utilizzando il selenio

from selenium import webdriver 
from selenium.common.exceptions import NoSuchElementException 
from selenium.webdriver.common.keys import Keys 

browser = webdriver.Firefox() 
browser.get("http://www.drugcite.com/?q=ACTIMMUNE") 

browser.close() 

Desidero scaricare entrambi i file dai collegamenti con nome "Esporta dati" dall'URL specificato. Come posso ottenerlo visto che funziona solo con l'evento click.

Grazie

+1

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

+0

no perché funziona solo con l'evento click. – sam

+0

ma se si analizza il codice HTML della pagina è possibile ottenere il collegamento che l'evento click invia al browser e utilizzare quello – Serial

risposta

41

Trova il collegamento mediante find_element(s)_by_*, quindi chiamare click metodo.

from selenium import webdriver 

# To prevent download dialog 
profile = webdriver.FirefoxProfile() 
profile.set_preference('browser.download.folderList', 2) # custom location 
profile.set_preference('browser.download.manager.showWhenStarting', False) 
profile.set_preference('browser.download.dir', '/tmp') 
profile.set_preference('browser.helperApps.neverAsk.saveToDisk', 'text/csv') 

browser = webdriver.Firefox(profile) 
browser.get("http://www.drugcite.com/?q=ACTIMMUNE") 

browser.find_element_by_id('exportpt').click() 
browser.find_element_by_id('exporthlgt').click() 

Aggiunto codice di manipolazione profilo per impedire la finestra di download.

+0

cosa si dovrebbe fare se volevo nascondere il browser o mantenere il browser in modalità nascosta/ridotta durante l'elaborazione? – sam

+0

@sam, cerca 'headless' +' selenium' + 'firefox'. – falsetru

+0

@sam, o 'phanromjs',' ghostdriver'. – falsetru

4

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.

1

in Chrome Quello che faccio è scaricare i file cliccando sui link, poi apro chrome://downloads pagina e poi recuperare l'elenco di file scaricati da ombra DOM come questo:

docs = document 
    .querySelector('downloads-manager') 
    .shadowRoot.querySelector('#downloads-list') 
    .getElementsByTagName('downloads-item') 

Questa soluzione è trattenuto a Chrome , i dati contengono anche informazioni come il percorso del file e la data di download. (nota che questo codice è da JS, potrebbe non essere la sintassi python corretta)

Problemi correlati