2013-08-26 49 views
27

Pracuję nad python i selenem. Chcę pobrać plik z kliknięcia zdarzenia przy użyciu selenu. Napisałem następujący kod.Pobieranie pliku przy użyciu selenu

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() 

Chcę pobrać oba pliki z linków o nazwie "Eksportuj dane" z podanego adresu URL. Jak mogę to osiągnąć, ponieważ działa tylko z wydarzeniem kliknięcia.

Dzięki

+1

Polecam używając 'urllib' i użyj' urllib.urlretrieve (url) 'aby pobrać gdzie' url' jest adresem URL, który link wysyła do – Serial

+0

nie, ponieważ działa tylko z wydarzeniem kliknięcia. – sam

+0

ale jeśli przeanalizujesz kod HTML strony, możesz uzyskać link, który zdarzenie click wysyła do przeglądarki i używać tego – Serial

Odpowiedz

41

Znajdź łącze za pomocą find_element(s)_by_*, następnie zadzwonić click metody.

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() 

Dodano kod manipulacji profilem, aby zapobiec wyświetlaniu okna dialogowego pobierania.

+0

co należy zrobić, gdy chciałem ukryć przeglądarkę lub zachować przeglądarkę w trybie ukrywania/minimalizowania podczas przetwarzania? – sam

+0

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

+0

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

4

Przyznam, że to rozwiązanie jest trochę bardziej "hackowe" niż alternatywna wersja SaveToDisk profilu Firefoksa, ale działa zarówno w przeglądarce Chrome, jak i Firefox i nie polega na funkcji specyficznej dla przeglądarki, która mogłaby się zmienić w dowolnym momencie . A jeśli nic innego, może da to komuś trochę inną perspektywę na to, jak rozwiązać przyszłe wyzwania.

wstępne: Sprawdź, czy masz zainstalowane selen i pyvirtualdisplay ...

  • 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. 

wyjaśnieniu

Najpierw załadować URL w domenie jesteśmy kierowania do pobrania pliku z. Dzięki temu możemy wykonać żądanie AJAX w tej domenie, bez problemów z numerem cross site scripting.

Następnie wprowadzamy trochę javascript do DOM, który odpala żądanie AJAX. Gdy żądanie AJAX zwróci odpowiedź, wykonamy odpowiedź i załadujemy ją do obiektu FileReader. Stamtąd możemy wyodrębnić zakodowaną zawartość base64, wywołując readAsDataUrl(). Następnie zajmujemy się zakodowaniem zawartości base64 i dołączamy ją do window, zmiennej dostępnej gobicznie.

W końcu, ponieważ żądanie AJAX jest asynchroniczne, wchodzimy do Pythona, czekając na pętlę, aż zawartość zostanie dołączona do okna. Po dołączeniu, dekodujemy zawartość base64 pobraną z okna i zapisujemy do pliku.

To rozwiązanie powinno działać we wszystkich nowoczesnych przeglądarkach obsługiwanych przez Selenium i działa zarówno tekstowo jak i binarnie oraz we wszystkich typach MIME.

Alternatywne podejście

Choć nie testowałem tego, Selen ma pozwolić Ci możliwość, aby czekać, aż element jest obecny w DOM. Zamiast pętli, dopóki nie zostanie wypełniona globalnie dostępna zmienna, możesz utworzyć element o określonym ID w DOM i użyć powiązania tego elementu jako wyzwalacza do pobrania pobranego pliku.

1

w chromie, co robię jest pobieranie plików, klikając na linki, a następnie otworzyć chrome://downloads stronę, a następnie odzyskać pobraną listę plików z cienia DOM takiego:

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

To rozwiązanie jest ograniczony do Chrome dane zawierają również informacje, takie jak ścieżka do pliku i data pobrania. (zauważ, że ten kod pochodzi z JS, może nie być poprawną składnią pythona)

Powiązane problemy