如何在C++中嵌入Python Selenium成功实现谷歌搜索HELLO并解决函数调用错误
Let's break down and fix your problems step by step. The core issues causing those AttributeError and "Cannot find function" messages, plus getting the Google search working, are easy to resolve once you spot them.
Key Problems Identified
Premature Python Module Destruction
Your__callpy_wargand__callpy_noargfunctions callPy_DECREF(pModule)after every function invocation. This destroys the Python module object immediately after the first call, so subsequent attempts to access functions likeclick_ELEMENTfail because the module no longer exists. That's why you're seeing those "Cannot find function" errors and weird object type mismatches.Uninitialized Variables in C++
The__callpy_wargfunction uses an undeclared variableiin the loop that builds the argument tuple. This will cause a compile error.Outdated Selenium Syntax
Your Python code usesdriver.find_element_by_name()which is deprecated in newer Selenium versions. Replacing it with the moderndriver.find_element(By.NAME, ...)syntax avoids warnings and ensures compatibility.
Corrected Code Implementations
Fixed C++ Code
#define PY_SSIZE_T_CLEAN #include <python/Python.h> #include <conio.h> #include <iostream> #include <string> #include <filesystem> #include <vector> // Assuming pyhelper.hpp is properly included using namespace std; #define PYSRCDIR "/PythonSrc/" PyObject* pName, *pModule, *pFunc; PyObject* pArgs, *pValue; string file; // Declare global file variable inline int __initialize_path(string __file) { file = __file; std::filesystem::path path = std::filesystem::current_path(); std::string _path = path.string(); std::replace(_path.begin(), _path.end(), '\\', '/'); if (!Py_IsInitialized()) { cout << "cpp >> INITIALIZE with <Py_Initialize();> first\n"; return -1; } PyRun_SimpleString("import sys"); PyRun_SimpleString((string("sys.path.append(\"") + _path + PYSRCDIR + "\")").c_str()); pName = PyUnicode_DecodeFSDefault(file.c_str()); pModule = PyImport_Import(pName); Py_DECREF(pName); return pModule ? 0 : -1; } inline int __callpy_noarg(string funcptr) { if (pModule == NULL) { PyErr_Print(); fprintf(stderr, "Failed to load module \"%s\"\n", file.c_str()); return 1; } pFunc = PyObject_GetAttrString(pModule, funcptr.c_str()); if (pFunc && PyCallable_Check(pFunc)) { pValue = PyObject_CallObject(pFunc, NULL); } else { if (PyErr_Occurred()) PyErr_Print(); fprintf(stderr, "Cannot find function \"%s\"\n", funcptr.c_str()); } Py_XDECREF(pFunc); // DO NOT DECREF pModule here - keep it for subsequent calls return 0; } inline int __callpy_warg(string funcptr, vector<string>* args) { if (pModule == NULL) { PyErr_Print(); fprintf(stderr, "Failed to load module \"%s\"\n", file.c_str()); return 1; } pFunc = PyObject_GetAttrString(pModule, funcptr.c_str()); if (pFunc && PyCallable_Check(pFunc)) { pArgs = PyTuple_New(args->size()); for (int i = 0; i < args->size(); ++i) { // Declare i explicitly string str = args->at(i); pValue = PyUnicode_FromString(str.c_str()); if (!pValue) { Py_DECREF(pArgs); fprintf(stderr, "Cannot convert argument %d\n", i); return 1; } PyTuple_SetItem(pArgs, i, pValue); } pValue = PyObject_CallObject(pFunc, pArgs); Py_DECREF(pArgs); if (pValue != NULL) { printf("Result of call: %ld\n", PyLong_AsLong(pValue)); Py_DECREF(pValue); } else { PyErr_Print(); fprintf(stderr, "Call to \"%s\" failed\n", funcptr.c_str()); return 1; } } else { if (PyErr_Occurred()) PyErr_Print(); fprintf(stderr, "Cannot find function \"%s\"\n", funcptr.c_str()); } Py_XDECREF(pFunc); // DO NOT DECREF pModule here return 0; } int main() { CPyInstance inst; // This should handle Py_Initialize() if (__initialize_path("pyemb3") != 0) { cerr << "Failed to initialize Python module\n"; return 1; } // Initialize Selenium with empty param (non-headless) std::vector<string> vec1 = {""}; __callpy_warg("init_selenium", &vec1); // Navigate to Google std::vector<string> vec2 = {"https://www.google.com/"}; __callpy_warg("get_search", &vec2); // Wait a moment for page to load (optional but helpful) std::vector<string> vec_wait = {"2"}; __callpy_warg("driver_WAIT", &vec_wait); // Get search box element by name "q" std::vector<string> vec3 = {"q"}; __callpy_warg("get_elem_NAME", &vec3); // Click the search box (optional, but ensures focus) std::vector<string> vec4 = {"0"}; __callpy_warg("click_ELEMENT", &vec4); // Type "HELLO" into the search box std::vector<string> vec5 = {"0", "HELLO"}; __callpy_warg("type_ELEMENT", &vec5); // Press Enter to search std::vector<string> vec6 = {"0", "\ue007"}; __callpy_warg("type_ELEMENT", &vec6); // Keep browser open for a few seconds std::vector<string> vec_wait2 = {"5"}; __callpy_warg("driver_WAIT", &vec_wait2); // Cleanup __callpy_noarg("driver_QUIT"); Py_DECREF(pModule); // Now we can safely destroy the module return 0; }
Updated Python Code
from selenium.webdriver.chrome.options import Options from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time import chromedriver_autoinstaller chromedriver_autoinstaller.install() driver = None elements_list = [] def init_selenium(param): global driver options = Options() paramlist = param.split(" ") if param else [] if "headless" in paramlist: options.add_argument("--headless") print("init_selenium --HEADLESS mode enabled") driver = webdriver.Chrome(options=options) print("init_selenium --Chrome driver initialized") def get_search(PATH): global driver if not driver: print("get_search --Selenium not initialized. Call init_selenium first.") return if PATH: driver.get(PATH) print(f"get_search --Navigated to {PATH}") else: print("get_search --NO PATH SPECIFIED") def wait_elem_XPATH(ELEMDATA, TIME): global driver if not driver: print("wait_elem_XPATH --Selenium not initialized") return try: element = WebDriverWait(driver, int(TIME)).until( EC.presence_of_element_located((By.XPATH, ELEMDATA))) elements_list.append(element) print(f"wait_elem_XPATH --Found element with XPath: {ELEMDATA}") except Exception as e: print(f"wait_elem_XPATH --Error: {str(e)}") def get_elem_XPATH(ELEMDATA): global driver if not driver: print("get_elem_XPATH --Selenium not initialized") return try: element = driver.find_element(By.XPATH, ELEMDATA) elements_list.append(element) print(f"get_elem_XPATH --Added element with XPath: {ELEMDATA}") except Exception as e: print(f"get_elem_XPATH --Error: {str(e)}") def get_elem_NAME(ELEMDATA): global driver if not driver: print("get_elem_NAME --Selenium not initialized") return try: element = driver.find_element(By.NAME, ELEMDATA) elements_list.append(element) print(f"get_elem_NAME --Added element with name: {ELEMDATA}") except Exception as e: print(f"get_elem_NAME --Error: {str(e)}") def click_ELEMENT(ELEMNUMBER): global elements_list try: elem_idx = int(ELEMNUMBER) if 0 <= elem_idx < len(elements_list): elements_list[elem_idx].click() print(f"click_ELEMENT --Clicked element at index {elem_idx}") else: print(f"click_ELEMENT --Invalid index {elem_idx}") except Exception as e: print(f"click_ELEMENT --Error: {str(e)}") def type_ELEMENT(ELEMNUMBER, TEXT): global elements_list try: elem_idx = int(ELEMNUMBER) if 0 <= elem_idx < len(elements_list): elements_list[elem_idx].send_keys(TEXT) print(f"type_ELEMENT --Typed '{TEXT}' into element at index {elem_idx}") else: print(f"type_ELEMENT --Invalid index {elem_idx}") except Exception as e: print(f"type_ELEMENT --Error: {str(e)}") def driver_QUIT(): global driver if driver: driver.quit() print("driver_QUIT --Chrome driver closed") driver = None def driver_WAIT(sec): global driver if driver: time.sleep(int(sec)) # Using time.sleep for explicit wait here print(f"driver_WAIT --Waited {sec} seconds")
What Changed & Why
Preserved Python Module
RemovedPy_DECREF(pModule)from the function call helpers. Now the module is only destroyed at the end ofmain(), allowing repeated function calls without losing access to the Python functions.Fixed Variable Declarations
Added explicitint ideclaration in the argument loop of__callpy_wargto resolve compile errors.Modernized Selenium Code
Replaced deprecatedfind_element_by_namewithfind_element(By.NAME, ...)and added error checking for uninitialized driver instances to make the code more robust.Added Explicit Waits
Added calls todriver_WAITto give the page time to load, which prevents race conditions where Selenium tries to interact with elements that haven't rendered yet.Improved Error Handling
Added print statements in Python functions to help debug what's happening at each step, making it easier to track issues if they arise.
Testing the Fix
When you run the corrected code:
- Chrome will open (non-headless mode)
- Navigate to Google's homepage
- Wait 2 seconds for the page to load
- Locate the search box element
- Click the search box to focus it
- Type "HELLO"
- Press Enter to trigger the search
- Wait 5 seconds to see the results
- Close the browser
This should resolve all your AttributeError and function-not-found issues, and successfully perform the Google search as intended.
内容的提问来源于stack exchange,提问作者S.T.A.L.K.E.R

