【问题标题】:Downloading with chrome headless and selenium使用 chrome headless 和 selenium 下载
【发布时间】:2018-01-19 18:22:34
【问题描述】:

我正在使用 python-selenium 和 Chrome 59 并尝试自动化一个简单的下载序列。当我正常启动浏览器时,可以下载,但是当我在无头模式下这样做时,下载不起作用。

# Headless implementation
from selenium import webdriver

chromeOptions = webdriver.ChromeOptions()
chromeOptions.add_argument("headless")

driver = webdriver.Chrome(chrome_options=chromeOptions)

driver.get('https://www.mockaroo.com/')
driver.find_element_by_id('download').click()
# ^^^ Download doesn't start

# Normal Mode
from selenium import webdriver

driver = webdriver.Chrome()

driver.get('https://www.mockaroo.com/')
driver.find_element_by_id('download').click()
# ^^^ Download works normally

我什至尝试过添加默认路径:

prefs = {"download.default_directory" : "/Users/Chetan/Desktop/"}
chromeOptions.add_argument("headless")
chromeOptions.add_experimental_option("prefs",prefs)

添加默认路径在正常实现中有效,但同样的问题在无头版本中仍然存在。

如何让下载以无头模式开始?

【问题讨论】:

  • 我也尝试过使用submit 并发送Keys.ENTER。它适用于普通浏览器,但不适用于无头浏览器。
  • 您希望仅使用 chrome 来完成吗?或者 Firefox 也可以?
  • 首选 chrome 或 phantomjs
  • 为什么不直接使用 urllib 来下载文件呢?单击文件以模拟下载仅适用于某些用户案例。我使用过在开始下载之前会打开“另存为”窗口的浏览器。如果您单击以查看它是否存在于服务器上,或者要验证文件的内容,那么 urllib 可能是您最好的选择。
  • @TehTris 问题是,我在另一个需要我之前登录的站点上执行此操作。这设置了标头和 cookie,所以我需要在使用之前设置它们。但是只使用js,似乎没有办法从客户端获取请求头......所以我不能使用urllib

标签: python google-chrome selenium google-chrome-headless


【解决方案1】:

我终于通过升级到 Chromium 90 让它工作了!我之前有版本 72-78,但我看到它最近已修复:https://bugs.chromium.org/p/chromium/issues/detail?id=696481 所以我决定试一试。

所以在升级之后,花了一段时间(MacOS 中的自制软件太慢了......),我只是做了,没有设置选项或任何东西(这是一个 JavaScript 示例):

await driver.findElement(By.className('download')).click();

它奏效了!我在我尝试下载了很长时间的同一个工作文件夹中看到了下载的 PDF...

