登录后Selenium无法点击StoryWeaver书籍卡片(macOS+Python)
问题描述
我正在用Selenium+Python自动化下载StoryWeaver平台的书籍。登录流程完全正常,但进入对应等级页面后,UI里明明能看到书籍卡片,脚本却无法点击卡片进入单本书籍详情页(PDF下载入口在详情页)。
脚本精简版本(登录部分正常):
import os, time from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.service import Service from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from webdriver_manager.chrome import ChromeDriverManager EMAIL = "…" PASSWORD = "…" LEVEL = 4 MAX_BOOKS = 10 OUTPUT_DIR = "storyweaver_pdfs" HOME_URL = f"https://storyweaver.org.in/en/stories?level={LEVEL}&query=&sort=Ratings" # — 登录选择器(全部可用) — LOGIN_BTN = (By.CSS_SELECTOR, "a[title='Sign Up/Log In']") EMAIL_METHOD = (By.CSS_SELECTOR, "a:has(div[title='Email'])") EMAIL_INPUT = (By.ID, "auth-modal-email") SUBMIT_BTN = (By.CSS_SELECTOR, "button.pb-button--in-group-last[type='submit']:not([class*='disabled'])") PASSWORD_INPUT = (By.ID, "auth-modal-password") LIBRARY_IND = (By.CSS_SELECTOR, "a[href='/en/library']") # 初始化驱动… os.makedirs(OUTPUT_DIR, exist_ok=True) prefs = { "download.default_directory": os.path.abspath(OUTPUT_DIR), "download.prompt_for_download": False, "plugins.always_open_pdf_externally": True, } opts = Options() opts.add_argument("--start-maximized") opts.add_experimental_option("prefs", prefs) opts.add_experimental_option("excludeSwitches", ["enable-automation"]) opts.add_experimental_option("useAutomationExtension", False) driver = webdriver.Chrome( service=Service(ChromeDriverManager().install()), options=opts ) wait = WebDriverWait(driver, 30) try: # 1) 登录(完全正常) driver.get(HOME_URL) wait.until(EC.element_to_be_clickable(LOGIN_BTN)).click() wait.until(EC.element_to_be_clickable(EMAIL_METHOD)).click() wait.until(EC.element_to_be_clickable(EMAIL_INPUT)).send_keys(EMAIL) wait.until(EC.element_to_be_clickable(SUBMIT_BTN)).click() wait.until(EC.element_to_be_clickable(PASSWORD_INPUT)).send_keys(PASSWORD) wait.until(EC.element_to_be_clickable(SUBMIT_BTN)).click() wait.until(EC.presence_of_element_located(LIBRARY_IND)) print("✅ 登录成功 — 当前在等级页面") # 2) 查找书籍卡片并点击(此处失败) card_selector = ( By.XPATH, "//a[contains(@href,'/stories/') and descendant::*[contains(@class,'story-card')]]" ) wait.until(EC.presence_of_all_elements_located(card_selector)) cards = driver.find_elements(*card_selector)[:MAX_BOOKS] print("找到", len(cards), "个卡片") for c in cards: c.click() # <<--- 无任何反应,不跳转页面 finally: driver.quit()
浏览器中观察到的书籍卡片HTML结构:
<a href="/en/stories/34911-the-case-of-the-missing-water" class="pb-link pb-link--default pb-book-card__link"> <div class="pb-book-card__container"> <div class="pb-book-card__wrapper"> <div class="pb-book-card__image-wrapper"> <div class="pb-img__wrapper"><img …></div> </div> <div class="pb-book-card__meta-wrapper"> <h3 class="pb-book-card__title">The Case of the Missing Water</h3> … </div> </div> </div> </a>
Selenium的click()方法无任何反应(无异常、无页面跳转)。已尝试以下方案但均无效:
- 使用JavaScriptExecutor执行点击:
driver.execute_script("arguments[0].click()", c) - 等待元素可见而非仅存在或可点击
- 点击内部
<h3>标签或图片容器而非<a>标签 - 将卡片滚动到可见区域
- 点击前添加短暂等待(
time.sleep(1))
疑问
- 为何元素可见且可用,但Selenium无法点击?
- 哪种定位器或点击策略能可靠打开书籍详情页?
- 能否提供从等级页跳转到书籍详情页的精简代码示例?
解决方案
1. 无法点击的原因
- 定位器精度不足:原XPath选择器可能匹配到不可交互的隐藏元素,或者页面中存在多个符合条件的
<a>标签,其中部分并非目标书籍卡片。 - 页面交互状态未就绪:元素虽已存在,但页面可能未完成事件绑定,或存在隐形遮罩(如未完全加载的弹窗、广告层)阻挡点击区域。
- Stale Element问题:页面动态更新后,之前定位的元素已失效,点击操作无法触发交互。
2. 可靠的定位器与点击策略
- 使用精准CSS选择器:直接通过卡片的专属类定位,比如
By.CSS_SELECTOR, "a.pb-book-card__link",避免模糊匹配带来的误判。 - 优先等待元素可交互:用
EC.element_to_be_clickable替代仅判断元素存在,确保元素处于可点击状态。 - 循环中重新定位元素:每次处理卡片前重新查找元素,避免页面刷新导致的元素失效。
- 直接跳转URL:若点击交互始终失败,可提取卡片的
href属性,用driver.get()直接访问详情页,绕过点击环节。
3. 精简代码示例(替换原脚本第2部分)
方案1:优化点击交互
# 2) 查找书籍卡片并跳转详情页 card_selector = (By.CSS_SELECTOR, "a.pb-book-card__link") # 等待至少一个卡片可点击 wait.until(EC.element_to_be_clickable(card_selector)) # 循环处理前MAX_BOOKS个卡片 for i in range(MAX_BOOKS): # 每次循环重新定位,避免stale element cards = wait.until(EC.presence_of_all_elements_located(card_selector)) if i >= len(cards): break current_card = cards[i] # 滚动到元素可见区域 driver.execute_script("arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});", current_card) # 等待元素完全可点击后执行点击 wait.until(EC.element_to_be_clickable(current_card)).click() # 等待详情页加载完成(根据详情页特征元素判断) wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "div.pb-story-header__wrapper"))) print(f"✅ 进入第{i+1}本书详情页") # 此处添加PDF下载逻辑... # 返回等级页面继续处理下一个 driver.back() # 等待等级页面重新加载完成 wait.until(EC.presence_of_element_located(card_selector))
方案2:直接跳转URL(更稳妥)
# 2) 提取卡片URL并直接跳转 card_selector = (By.CSS_SELECTOR, "a.pb-book-card__link") wait.until(EC.presence_of_all_elements_located(card_selector)) cards = driver.find_elements(*card_selector)[:MAX_BOOKS] for idx, card in enumerate(cards): book_url = card.get_attribute("href") driver.get(book_url) # 等待详情页加载完成 wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, "div.pb-story-header__wrapper"))) print(f"✅ 进入第{idx+1}本书详情页") # 此处添加PDF下载逻辑... driver.back() wait.until(EC.presence_of_element_located(card_selector))
内容的提问来源于stack exchange,提问作者Mohammad Malik
相关产品推荐
相关产品推荐

