70 votes

Téléchargement avec chrome headless et selenium

J'utilise python-selenium et Chrome 59 et j'essaie d'automatiser une séquence de téléchargement simple. Lorsque je lance le navigateur normalement, le téléchargement fonctionne, mais lorsque je le fais en mode sans tête, le téléchargement ne fonctionne pas.

# 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

J'ai même essayé d'ajouter un chemin par défaut :

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

L'ajout d'un chemin par défaut fonctionne dans l'implémentation normale, mais le même problème persiste dans la version sans tête.

Comment faire pour que le téléchargement démarre en mode sans tête ?

0 votes

J'ai également essayé d'utiliser submit et l'envoi Keys.ENTER . Cela fonctionne pour le navigateur normal, mais pas pour le navigateur sans tête.

0 votes

Voulez-vous que cela soit fait en utilisant uniquement chrome ? ou firefox aussi ?

0 votes

Préférez chrome ou phantomjs

62voto

Shawn Button Points 466

Oui, c'est une "fonctionnalité", pour la sécurité. Comme mentionné précédemment, voici la discussion sur le bogue : https://bugs.chromium.org/p/chromium/issues/detail?id=696481

Une prise en charge a été ajoutée dans la version 62.0.3196.0 ou supérieure de Chrome pour permettre le téléchargement.

Voici une implémentation en python. J'ai dû ajouter la commande aux commandes de chromedriver. Je vais essayer de soumettre un PR pour qu'elle soit incluse dans la bibliothèque à l'avenir.

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)

Pour référence, voici un petit repo pour démontrer comment l'utiliser : https://github.com/shawnbutton/PythonHeadlessChrome

mise à jour 2020-05-01 Il y a eu des commentaires disant que cela ne fonctionne plus. Étant donné que ce patch a maintenant plus d'un an, il est tout à fait possible qu'ils aient changé la bibliothèque sous-jacente.

2 votes

J'ai essayé cela et cela ne fonctionne pas pour moi :( Quand j'essaie exactement comme cela, je n'obtiens rien, et quand je désactive le mode "sans tête", j'obtiens le fichier, mais ensuite Chrome se plante. Si je supprime complètement le code de cette réponse ainsi que le mode sans tête, Chrome fonctionne comme prévu. Je suppose que l'API de Chrome a changé ?

0 votes

@bitstream Cela a fonctionné pour moi sur Chromium 68.0.3440.75 & chromedriver 2.38 , consultez mon exemple complet

0 votes

@shawn-button comment télécharger des vidéos. Il semble que les vidéos HTML5 soient lues par défaut sur chrome.

26voto

Fayçal Points 388

Voici un exemple fonctionnel pour Python basé sur Réponse de Shawn Button . J'ai testé cela avec 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()

0 votes

Veillez également à ce que la cible de l'élément ne soit pas "_blank", sinon le fait de changer d'onglet et d'essayer de télécharger le fichier ne fonctionnera pas.

1 votes

Merci d'avoir posté. Je devais également ajouter chromedriver_location = "/path/to/chromedriver" et y faire référence dans la définition du pilote, c'est-à-dire driver = webdriver.Chrome(chromedriver_location,options=chrome_option‌​s) sidenote : le chrome_options est bientôt obsolète, et est déjà remplacé par le paramètre options param, comme le démontre mon petit exemple ici.

18voto

Some1Else Points 434

Il s'agit d'une fonctionnalité de Chrome qui empêche les logiciels de télécharger des fichiers sur votre ordinateur. Il existe cependant une solution de contournement. Pour en savoir plus, cliquez ici .

Ce que vous devez faire, c'est l'activer via DevTools, quelque chose comme ça :

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();
}

C'est la solution que quelqu'un a donnée dans le sujet mentionné. Voici son commentaire .

4 votes

Cette solution nécessite de patcher Chrome, ce n'est pas une solution de contournement. La commande Browser.setDownloadBehavior est pas présent dans Chrome v62.0.3186.0.

0 votes

J'ai rencontré le même problème il y a quelques mois. Je n'ai pas trouvé de solution jusqu'à aujourd'hui, grâce à un mec qui a commenté ma question et m'a dirigé ici. Lire cette réponse me rend heureux, mais je n'ai vraiment aucune idée de la façon de copier ou d'adapter ce code dans ma source.

0 votes

@TheChetan merci ! lien intéressant, bien que je le développe en java et une chromePrefs.put("Browser.setDownloadBehavior", "allow"); serait plus utile, si seulement cette chaîne était réelle et fonctionnait ) :

4voto

Hazem Points 134

Peut-être que le site web que vous manipulez renvoie des pages HTML différentes pour les navigateurs, ce qui signifie que le XPath ou l'Id que vous voulez peut être différent dans le navigateur headless. Essayez de télécharger la pageSource dans le navigateur headless et ouvrez-la en tant que page HTML pour voir l'Id ou le XPath que vous voulez. Vous pouvez voir cet exemple en c# Comment masquer FirefoxDriver (en utilisant Selenium) sans erreur de la fonction findElement dans PhantomDriver ? .

0 votes

Après avoir obtenu la page, je fais driver.get_screenshot_as_file('foo.png') J'obtiens une image de la chose réelle et elle semble correcte. De plus, le pilote est capable de trouver le bouton. J'enquête sur ce point.

1voto

victorvartan Points 436

D'habitude, c'est redondant de voir la même chose écrite dans un autre langage, mais comme ce problème m'a rendu fou, j'espère que je vais éviter à quelqu'un d'autre de souffrir... voici donc la version C# de Réponse de Shawn Button (testé avec headless 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);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X