【讨论】:

    【解决方案2】:

    更新的 Python 解决方案 - 于 2021 年 3 月 4 日在 chromedriver v88 和 v89 上测试

    这将允许您在无头模式下单击以下载文件。

        from selenium import webdriver
        from selenium.webdriver.common.keys import Keys
        from selenium.webdriver.chrome.options import Options
    
        # Instantiate headless driver
        chrome_options = Options()
    
        # Windows path
        chromedriver_location = 'C:\\path\\to\\chromedriver_win32\\chromedriver.exe'
        # Mac path. May have to allow chromedriver developer in os system prefs
        '/Users/path/to/chromedriver'
    
        chrome_options.add_argument("--headless")
        chrome_options.add_argument("--no-sandbox")
        chrome_options.add_argument("--disable-dev-shm-usage")
        
        chrome_prefs = {"download.default_directory": r"C:\path\to\Downloads"} # (windows)
        chrome_options.experimental_options["prefs"] = chrome_prefs
    
        driver = webdriver.Chrome(chromedriver_location,options=chrome_options)
    
        # Download your file
        driver.get('https://www.mockaroo.com/')
        driver.find_element_by_id('download').click()
    

    【讨论】:

      【解决方案3】:

      使用:google-chrome-stable amd64 86.0.4240.111-1,chromedriver 86.0.4240.22,selenium 3.141.0python 3.8.3

      尝试了多种建议的解决方案,但对于 chrome headless 没有任何效果,我的测试网站也打开了一个新的空白选项卡,然后下载了数据。

      最终放弃了 headless 并实现了 pyvirtualdisplayxvfd 来模拟 X 服务器,类似于:

      from selenium.webdriver.chrome.options import Options # and other imports
      import selenium.webdriver as webdriver
      import tempfile
      
      url = "https://really_badly_programmed_website.org"
      
      tmp_dir = tempfile.mkdtemp(prefix="hamster_")
      
      driver_path="/usr/bin/chromedriver"
      
      chrome_options = Options() 
      chrome_options.binary_location = "/usr/bin/google-chrome"
      
      prefs = {'download.default_directory': tmp_dir,}
      chrome_options.add_experimental_option("prefs", prefs)
      
      with Display(backend="xvfb",size=(1920,1080),color_depth=24) as disp:
      
          driver = webdriver.Chrome(options=chrome_options, executable_path=driver_path)
          driver.get(url)
      

      最后一切正常,下载文件在 tmp 文件夹中。

      【讨论】:

        【解决方案4】:

        是的,为了安全起见,这是一个“功能”。如前所述,这里是错误讨论:https://bugs.chromium.org/p/chromium/issues/detail?id=696481

        在 chrome 版本 62.0.3196.0 或更高版本中添加了支持以启用下载。

        这是一个python实现。我必须将该命令添加到 chromedriver 命令中。我将尝试提交 PR,以便将来将其包含在库中。

        def enable_download_in_headless_chrome(self, driver, download_dir):
            # add missing support for chrome "send_command"  to selenium webdriver
            driver.command_executor._commands["send_command"] = ("POST", '/session/$sessionId/chromium/send_command')
        
            params = {'cmd': 'Page.setDownloadBehavior', 'params': {'behavior': 'allow', 'downloadPath': download_dir}}
            command_result = driver.execute("send_command", params)
        

        作为参考,这里有一个小仓库来演示如何使用它: https://github.com/shawnbutton/PythonHeadlessChrome

        更新 2020-05-01 有 cmets 说这不再起作用了。鉴于这个补丁现在已经有一年多了,他们很可能已经改变了底层库。

        【讨论】:

        • 我试过这个,但它对我不起作用:(当我完全这样尝试时,我什么也得不到,当我关闭“无头”模式时,我得到了文件,但是然后 Chrome 崩溃。如果我从这个答案中完全删除代码以及无头模式,Chrome 会像预期的那样工作。我猜 Chrome 的 API 已经改变了?
        • @bitstream 它在 Chromium 68.0.3440.75chromedriver 2.38 上为我工作,check my full example
        • @shawn-button 如何下载视频..似乎 HTML5 视频默认在 chrome 上播放
        • 您说chrome 62.0.3196.0或以上版本增加了支持,可以下载了。但我目前正在使用 Chrome 71,但它在那里也不起作用。需要遵循相同的解决方法..
        • 这仍然是最新的吗?我尝试了github中的方法,它不下载文件。我测试了代码可以在不处于无头模式时下载文件。打印输出显示:来自浏览器的响应:result:value:None
        【解决方案5】:

        我通过使用@Shawn Button 共享的解决方法并为'downloadPath' 参数使用完整路径 解决了这个问题。使用相对路径不起作用并给我错误。

        版本:
        Chrome 版本 75.0.3770.100(官方版本)(32 位)
        ChromeDriver 75.0.3770.90

        【讨论】:

        【解决方案6】:

        通常看到用另一种语言写的同样的东西是多余的,但因为这个问题让我发疯,我希望我能拯救别人免于痛苦......所以这是Shawn Button's answer的C#版本(经过测试无头 chrome=71.0.3578.98, chromedriver=2.45.615279, platform=Linux 4.9.125-linuxkit x86_64)):

                    var enableDownloadCommandParameters = new Dictionary<string, object>
                    {
                        { "behavior", "allow" },
                        { "downloadPath", downloadDirectoryPath }
                    };
                    var result = ((OpenQA.Selenium.Chrome.ChromeDriver)driver).ExecuteChromeCommandWithResult("Page.setDownloadBehavior", enableDownloadCommandParameters);
        

        【讨论】:

          【解决方案7】:

          以下是 Java、selenium、chromedriver 和 chrome v 71.x 中的等效项。中的代码是允许保存下载的关键 其他 jars:com.fasterxml.jackson.core、com.fasterxml.jackson.annotation、com.fasterxml.jackson.databind

          System.setProperty("webdriver.chrome.driver","C:\libraries\chromedriver.exe");

                      String downloadFilepath = "C:\\Download";
                      HashMap<String, Object> chromePreferences = new HashMap<String, Object>();
                      chromePreferences.put("profile.default_content_settings.popups", 0);
                      chromePreferences.put("download.prompt_for_download", "false");
                      chromePreferences.put("download.default_directory", downloadFilepath);
                      ChromeOptions chromeOptions = new ChromeOptions();
                      chromeOptions.setBinary("C:\\pathto\\Chrome SxS\\Application\\chrome.exe");
          
                      //ChromeOptions options = new ChromeOptions();
                      //chromeOptions.setExperimentalOption("prefs", chromePreferences);
                      chromeOptions.addArguments("start-maximized");
                      chromeOptions.addArguments("disable-infobars");
          
          
                      //HEADLESS CHROME
                      **chromeOptions.addArguments("headless");**
          
                      chromeOptions.setExperimentalOption("prefs", chromePreferences);
                      DesiredCapabilities cap = DesiredCapabilities.chrome();
                      cap.setCapability(CapabilityType.ACCEPT_SSL_CERTS, true);
                      cap.setCapability(ChromeOptions.CAPABILITY, chromeOptions);
          
                      **ChromeDriverService driverService = ChromeDriverService.createDefaultService();
                      ChromeDriver driver = new ChromeDriver(driverService, chromeOptions);
          
                      Map<String, Object> commandParams = new HashMap<>();
                      commandParams.put("cmd", "Page.setDownloadBehavior");
                      Map<String, String> params = new HashMap<>();
                      params.put("behavior", "allow");
                      params.put("downloadPath", downloadFilepath);
                      commandParams.put("params", params);
                      ObjectMapper objectMapper = new ObjectMapper();
                      HttpClient httpClient = HttpClientBuilder.create().build();
                      String command = objectMapper.writeValueAsString(commandParams);
                      String u = driverService.getUrl().toString() + "/session/" + driver.getSessionId() + "/chromium/send_command";
                      HttpPost request = new HttpPost(u);
                      request.addHeader("content-type", "application/json");
                      request.setEntity(new StringEntity(command));**
                      try {
                          httpClient.execute(request);
                      } catch (IOException e2) {
                          // TODO Auto-generated catch block
                          e2.printStackTrace();
                      }**
          
                  //Continue using the driver for automation  
              driver.manage().window().maximize();
          

          【讨论】:

            【解决方案8】:

            这是一个基于 Shawn Button's answer 的 Python 工作示例。我已经用 Chromium 68.0.3440.75chromedriver 2.38

            对此进行了测试
            from selenium import webdriver
            from selenium.webdriver.chrome.options import Options
            
            chrome_options = Options()
            chrome_options.add_experimental_option("prefs", {
              "download.default_directory": "/path/to/download/dir",
              "download.prompt_for_download": False,
            })
            
            chrome_options.add_argument("--headless")
            driver = webdriver.Chrome(chrome_options=chrome_options)
            
            driver.command_executor._commands["send_command"] = ("POST", '/session/$sessionId/chromium/send_command')
            params = {'cmd': 'Page.setDownloadBehavior', 'params': {'behavior': 'allow', 'downloadPath': "/path/to/download/dir"}}
            command_result = driver.execute("send_command", params)
            
            driver.get('http://download-page.url/')
            driver.find_element_by_css_selector("#download_link").click()
            

            【讨论】:

            • 还要注意元素目标未设置为“_blank”,否则切换选项卡和尝试下载文件将不起作用
            • 感谢发帖。我还需要添加chromedriver_location = "/path/to/chromedriver",然后在驱动程序定义中引用它,即driver = webdriver.Chrome(chromedriver_location,options=chrome_options) 旁注:chrome_options 参数即将弃用,并且已经被options 参数替换,如我的小示例中所示在这里。
            【解决方案9】:

            使用 selenium-cucumber-js / selenium-webdriver 的 JavaScript 完整工作示例:

            const chromedriver = require('chromedriver');
            const selenium = require('selenium-webdriver');
            const command = require('selenium-webdriver/lib/command');
            const chrome = require('selenium-webdriver/chrome');
            
            module.exports = function() {
            
              const chromeOptions = new chrome.Options()
                .addArguments('--no-sandbox', '--headless', '--start-maximized', '--ignore-certificate-errors')
                .setUserPreferences({
                  'profile.default_content_settings.popups': 0, // disable download file dialog
                  'download.default_directory': '/tmp/downloads', // default file download location
                  "download.prompt_for_download": false,
                  'download.directory_upgrade': true,
                  'safebrowsing.enabled': false,
                  'plugins.always_open_pdf_externally': true,
                  'plugins.plugins_disabled': ["Chrome PDF Viewer"]
                })
                .windowSize({width: 1600, height: 1200});
            
              const driver = new selenium.Builder()
                .withCapabilities({
                  browserName: 'chrome',
                  javascriptEnabled: true,
                  acceptSslCerts: true,
                  path: chromedriver.path
                })
                .setChromeOptions(chromeOptions)
                .build();
            
              driver.manage().window().maximize();
            
              driver.getSession()
                .then(session => {
                  const cmd = new command.Command("SEND_COMMAND")
                    .setParameter("cmd", "Page.setDownloadBehavior")
                    .setParameter("params", {'behavior': 'allow', 'downloadPath': '/tmp/downloads'});
                  driver.getExecutor().defineCommand("SEND_COMMAND", "POST", `/session/${session.getId()}/chromium/send_command`);
                  return driver.execute(cmd);
                });
            
              return driver;
            };
            

            关键部分是:

              driver.getSession()
                .then(session => {
                  const cmd = new command.Command("SEND_COMMAND")
                    .setParameter("cmd", "Page.setDownloadBehavior")
                    .setParameter("params", {'behavior': 'allow', 'downloadPath': '/tmp/downloads'});
                  driver.getExecutor().defineCommand("SEND_COMMAND", "POST", `/session/${session.getId()}/chromium/send_command`);
                  return driver.execute(cmd);
                });
            

            测试:

            • 铬 67.0.3396.99
            • Chromedriver 2.36.540469
            • 硒黄瓜js 1.5.12
            • selenium-webdriver 3.0.0

            【讨论】:

            • 感谢您发布 javascript 解决方案。如何执行命令并不完全清楚。
            【解决方案10】:

            也许您处理的网站会为浏览器返回不同的 HTML 页面,这意味着您想要的 XPath 或 Id 在无头浏览器中可能会有所不同。 尝试在无头浏览器中下载 pageSource 并将其作为 HTML 页面打开以查看所需的 Id 或 XPath。 您可以将其视为 c# 示例 How to hide FirefoxDriver (using Selenium) without findElement function error in PhantomDriver?

            【讨论】:

            • 在获得页面并执行driver.get_screenshot_as_file('foo.png') 之后,我获得了实际图像的图像,看起来还不错。此外,驾驶员能够找到按钮。正在调查此事。
            【解决方案11】:

            这是 Chrome 的一项功能,可防止软件将文件下载到您的计算机。不过有一个解决方法。 Read more about it here.

            您需要做的是通过 DevTools 启用它,类似这样:

            async function setDownload () {
              const client = await CDP({tab: 'ws://localhost:9222/devtools/browser'});
              const info =  await client.send('Browser.setDownloadBehavior', {behavior : "allow", downloadPath: "/tmp/"});
              await client.close();
            }
            

            这是有人在提到的主题中给出的解决方案。 Here is his comment.

            【讨论】:

            • 此解决方案需要修补 Chrome,这不是解决方法。命令 Browser.setDownloadBehavior 在 Chrome v62.0.3186.0 中不存在
            • 几个月前我也遇到了同样的问题。直到今天还没有找到任何解决方案,感谢一个家伙评论我的问题并指出我在这里。阅读这个答案让我很高兴,但我真的不知道如何在我的源代码中复制或调整这段代码。
            • @TheChetan 谢谢!有趣的链接,尽管我正在用 java 开发它,chromePrefs.put("Browser.setDownloadBehavior", "allow"); 会有所帮助,只要这个字符串是真实的并且可以工作..):
            • 你会如何在 Python 中使用 selenium 来实现?
            • @Nihvel 你能用 Java 解决这个问题吗?你能发布解决方案吗
            猜你喜欢
            • 2017-12-26
            • 2019-05-25
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-05-08
            • 2021-12-27
            • 2019-05-28
            • 2020-04-08
            相关资源
            最近更新 更多