[系统] 正在启动浏览器(国内镜像模式)...============================================================铁路查询============================================================▶ 出发站 (上海虹桥):▶ 到达站 (北京南): 太仓南▶ 日期 (2026-08-08):🔍 正在检索: 上海虹桥 ➔ 太仓南...车次 |出发站 |到达站 |时间(历时)-----------------------------------------------------------------G8352 |上海虹桥 |太仓南 |13:22-13:48 (0:26)C3772 |上海虹桥 |太仓南 |15:09-15:35 (0:26)G8364 |上海虹桥 |太仓南 |18:56-19:22 (0:26)-----------------------------------------------------------------📊 统计:找到 3 趟直达车次。[询问] 是否继续? (y/n, 默认y):============================================================铁路查询============================================================▶ 出发站 (上海虹桥):▶ 到达站 (北京南): 苏州北▶ 日期 (2026-08-08):🔍 正在检索: 上海虹桥 ➔ 苏州北...车次 |出发站 |到达站 |时间(历时)-----------------------------------------------------------------G1970 |上海虹桥 |苏州北 |06:09-06:32 (0:23)G1802 |上海虹桥 |苏州北 |06:13-06:36 (0:23)G1772 |上海虹桥 |苏州北 |06:35-06:58 (0:23)G2 |上海虹桥 |苏州北 |06:43-07:04 (0:21)后面太多不一一展示了...

def get_driver_path():"""按优先级获取 chromedriver 路径:1) 环境变量 CHROMEDRIVER_PATH 手动指定2) 脚本同目录下的 chromedriver(.exe)3) 通过国内镜像自动下载并缓存"""env_path = os.environ.get("CHROMEDRIVER_PATH")if env_path and os.path.exists(env_path):return env_pathlocal_name = "chromedriver.exe" if platform.system() == "Windows" else "chromedriver"local_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), local_name)if os.path.exists(local_path):return local_pathmajor = get_chrome_major_version()if not major:raise RuntimeError(...)return download_chromedriver_from_mirror(major)
CFT_CUTOVER_MAJOR = 115chrome_options.add_argument("--headless=new")chrome_options.add_argument("user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 ""(KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1")chrome_options.add_argument("--disable-blink-features=AutomationControlled")chrome_options.add_experimental_option("excludeSwitches", ["enable-automation"])chrome_options.add_experimental_option("useAutomationExtension", False)
driver.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument",{"source": "Object.defineProperty(navigator, 'webdriver', {get: () => undefined})"},)
last_count = 0stable_rounds = 0for _ in range(15): # 最多滚动 15 轮,防止极端情况下死循环driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")time.sleep(1.2)current_count = len(driver.find_elements(By.XPATH, item_xpath))if current_count == last_count:stable_rounds += 1if stable_rounds >= 2:breakelse:stable_rounds = 0last_count = current_count
train_match = re.search(r'\b([GDTZKCS]\d{1,5}|\d{4,5})\b', text)times = re.findall(r'(\d{2}:\d{2})', text)
def station_in_text(text, station_name):"""精确匹配站名,避免子串误判(例如 "苏州" 不应匹配到 "苏州北")。"""pattern = re.escape(station_name) + r'(?:站)?'regex = r'(?<![\u4e00-\u9fff])' + pattern + r'(?![\u4e00-\u9fff])'return re.search(regex, text) is not Nonedef normalize_station(name):"""去掉站名末尾的"站"字,便于统一比较("苏州站" 与 "苏州" 视为同一站)"""if name and name.endswith("站"):return name[:-1]return name
station_in_text用了正则的负向零宽断言((?<!...) 和 (?!...)),要求目标站名的前后不能紧跟其他中文字符——这样"苏州"就不会命中"苏州北"里的"苏州",因为后面紧跟着"北"字。同时用 (?:站)? 兼容"苏州"和"苏州站"两种写法。normalize_station 则负责把"苏州站"统一成"苏州",方便后续比较时忽略"站"字的差异。def find_station_for_time(text, time_str, expected_norm):idx = text.find(time_str)before = text[:idx]after = text[idx + len(time_str):]m_before = re.search(r'([\u4e00-\u9fff]{2,10})\s*$', before)m_after = re.search(r'^\s*([\u4e00-\u9fff]{2,10})', after)candidates = []if m_before:candidates.append(m_before.group(1))if m_after:candidates.append(m_after.group(1))for c in candidates:if normalize_station(c) == expected_norm:return creturn candidates[0] if candidates else None
def get_display_width(text):"""计算含中文字符的显示宽度(中文按 2 个字符宽计算)"""width = 0for char in text:if '\u4e00' <= char <= '\u9fff':width += 2else:width += 1return widthdef align_text(text, target_width):current_width = get_display_width(text)padding = max(0, target_width - current_width)return text + (" " * padding)
claude code
[==============往期推荐==============]
点击标题可直接跳转