Python 通过 PHP shell_exec() 调用时脚本挂起

Python Script Hangs When Called via PHP shell_exec()

我有一个 Python 脚本,使用 shell_exec 从 PHP 调用,一切正常,脚本在 Python 脚本已完成,一切正常。

现在我已经更改了 Python 脚本以包含一个 class 从它创建一个网络 driver 和 returns driver到通过 object 调用它的脚本,然后另一个函数关闭作为参数传入的 driver。

自从设置这个新的 class 并使用 chrome 添加 object 到 create/close 一个 driver for webdriver Python 脚本在完成后挂起,它永远不会将控制权交还给 PHP 脚本以完成处理。

我看过 php system() shell_exec() hangs the browser and also at http://php.net/manual/it/function.shell-exec.php#106250,但运气不好。

我需要PHP脚本到运行然后传给Python脚本,等待Python脚本完成再交回给PHP 脚本完成处理。

所有这个过程都是从 Run.php 开始的:

$reponse = shell_exec('python36 "script1.py" "https://www.example.com/" "23"');

目录结构

root_dir
    python_dir
        scripts_dir
            Script1.py
            Script2.py
            Script3.py
        Global.py
    run.php

Global.py

import re
from selenium import webdriver

class Global:

    def getDriver(self):
        try:
            chrome_options = webdriver.ChromeOptions()
            chrome_options.add_argument('headless')
            chrome_options.add_argument('no-sandbox')

            # Windows - Dev Environment
            driver = webdriver.Chrome(executable_path='C:\chromedriver.exe', chrome_options=chrome_options)
            # Linux - Prod Environment
            #driver = webdriver.Chrome(chrome_options=chrome_options)

            return driver
        except Exception as e:
            print('Error: ' + str(e.args[0]))

    def closeDriver(self, driver):
        try:
            driver.close()
            return None
        except Exception as e:
            print('Error: ' + str(e.args[0]))

scripts_dir 中的所有脚本都有不同的代码,具体取决于它们正在执行的任务,但是新的 Global object 内容对于所有脚本都是相同的, new Global object 是对所有脚本所做的唯一更改,因为它们到目前为止工作正常,Python 脚本挂起。

Script1.py

import os, sys
ROOT_DIR = os.path.normpath(os.path.join(os.path.abspath(__file__), '../..'))
sys.path.insert(0, ROOT_DIR)
from Global import Global

globalObj = Global()

try:
    # Load all items on page
    driver = globalObj.getDriver()
    driver.get(PARAMS[1])

    #... code to create a JSON file and populate it.

    # Close webdriver
    globalObj.closeDriver(driver)

    print('Completed Ref: ' + str(PARAMS[2]))

except Exception as e:
    print('Error: ' + str(e.args[0]))

Python 或 PHP 或我可用的任何其他日志中没有错误或警告,Python 脚本 运行 并创建预期的 JSON 文件他们应该并且看起来都正常工作,当 Python 脚本挂起时,行 print('Completed Ref: ' + str(PARAMS[2])) 似乎没有打印出来。

运行 Script1.py 在命令行上大约需要 35 秒才能成功完成。

更新 - 2018 年 2 月 27 日

如果我删除对 Global class 到 getDrivercloseDriver 的调用,那么脚本 运行 将完成并返回给 PHP 继续处理。

Script1.py - 更新

import os, sys
from selenium import webdriver

try:
    # Load all items on page
    chrome_options = webdriver.ChromeOptions()
    chrome_options.add_argument('headless')
    chrome_options.add_argument('no-sandbox')

    driver = webdriver.Chrome(executable_path='C:\chromedriver.exe', chrome_options=chrome_options)
    driver.get(PARAMS[1])

    #... code to create a JSON file and populate it.

    # Close webdriver
    driver.close()

    print('Completed Ref: ' + str(PARAMS[2]))

except Exception as e:
    print('Error: ' + str(e.args[0]))

脚本 运行 在命令行和通过 PHP 使用 shell_exec 执行时都很好,使用上面的例子,所以当它出现时一定有我遗漏的东西创建一个 object 并调用 getDrivercloseDriver classes.

2018 年 2 月 2 日 - 27 日更新

经过进一步测试,我可以确认问题是由 script1.py 脚本调用 class Global 创建网络 driver 并关闭它引起的,一旦我在 script1.py 中删除这些调用,脚本 运行 就可以了。

我试过在 globalObj.closeDriver() 之后加入 sys.exit(),我也试过在脚本末尾加入 del globalObj,但没有成功。

2018 年 2 月 3 - 27 日更新

我已经将 webdriver 从 Chrome 更改为 Firefox 和 PhantomJS,其他 drivers 在全局 class 上运行良好,看起来像可能与现阶段的Chromedriver有关。

我正在使用 Chrome 64.0.3282.186 (Official Build) (64-bit) 和 Chromedriver 2.35.

您可以在获取参数后尝试 WebDriverWait,它会等到进程结束

myElem = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.NAME, 'name_of_element')))

*我正在使用 firefox 驱动程序。

看起来 driver.close() 是问题的原因。您需要将 .quit() 与 Chromedriver 一起使用以结束 chromedriver 进程,否则该进程将不会终止,这就是 Python 脚本看起来像是挂起的原因。

发件人:

driver.close()

收件人:

driver.quit()