繁体   English   中英

尝试下载文件时,带有 Python 的无头 Chrome 挂起

[英]Headless Chrome with Python suspends when trying to download a file

我在 Mac 上使用 Python、Jupyter、Selenium webdriver 和无头 Chrome(使用 Canary)。

我写了一个脚本来抓取一个非常古老的网站,为了从那个网站下载一个文件,我需要点击几个按钮,最终会引导我到一个按钮,点击它会下载一个 CSV 文件

问题是,当无头 Chrome 尝试下载目标文件时,它会挂起并且什么都不做(即不下载所需的文件),即使脚本完成运行(是的,我在脚本末尾关闭了它)

我试过:

  • 下载其他文件(来自不同的网站)和无头 Chrome 似乎可以毫无问题地下载它们(我启用了无头 chrome 选项以成功下载文件)
  • 拍摄网站快照以确保它正确导航到下载页面(是的,它正确导航)
  • 修改用户代理(它似乎正在使用我期望的用户代理)
  • 在没有 headless 选项的情况下运行完全相同的代码 - 它使用常规 chrome 成功下载文件
  • 使用driver.execute_script(js_that_changes_plugins_and_langs)更改驱动程序上的插件和语言 JS 脚本,但我不太确定如何检查它是否确实在执行它(并且它仍然无法正常工作)

我面临的问题:

  • 我找不到只获取最后一个下载 URL 的方法,因为它似乎使用了一些沿途生成的唯一 ID(当您转到主页以及在站点中的页面之间导航时会给出),因此对于每个会议将改变
  • 导航 URL 似乎源自主页内的 iframe(以及以下 URL),我不太确定如何检查它生成的 Javascript

我提供网站 URL 没有任何问题,但是:

  • 你必须在不同的页面上点击大约 6 次,才能通过下载按钮到达最后一页。 这些点击并不直观,需要花很多时间来解释如何导航到我想要的地方
  • 这个网站不是英文的,这会使解释如何导航变得更加困难

我需要它是无头的,而不是普通的 Chrome,因为我们要运行代码的机器非常弱,无法运行 chrome GUI

所以我的问题是:有谁知道可能是什么问题? 或者至少,我该如何调试它?

这或多或少是我正在使用的代码:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

def enable_download_in_headless_chrome(driver, download_dir):
        """
        there is currently a "feature" in chrome where
        headless does not allow file download: https://bugs.chromium.org/p/chromium/issues/detail?id=696481
        This method is a hacky work-around until the official chromedriver support for this.
        Requires chrome version 62.0.3196.0 or above.
        """

        # add missing support for chrome "send_command"  to selenium webdriver
        driver.command_executor._commands["send_command"] = ("POST", '/session/' + driver.session_id + '/chromium/send_command')

        params = {'cmd': 'Page.setDownloadBehavior', 'params': {'behavior': 'allow', 'downloadPath': download_dir}}
        command_result = driver.execute("send_command", params)
        print("response from browser:")
        for key in command_result:
            print("result:" + key + ":" + str(command_result[key]))

chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('headless')
chrome_options.add_argument('no-sandbox')
chrome_options.add_argument('disable-gpu')
chrome_options.add_argument('remote-deubgging-port=9222')
chrome_options.add_argument('disable-popup-blocking')
chrome_options.add_argument('enable-logging')
download_dir = # some path here
driver = webdriver.Chrome(chrome_options=chrome_options)
enable_download_in_headless_chrome(driver, download_dir)
ok_button = driver.find_element_by_id('the-button-name')
ok_button.click()

感谢您的帮助

由于您没有提供下载猜测工作的 URL。 Target 很可能安装了类似 recapta 的墙以防止刮擦。 因此,请确保您没有碰到这个“recapta”墙,并且如果您确实实现了通知您执行手动任务以授予访问权限的代码。

对于 js 这个解决方案是由 zavodnyuk在这里给出的:

