2013-08-26 3 views
27

Я работаю над python и селеном. Я хочу загрузить файл из события click, используя selenium. Я написал следующий код.загрузка файла с использованием selenium

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

Я хочу загрузить оба файла из ссылок с именем «Экспорт данных» из данного URL-адреса. Как я могу достичь этого, поскольку он работает только с событием клика.

Благодаря

+1

Я рекомендую использовать 'urllib' и использовать' urllib.urlretrieve (url) 'для загрузки, где' url' - это URL-адрес, который посылает ссылка вы до – Serial

+0

нет, потому что он работает только с событием click. – sam

+0

, но если вы проанализируете HTML-страницу, вы можете получить ссылку, которую посылает событие клика в браузере, и использовать ее – Serial

ответ

41

Найдите ссылку с помощью find_element(s)_by_*, а затем вызвать метод click.

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

Добавлен код манипулирования профилем, чтобы предотвратить диалог загрузки.

+0

Что делать, если я хочу скрыть браузер или сохранить браузер в режиме скрытия/минимизации во время обработки? – sam

+0

@sam, поиск 'headless' +' selenium' + 'firefox'. – falsetru

+0

@sam, или 'phanromjs',' ghostdriver'. – falsetru

4

Я признаю это решение немного более «взломанным», чем альтернативный вариант Firefox Profile saveToDisk, но он работает как в Chrome, так и в Firefox и не зависит от специфики браузера, которая может измениться в любое время , И если ничего другого, возможно, это даст кому-то немного другой взгляд на то, как решать будущие задачи.

Предпосылки: Убедитесь, что у вас есть селен и pyvirtualdisplay установлен ...

  • Python 2: sudo pip install selenium pyvirtualdisplay
  • Python 3: sudo pip3 install selenium pyvirtualdisplay

Волшебное

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

Сначала загрузите URL на домене мы таргетирования загрузки файла с. Это позволяет нам выполнить запрос AJAX в этом домене, без проблем с cross site scripting.

Далее мы вводим некоторый javascript в DOM, который запускает запрос AJAX. Когда запрос AJAX возвращает ответ, мы берем ответ и загружаем его в объект FileReader. Оттуда мы можем извлечь содержимое в кодировке base64 файла, вызвав readAsDataUrl(). Затем мы берем кодированный base64 контент и добавляем его в window, являющуюся доступной по всему миру переменной.

Наконец, поскольку запрос AJAX является асинхронным, мы вводим цикл Python while, ожидающий добавления содержимого в окно. Когда он добавлен, мы декодируем содержимое base64, извлеченное из окна, и сохраним его в файле.

Это решение должно работать во всех современных браузерах, поддерживаемых Selenium, и работает как текстовые, так и двоичные, а также во всех типах mime.

Альтернативный подход

В то время как я не проверял это, Селен делает себе вам возможность ждать, пока элемент не присутствует в DOM. Вместо того, чтобы заполнять глобально доступную переменную, вы можете создать элемент с определенным идентификатором в DOM и использовать привязку этого элемента в качестве триггера для загрузки загруженного файла.

1

В хроме, что я делаю, загрузку файлов, нажав на ссылки, а затем открыть chrome://downloads страницу, а затем извлечь загруженный список файлов из теневой DOM так:

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

Это решение сдерживается в хроме , данные также содержат информацию, такую ​​как путь к файлу и дату загрузки. (обратите внимание, что этот код принадлежит JS, может быть не корректным синтаксисом python)

Смежные вопросы