【问题标题】:How can I download a file on a click event using selenium?如何使用 selenium 在点击事件中下载文件?
【发布时间】:2013-08-28 16:34:08
【问题描述】:

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

【问题讨论】:

标签: python selenium selenium-webdriver web-scraping


【解决方案1】:

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

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

【讨论】:

  • @sam,搜索headless + selenium + firefox
  • @sam 您可以使用 PyVirtualDisplay 无头运行 firefox。它对我有用。
  • 仍在下载对话框。
  • 嗨,我正在尝试做同样的事情(并且它正在工作),但我想知道是否有人知道如何控制下载位置。它会自动下载到我的下载文件夹中,但我想将它路由到我的 .py 文件所在的文件夹(这样我就可以直接使用脚本导入它)。谢谢!
  • 没关系,我找到了答案。在这里,以防其他人需要它:stackoverflow.com/questions/25251583/…
【解决方案2】:

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

先决条件:确保您已安装 selenium 和 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.

说明

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

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

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

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

替代方法

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

【讨论】:

  • 我有一个 PDF 的下载按钮,该按钮位于验证码后面,因此与会话相关联。我拥有的download_url 不是.pdf 文件,而是带有$(document).ready(function () { 的javascript 页面,该页面调用$.post() 到实际的PDF。当我使用您的解决方案时,我最终下载的是 HTML 文件,而不是我想要下载的 PDF。在这种情况下我将如何适应这种情况?
【解决方案3】:

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

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

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

【讨论】:

  • 请注意问题标签。这是一个python问题,不是JS!
【解决方案4】:

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

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

【讨论】:

    猜你喜欢
    • 2022-01-19
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多