尝试使用兼容的(例如,从您的真实浏览器)设置自定义用户代理。 功能:{'browserName':'chrome',chromeOptions:{args:[“user-agent=Mozilla/5.0(X11;Linux x86_64)AppleWebKit/537.36(KHTML,像壁虎)Chrome/60.0.3112.113 Safari/537.36”, "--headless", "--disable-gpu" ] } 在 js 上为 selenium/protractor 工作

我希望这会向您暗示正确的方向,因为互联网上对 Python 的描述并不多。

基于评论1的编辑:

在基本调试模式下,我依赖于可能的候选 defs 开始时的打印语句。 我所说的 printstatement 也可以是写入文件的行。 不依赖第三方花哨的包,因为我想在大多数情况下从代码中学习,然后在接近耗时但值得花时间的努力。 例如我如何直截了当地调试:

def header_inspect(self, ID, action, data):
    print  'header_inspect, ID : %s\n, action : %s\nprocess-data : %s' % (ID, action, data)

我认为这里的活动部分太多了。 如果你真的需要硒,还有其他的——好吧——那没问题。 但是,我会从尽可能简单的事情开始。

在 Python 2.7 上,我使用了机械化- 这样我就能够模拟与服务器的整个通信。 今天这不是最好的选择,因为 python 3.X 是要走的路。 我将描述我是如何处理此类问题的。 只是为了给你更好的图片,然后我会尝试描述可能的工具。

所以典型的情况是登录,翻页,转动一些开关,触发下载,或者获取内容并用漂亮的汤处理它。 首先,您需要查看交换了哪些信息。 转到 Web 浏览器中的开发工具,然后选择网络选项卡。 也许你知道,但这一步是强制性的,我想写一般的答案。 然后做你的正常工作 - 只需登录,并执行其他步骤。 服务器处理的所有事情都必须传输,因此您可以将其视为网络请求。 Mechanize 很好,因为我能够准备dict ,并将其作为post请求发送到页面。 post典型错误是发帖到页面地址 所以,如果你访问洁具index.html ,你正在做post在该网页上,而服务器希望它被送到add_user_data.html之后,你洁具重定向。 诸如会话 id 之类的东西可以通过标头条目或 cookie 来支持 - 只需查看模式的网络通信即可。

正如我写的那样,Python 2.7 将停产。 Mechanize 不适用于 Python 3.x,因此应使用其他工具。 你可以寻找机械化的替代品,看看什么对你来说是好的。 典型的答案是scrapy 这是一个有点不同的工具,更多地用于抓取网页。 因此,如果您计划更大的事情,那也许是最好的选择。 如果您需要单个脚本 - 我将从httpie开始。 命令行工具/python包,良好的OSX支持,可以发送表单,也可以进行会话管理 我每天都在使用它,但是我的服务器是无状态的。

我会更乐意提供确切的例子,但没有服务器信息是不可能的。 你能附上你的示例会话的转储吗? 将其匿名化,我将提供示例样本,或者其他工具可能会更糟?

在没有具体信息的情况下,我们能给您的唯一建议似乎与您如何理解正在发生的事情有关。

为了调试目的,在带头模式下手动逐步进行怎么样? 这里的赌注是您的问题在于自动化您的任务而不是无头的事实。

使用所有导入和函数定义(例如enable_download_in_headless_chrome )执行脚本,不使用这些定义。 实际上,这样做直到download_dir = # some path here ,然后在 Python Shell 中键入

>>> driver = webdriver.Chrome(chrome_options=chrome_options)

现在手动与浏览器交互并打开Chrome DevTools并转到Console 确保将显示错误。 让我们继续并输入其余的命令

>>> enable_download_in_headless_chrome(driver, download_dir)
>>> ...
>>> ok_button.click()

它说什么?

试试 String downloadFilepath ="\\\\sd-";

chromePrefs.put("download.default_directory", downloadFilepath);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM