56

我正在研究 python 和 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 的名称为“导出数据”的链接下载这两个文件。我如何才能实现它,因为它仅适用于点击事件?

4

4 回答 4

76

使用 找到链接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()

添加了配置文件操作代码以防止下载对话框。

于 2013-08-26T09:06:28.487 回答
16

我承认这个解决方案比 Firefox Profile saveToDisk 替代方案更“hacky”,但它适用于 Chrome 和 Firefox,并且不依赖可能随时更改的特定于浏览器的功能。如果不出意外,也许这会让人们对如何解决未来的挑战有一点不同的看法。

先决条件:确保您已安装 selenium 和 pyvirtualdisplay ...

  • 蟒蛇2:sudo pip install selenium pyvirtualdisplay
  • 蟒蛇 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.

解释

我们首先在我们要从中下载文件的域上加载一个 URL。这允许我们在该域上执行 AJAX 请求,而不会遇到跨站点脚本问题。

接下来,我们将一些 javascript 注入到触发 AJAX 请求的 DOM 中。一旦 AJAX 请求返回响应,我们就会获取响应并将其加载到 FileReader 对象中。从那里我们可以通过调用 readAsDataUrl() 来提取文件的 base64 编码内容。然后,我们获取 base64 编码的内容并将其附加到window全局可访问的变量中。

最后,因为 AJAX 请求是异步的,我们进入一个 Python while 循环等待内容被追加到窗口中。附加后,我们解码从窗口检索到的 base64 内容并将其保存到文件中。

这个解决方案应该适用于 Selenium 支持的所有现代浏览器,无论是文本还是二进制,以及所有 mime 类型。

替代方法

虽然我没有对此进行测试,但 Selenium 确实让您能够等到元素出现在 DOM 中。您可以在 DOM 中创建一个具有特定 ID 的元素,并使用该元素的绑定作为触发器来检索下载的文件,而不是循环直到填充了全局可访问的变量。

于 2016-04-14T03:50:00.460 回答
3

在 chrome 中,我所做的是通过单击链接下载文件,然后打开chrome://downloads页面,然后从 shadow DOM 中检索下载的文件列表,如下所示:

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

该解决方案仅限于 chrome,数据还包含文件路径和下载日期等信息。(注意这段代码来自 JS,可能不是正确的 python 语法)

于 2017-05-18T22:15:00.203 回答
1

这是完整的工作代码。您可以使用网页抓取来输入用户名密码和其他字段。要获取出现在网页上的字段名称,请使用检查元素。元素名称(用户名、密码或点击按钮)可以通过类或名称输入。

from selenium import webdriver
# Using Chrome to access web
options = webdriver.ChromeOptions() 
options.add_argument("download.default_directory=C:/Test") # Set the download Path
driver = webdriver.Chrome(options=options)
# Open the website
try:
    driver.get('xxxx') # Your Website Address
    password_box = driver.find_element_by_name('password')
    password_box.send_keys('xxxx') #Password
    download_button = driver.find_element_by_class_name('link_w_pass')
    download_button.click()
    driver.quit()
except:
    driver.quit()
    print("Faulty URL")
于 2019-09-08T11:05:59.197 回答