如何使用 selenium 在点击事件中下载文件?

Posted

技术标签:

【中文标题】如何使用 selenium 在点击事件中下载文件?【英文标题】:How can I download a file on a click event using 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 的名称为“导出数据”的链接下载这两个文件。我如何才能实现它,因为它仅适用于点击事件?

【问题讨论】:

我建议使用urllib 并使用urllib.urlretrieve(url) 获取下载,其中url 是链接发送给您的网址 否,因为它只适用于点击事件。 但是如果你解析页面的 html,你可以获得点击事件发送到浏览器的链接并使用它 哦,别介意现在看看你是对的页面,我的错 How to download any file and save it to the desired location using Selenium Webdriver的可能重复 【参考方案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 文件所在的文件夹(这样我就可以直接使用脚本导入它)。谢谢! 没关系,我找到了答案。在这里,以防其他人需要它:***.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")

【讨论】:

以上是关于如何使用 selenium 在点击事件中下载文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 wkwebview 中下载文件

如何使用 Retrofit 库在 Android 中下载文件?

如何使用 webview 组件在 App 中下载文件?

如何将 JSON 数据转换为 XML 格式数据并在 React JS 中下载文件

如何通过在android中使用agora sdk在onFileMessageReceived中的给定路径中下载文件

如何在 Puppeteer 中下载文件之前更改文件名?