本地正常的Python Selenium脚本在GCP虚拟机抛出ElementNotInteractableException
Selenium脚本本地正常,部署到GCP虚拟机后点击元素抛出ElementNotInteractableException
我有一个抓取Wolt餐厅页面的Python Selenium脚本,本地运行完全正常,但部署到Google Cloud虚拟机后,点击页面左上角地址图标(XPATH选择器为//div[@data-projection-id='2']/button)时抛出ElementNotInteractableException。
以下是精简后的代码,最后一行执行失败:
from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import logging logging.basicConfig( filename="wolt_scraping_logs.log", level="INFO", format="%(asctime)s - %(levelname)s - %(message)s", datefmt="%Y-%m-%d %H:%M:%S", encoding="utf-8" ) # Set the Chrome options chrome_options = Options() chrome_options.add_argument("start-maximized") # Required for a maximized Viewport chrome_options.add_experimental_option('excludeSwitches', ['enable-logging', 'enable-automation', 'disable-popup-blocking']) # Disable pop-ups to speed up browsing chrome_options.add_experimental_option("detach", True) # Keeps the Chrome window open after all the Selenium commands/operations are performed chrome_options.add_experimental_option('prefs', {'intl.accept_languages': 'en,en_US'}) # Operate Chrome using English as the main language chrome_options.add_argument('--blink-settings=imagesEnabled=false') # Disable images chrome_options.add_argument('--disable-extensions') # Disable extensions chrome_options.add_argument("--headless=new") # Operate Selenium in headless mode chrome_options.add_argument('--no-sandbox') # Disables the sandbox for all process types that are normally sandboxed. Meant to be used as a browser-level switch for testing purposes only chrome_options.add_argument('--disable-gpu') # An additional Selenium setting for headless to work properly, although for newer Selenium versions, it's not needed anymore chrome_options.add_argument("--window-size=1920x1080") # Set the Chrome window size to 1920 x 1080 # Global inputs base_url = "https://wolt.com/en/discovery/restaurants" # Instantiate a webdriver driver = webdriver.Chrome(options=chrome_options) # Navigate to the target URL logging.info("Navigate to the target URL...") driver.get(base_url) # Maximize the window logging.info("Maximize the window...") driver.maximize_window() # Click on the "Accept Cookies" button logging.info("Click on the 'Accept Cookies' button") WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//button[@data-localization-key='gdpr-consents.banner.accept-button']"))) driver.find_element(by=By.XPATH, value="//button[@data-localization-key='gdpr-consents.banner.accept-button']").click() # Click on the "Delivery to" button logging.info("Click on the 'Delivery to' button") WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//div[@data-projection-id='2']/button"))) driver.find_element(by=By.XPATH, value="//div[@data-projection-id='2']/button").click() # The code fails here
虚拟机上的错误信息:
Traceback (most recent call last): File "wolt-crawler/wolt_finland_scraping_script.py", line 73, in <module> driver.find_element(by=By.XPATH, value="//div[@data-projection-id='2']/button").click() File "/home/omarmoataz6/venv-scraping/lib/python3.8/site-packages/selenium/webdriver/remote/webelement.py", line 93, in click self._execute(Command.CLICK_ELEMENT) File "/home/omarmoataz6/venv-scraping/lib/python3.8/site-packages/selenium/webdriver/remote/webelement.py", line 403, in _execute return self._parent.execute(command, params) File "/home/omarmoataz6/venv-scraping/lib/python3.8/site-packages/selenium/webdriver/remote/webdriver.py", line 440, in execute self.error_handler.check_response(response) File "/home/omarmoataz6/venv-scraping/lib/python3.8/site-packages/selenium/webdriver/remote/errorhandler.py", line 245, in check_response raise exception_class(message, screen, stacktrace) selenium.common.exceptions.ElementNotInteractableException: Message: element not interactable (Session info: chrome=112.0.5615.49) Stacktrace: #0 0x5629637d4603 <unknown> #1 0x562963525741 <unknown> #2 0x562963562c43 <unknown> #3 0x562963557025 <unknown> #4 0x562963582402 <unknown> #5 0x562963556891 <unknown> #6 0x5629635825ce <unknown> #7 0x56296359a9a9 <unknown> #8 0x5629635821d3 <unknown> #9 0x562963554fb8 <unknown> #10 0x5629635560ee <unknown> #11 0x56296379b046 <unknown> #12 0x56296379e3c3 <unknown> #13 0x56296379ec5a <unknown> #14 0x5629637a6b0e <unknown> #15 0x56296379f3ec <unknown> #16 0x562963775ea8 <unknown> #17 0x5629637bf318 <unknown> #18 0x5629637bf4c3 <unknown> #19 0x5629637cce34 <unknown> #20 0x7fdad2727609 <unknown>
问题原因及解决方案
核心原因
本地与虚拟机环境的差异集中在无头模式页面渲染、元素交互时机、页面加载状态三个方面:
- 无头模式下页面渲染布局可能与有头模式不同,导致元素被遮挡或未完全加载
presence_of_element_located仅等待元素存在于DOM,但不保证元素可见、可交互- 虚拟机网络延迟可能导致页面加载速度慢于本地,元素未就绪就执行点击操作
具体解决方案
1. 替换等待条件,确保元素可交互
将presence_of_element_located替换为element_to_be_clickable,该条件会等待元素同时满足存在、可见、可交互三个状态:
# 替换原有的地址按钮点击代码 WebDriverWait(driver, 15).until(EC.element_to_be_clickable((By.XPATH, "//div[@data-projection-id='2']/button"))).click()
2. 优化无头模式Chrome配置
虚拟机上的无头模式需要针对性配置,避免渲染异常:
- 移除冲突的窗口设置:同时设置
start-maximized和window-size会导致无头模式渲染混乱,优先保留window-size并移除driver.maximize_window()(无头模式下该方法无效) - 添加
--disable-dev-shm-usage:解决虚拟机/dev/shm空间不足问题,避免页面渲染失败 - 移除无效配置:
detach在无头模式下不生效,可直接删除
修改后的Chrome配置:
chrome_options = Options() chrome_options.add_experimental_option('excludeSwitches', ['enable-logging', 'enable-automation', 'disable-popup-blocking']) chrome_options.add_experimental_option('prefs', {'intl.accept_languages': 'en,en_US'}) chrome_options.add_argument('--blink-settings=imagesEnabled=false') chrome_options.add_argument('--disable-extensions') chrome_options.add_argument("--headless=new") chrome_options.add_argument('--no-sandbox') chrome_options.add_argument('--disable-gpu') chrome_options.add_argument("--window-size=1920x1080") chrome_options.add_argument('--disable-dev-shm-usage') # 新增:解决共享内存不足问题
3. 处理元素遮挡与滚动
虚拟机上可能存在额外弹窗或布局偏移导致元素被遮挡,可通过滚动确保元素可见:
import time delivery_button = WebDriverWait(driver, 15).until(EC.element_to_be_clickable((By.XPATH, "//div[@data-projection-id='2']/button"))) # 滚动到元素可见位置 driver.execute_script("arguments[0].scrollIntoView(true);", delivery_button) # 短暂等待元素状态稳定 time.sleep(0.5) delivery_button.click()
4. 优化元素定位器稳定性
data-projection-id属于动态生成属性,不同环境下可能变化,建议使用更稳定的定位方式,比如结合元素的功能描述:
# 示例:通过按钮的aria-label属性定位 By.XPATH, "//button[contains(@aria-label, 'Delivery address')]"
内容的提问来源于stack exchange,提问作者oelmaria
相关产品推荐
相关产品推荐

