使用Selenium的航班爬虫运行无响应,VS Code无报错求助
航班价格追踪程序无响应问题排查与修复
问题概述
开发基于Selenium的航班价格追踪毕业项目,VS Code无语法报错,但程序运行后无任何反应,需强制终止。已更新Chrome浏览器及对应ChromeDriver、Python为最新版本,检查缩进无问题。
原代码
from selenium import webdriver # 缺失的导入,需添加 from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException import time import pandas as pd import smtplib from email.message import EmailMessage import schedule departure_flight_inputs={'Departure': " ORD", 'Arrival': " LAS", 'Date': "Jun 1, 2024"} return_flight_inputs={'Departure': " LAS", 'Arrival': " ORD", 'Date': "Jun 8, 2024"} def find_cheapest_flights(flight_info): PATH = R"C:\Users\theya\OneDrive\Desktop\FlightPriceTracker\chromedriver.exe" driver = webdriver.Chrome(executable_path=PATH) leaving_from = flight_info['Departure'] going_to = flight_info['Arrival'] trip_date = flight_info['Date'] #Go to Expedia driver.get( "https://expedia.com") #Click on Flights # The flight_xpath represent the flight button on expedia flight_xpath = "//a[@class='uitk-tab-anchor' and @aria-controls='search_form_product_selector_flights']" # 移除selected类 flight_element = WebDriverWait(driver,5).until( EC.presence_of_element_located((By.XPATH, flight_xpath)) ) flight_element.click() time.sleep(0.2) #Click on One-Way. IBecause it is easier this way. oneway_xpath = "//a[@class='uitk-tab-anchor' and @aria-controls='FlightSearchForm_ONE_WAY']" # 移除selected类 one_way_element = WebDriverWait(driver,5).until( EC.presence_of_element_located((By.XPATH, oneway_xpath)) ) one_way_element.click() time.sleep(0.2) #Part 1: Flying From, Flying To, Departure Date, Return Date #********************** Complete Leaving From Portion ********************** leaving_from_xpath = "//button[@aria-label='Leaving from' and @data-stid='origin_select-dialog-trigger']" leaving_from_element = WebDriverWait(driver,5).until( EC.presence_of_element_located((By.XPATH, leaving_from_xpath)) ) leaving_from_element.clear() # 添加括号调用方法 leaving_from_element.click() time.sleep(1) leaving_from_element.send_keys(leaving_from) time.sleep(1) #Need this otherwise it will be too fast for the broswer leaving_from_element.send_keys(Keys.DOWN, Keys.RETURN) #********************** Complete Leaving From Portion ********************** #********************** Complete Going To Portion ********************** going_to_xpath = "//button[@aria-label='Going to' and @data-stid='destination_select-dialog-trigger']" going_to_element = WebDriverWait(driver,5).until( EC.presence_of_element_located((By.XPATH, going_to_xpath)) ) going_to_element.clear() # 添加括号调用方法 going_to_element.click() time.sleep(1) going_to_element.send_keys(going_to) time.sleep(1) #Need this otherwise it will be too fast for the broswer going_to_element.send_keys(Keys.DOWN, Keys.RETURN) #Go down on item and click on it #********************** Complete Going To Portion ********************** #********************** Complete Departure Date Portion ********************** departing_box_xpath = '//button[contains(@aria-label,"Departing")]' depart_box_element = WebDriverWait(driver,5).until( EC.presence_of_element_located((By.XPATH, departing_box_xpath)) ) depart_box_element.click() #Click on the departure box time.sleep(2) #Find the current date. WILL arrow through too trip_date_xpath = '//button[contains(@aria-label,"{}")]'.format(trip_date) departing_date_element = None max_attempts = 12 # 添加循环次数限制 attempts = 0 while departing_date_element is None and attempts < max_attempts: try: departing_date_element = WebDriverWait(driver,3).until( EC.presence_of_element_located((By.XPATH, trip_date_xpath)) ) departing_date_element.click() #Click on the departure date except TimeoutException: attempts +=1 next_month_xpath = "//button[@data-stid='uitk-calendar-navigation-controls-next-button']" driver.find_element(By.XPATH, next_month_xpath).click() # 改用新版Selenium语法 time.sleep(1) if departing_date_element is None: print(f"无法找到日期: {trip_date}") driver.quit() return [] depart_date_done_xpath = "//button[@data-stid='apply-date-selector' and text()='Done']" driver.find_element(By.XPATH, depart_date_done_xpath).click() # 改用新版Selenium语法 #********************** Complete Departure Date Portion ********************** #********************** Click Search ********************** search_button_xpath = "//button[@id='search_button' and @type='submit' and @aria-label='Search' and text()='Search']" driver.find_element(By.XPATH, search_button_xpath).click() # 改用新版Selenium语法 time.sleep(15) #Need to let the page load properly #********************** Click Search ********************** #Part 2: Setting Conditions for our flight #********************** Check for Nonstop Flights Sorted by Lowest Price ********************** nonstop_flight_xpath = "//label[span[text()='Nonstop']]" # 简化XPATH,避免动态ID if len(driver.find_elements(By.XPATH, nonstop_flight_xpath)) > 0: # 改用新版Selenium语法 driver.find_element(By.XPATH, nonstop_flight_xpath).click() # 改用新版Selenium语法 time.sleep(5) #Check if there are available flights available_flights = driver.find_elements(By.XPATH, "//span[contains(text(),'Select and show fare information ')]") # 改用新版Selenium语法 if len(available_flights) > 0: if len(available_flights) == 1: #Don't have to sort by prices here flights = [(item.text.split(",")[0].split('for')[-1].title(), item.text.split(",")[1].title().replace("At",":"), item.text.split(",")[2].title().replace("At",":"), item.text.split(",")[3].title().replace("At",":")) for item in available_flights[0:5]] else: #Sort by lowest prices driver.find_element(By.XPATH, "//select[@id='sort-filter-dropdown-SORT']/option[@value='PRICE_INCREASING']").click() # 改用新版Selenium语法 time.sleep(5) flights = [(item.text.split(",")[0].split('for')[-1].title(), item.text.split(",")[1].title().replace("At",":"), item.text.split(",")[2].title().replace("At",":"), item.text.split(",")[3].title().replace("At",":")) for item in available_flights[0:5]] print("Conditions satisfied for: {}:{}, {}:{}, {}:{}".format("Departure",leaving_from, "Arrival",going_to, "Date",trip_date)) driver.quit() return flights else: print('Not all conditions could be met for the following: "{}:{}, {}:{}, {}:{}'.format("Departure",leaving_from, "Arrival",going_to, "Date",trip_date)) driver.quit() return [] #********************** Check for Nonstop Flights Sorted by Lowest Price ********************** def send_email(): #Get return values departing_flights = find_cheapest_flights(departure_flight_inputs) return_flights = find_cheapest_flights(return_flight_inputs) #Put it into a dataframe to visualize this more easily df = pd.DataFrame(departing_flights + return_flights) if not df.empty: #Only send an email if we have actual flight info email = "theyamarlyons@gmail.com" # 直接赋值邮箱,而非读取文件 password = "$mallJoke11" # 实际项目建议用环境变量存储密码 msg = EmailMessage() msg['Subject'] = "Python Flight Info! {} --> {}, Departing: {}, Returning: {}".format(departure_flight_inputs['Departure'], departure_flight_inputs['Arrival'], departure_flight_inputs['Date'],return_flight_inputs['Date']) msg['From'] = email msg['To'] = email msg.add_alternative('''\ <!DOCTYPE html> <html> <body> {} </body> </html>'''.format(df.to_html()), subtype="html") with smtplib.SMTP_SSL('smtp.gmail.com',465) as smtp: smtp.login(email,password) smtp.send_message(msg) schedule.clear() schedule.every(30).minutes.do(send_email) while True: schedule.run_pending() time.sleep(1)
核心修复点
- 缺失WebDriver导入:添加
from selenium import webdriver,否则程序会因找不到webdriver对象静默报错。 - XPATH定位错误:移除Flights/One-Way按钮XPATH中的
uitk-tab-anchor-selected类(首次打开页面时默认未选中),简化Nonstop筛选的XPATH避免动态ID问题。 - 未调用clear()方法:将
clear改为clear(),实际执行清空输入框操作。 - 日期循环无限执行风险:添加最大循环次数限制,避免因目标日期不存在导致无限循环。
- 旧版Selenium语法替换:将
find_element_by_xpath替换为find_element(By.XPATH, ...),适配Selenium 4版本。 - 邮件读取逻辑错误:直接赋值邮箱字符串,而非试图读取名为邮箱地址的文件。
调试建议
- 启用ChromeDriver日志:添加以下代码初始化driver,查看详细运行日志定位问题:
from selenium.webdriver.chrome.options import Options options = Options() options.add_argument("--log-level=DEBUG") driver = webdriver.Chrome(executable_path=PATH, options=options) - 在关键步骤(如页面跳转、元素点击)后添加
print语句,确认程序执行到的位置。
内容的提问来源于stack exchange,提问作者Yamar Lyons
相关产品推荐
相关产品推荐

