簡體   English   中英

使用 chrome headless 和 selenium 下載

[英]Downloading with chrome headless and selenium

我正在使用 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)

添加默認路徑在正常實現中有效,但在無頭版本中仍然存在相同的問題。

如何讓下載以無頭模式開始?

是的,這是一個“功能”,為了安全。 如前所述,這里是錯誤討論: 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有評論說這不再起作用了。 鑒於這個補丁現在已經一年多了,他們很可能已經改變了底層庫。

這是基於Shawn Button's answer 的Python 工作示例。 我已經用Chromium 68.0.3440.75 & chromedriver 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()

這是 Chrome 的一項功能,可防止軟件將文件下載到您的計算機。 不過有一個解決方法。在此處閱讀更多相關信息

你需要做的是通過 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();
}

這是有人在提到的主題中給出的解決方案。 這是他的評論

也許您處理的網站為瀏覽器返回不同的 HTML 頁面,這意味着您想要的 XPath 或 Id 在無頭瀏覽器中可能有所不同。 嘗試在無頭瀏覽器中下載 pageSource 並將其作為 HTML 頁面打開以查看所需的 Id 或 XPath。 您可以將其視為 c# 示例How to hide FirefoxDriver (using Selenium) without findElement function error in PhantomDriver? .

更新的 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()

通常看到用另一種語言編寫的相同內容是多余的,但是因為這個問題讓我發瘋了,我希望我能將其他人從痛苦中拯救出來......所以這是Shawn Button 答案的 C# 版本(使用無頭 chrome= 71.0.3578.98,chromedriver=2.45.615279,平台=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);

帶有 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
  • 硒網絡驅動程序 3.0.0

以下是 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();

我通過使用@Shawn Button 共享的解決方法並使用“downloadPath”參數的完整路徑解決了這個問題。 使用相對路徑不起作用並給我錯誤。

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

使用: google-chrome-stable amd64 86.0.4240.111-1chromedriver 86.0.4240.22selenium 3.141.0 python 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 文件夾中有下載文件。

我終於通過升級到 Chromium 90 讓它工作了! 我以前有 72-78 版本,但我看到它最近已修復: https : //bugs.chromium.org/p/chromium/issues/detail? id =696481所以我決定試一試。

所以升級后,花了一段時間(MacOS 中的自制軟件太慢了......),我只是做了,沒有設置選項或任何東西(這是一個 JavaScript 示例):

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

它奏效了! 我在我一直嘗試下載的同一個工作文件夾中看到了下載的 PDF...

Chromium 開發人員最近添加了第二個無頭模式(2021 年)。 https://bugs.chromium.org/p/chromium/issues/detail?id=706008#c38

--headless=chrome標志現在允許您在新的無頭模式下獲得 Chrome 的全部功能,您甚至可以在其中運行擴展程序。

用法:

options.add_argument("--headless=chrome")

如果某些東西在常規 Chrome 中有效,那么它現在也應該適用於較新的無頭模式。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM