def click_btn(self, xpath, el=None): try: if not el: btn = self.wait.until( EC.presence_of_element_located((By.XPATH, xpath))) if btn.is_enabled(): btn.click() else: logger.debug(f"click_btn:{xpath}该元素不可操作") self.run_status = False self.result["status"] = 401 self.result["errorMessage"] = "获取的元素不可用" else: el.find_element_by_xpath(xpath=xpath).click() time.sleep(2) except TimeoutException: logger.error(f"点击{xpath}超时") self.run_status = False self.result["status"] = 401 self.result["errorMessage"] = "获取的元素超时" except Exception as e: logger.error(f"定位{xpath}时,点击click时出错,错误信息:{str(e)}") self.run_status = False self.result["status"] = 401 self.result["errorMessage"] = "获取的元素出现错误"
def fill_card_info(self): """ 输入卡号信息 :return: """ card_num_xpath = '//*[@id="cardNumber"]' card_name_xpath = '//*[@id="cardholderName"]' month_xpath = '//*[@id="expiryMonth"]' year_xpath = '//*[@id="expiryYear"]' cvv_code_xpath = '//*[@id="securityCode"]' card_info_dict = { card_num_xpath: self.payment_card_info["cardNumber"], card_name_xpath: self.payment_card_info["lastName"] + "/" + self.payment_card_info["firstName"], month_xpath: self.payment_card_info["cardExpired"].split("-")[1], year_xpath: self.payment_card_info["cardExpired"].split("-")[0], cvv_code_xpath: self.payment_card_info["cvv"], } for k, v in card_info_dict.items(): self.fill_input(content=v, xpath=k) logger.debug("填写卡号信息完成")
def fill_input(self, content, xpath, single_input=False): """ 获取到xpath表达式,定位元素,输入内容 :param args: :param kwargs: :return: """ try: input_content = self.wait.until( EC.presence_of_element_located((By.XPATH, xpath))) if input_content.is_enabled(): # 一个一个字母输入 input_content.clear() if single_input: for item in content: input_content.send_keys(item) time.sleep(0.7) else: input_content.send_keys(content) else: logger.debug(f"fill_input:{xpath}该元素不可操作") self.run_status = False self.result["status"] = 401 self.result["errorMessage"] = "获取的元素不可用" except Exception as e: logger.error(f"定位{xpath}时,填写{content}时出错,错误信息:{str(e)}") self.run_status = False self.result["status"] = 401 self.result["errorMessage"] = "获取元素失败"
def pay(self): """ 点击进行支付 :return: """ logger.debug("开始支付") pay_btn_xpath = '//*[@id="submitButton"]' self.click_btn(pay_btn_xpath)
def fill_passenger_info(self): """ 填写乘客信息 :return: """ # 关闭登陆提示框 close_btn_xpath = '//*[@id="loginform-modal"]/div/div/div[1]/a' self.click_btn(close_btn_xpath) # 获取所有的乘客输入div标签 form_passenger_xpath = '//ng-form[@name="passengersCtl.paxInfo"]/div[1]/div' form_passengers_obj = self.get_ele_list(form_passenger_xpath) for index, passenger in enumerate(self.passengers_info): # index : 乘客对应的form输入框 # 名字 firstname_xpath = './form//ul/li[1]/input' lastname_xpath = './form//ul/li[2]/input' self.fill_input(content=passenger["name"].split("/")[1], xpath=firstname_xpath, el=form_passengers_obj[index]) self.fill_input(content=passenger["name"].split("/")[0], xpath=lastname_xpath, el=form_passengers_obj[index]) man_xpath = './form/div[1]/div[4]/div[2]/div/div[2]/ul/li[3]/span[1]' female_xpath = './form/div[1]/div[4]/div[2]/div/div[2]/ul/li[3]/span[2]' if passenger["sex"] == "F": # 女生 self.click_btn(el=form_passengers_obj[index], xpath=female_xpath) else: self.click_btn(el=form_passengers_obj[index], xpath=man_xpath) # 输入出生日期 # 在输入日期之前先点击输入框 birthday_xpath = './form//ul/li[4]/input' self.click_btn(xpath=birthday_xpath, el=form_passengers_obj[index]) birthday = "".join(passenger["birthday"].split("-")) self.fill_input(xpath=birthday_xpath, content=birthday, el=form_passengers_obj[index], single_input=False) # 选择国家 if index == 0: country_xpath = './form/div[2]/ul/li[1]/dl/dd/div[1]/label/input' self.fill_input(content="日本", xpath=country_xpath, el=form_passengers_obj[index]) # 填写完毕,点击下一步 next_xpath = '//dd[@class="usr-action-box--jp-b next"]/button' self.click_btn(xpath=next_xpath) logger.debug("乘客信息填写完毕") time.sleep(3)
def select_payment_method(self): """ 选择支付方式 :return: """ # 选用VCC visa vcc_xpath = '//div[@class="cardtype ng-scope"]/label[2]/span' self.click_btn(vcc_xpath) time.sleep(5) logger.debug("支付方式选择完毕") # 点击下一步 next_xpath = '//div[@class="vnl-payment-action"]/a[@data-id="act-next"]' self.click_btn(next_xpath) time.sleep(5)
def click_btn(self, xpath): try: btn = self.wait.until( EC.presence_of_element_located((By.XPATH, xpath))) if btn.is_enabled(): btn.click() time.sleep(1) else: logger.debug(f"click_btn:{xpath}该元素不可操作") self.run_status = False except TimeoutException: logger.error(f"点击{xpath}超时") self.run_status = False except Exception as e: logger.error(f"定位{xpath}时,点击click时出错,错误信息:{str(e)}") self.run_status = False
def click_btn(self, xpath, el=None): try: if not el: btn = self.wait.until( EC.presence_of_element_located((By.XPATH, xpath))) if btn.is_enabled(): btn.click() time.sleep(1) else: logger.debug(f"click_btn:{xpath}该元素不可操作") self.run_status = False else: el.find_element_by_xpath(xpath).click() except TimeoutException: logger.error(f"点击{xpath}超时") self.run_status = False raise StopException("点击超时") except Exception as e: logger.error(f"定位{xpath}时,点击click时出错,错误信息:{str(e)}") self.run_status = False
def sure_info(self): """ 确认所选信息, 校对选择的行李是否正确,获取行李的总价格回填行李价格和其他费用 :return: """ if sum(self.luggages_weight) > 0: # 有行李 # 获取行李价格 detail_info_list_xpath = '//div[@data-id="flight_1"]//div[@class="vnl-fare-detail-summary-detail-box vnl-fare-detail-summary-detail__blk"][2]/div' detail_info_list = self.get_ele_list(detail_info_list_xpath) for item in detail_info_list: if "託運行李" in self.get_text(xpath='./p', el=item): luggage_price_xpath = './/dd[@class="price ng-binding"]' luggage_price = self.get_text(xpath=luggage_price_xpath, el=item).split()[1].replace( ",", "") # 'HKD 1,976' self.fill_back["baggagePrice"] = float(luggage_price) break else: # 回填行李重量和价格 self.fill_back["baggagePrice"] = 0.0 # 获取价格,去掉行李的价格 total_price_xpath = '//div[@class="vnl_itinerary_price-total_price"]/span/span' total_price = self.get_text( xpath=total_price_xpath).split()[1].replace(",", "") self.fill_back["price"] = float( total_price) - self.fill_back["baggagePrice"] # 同意协议 sure_xpath = '//div[@class="reconfirm_agreement_check"]/p//span[1]' self.click_btn(sure_xpath) # 点击下一步 pay_btn_xpath = '//a[@data-id="act-purchase"]' self.click_btn(pay_btn_xpath) logger.debug("同意协议点击无误") time.sleep(10)
def fill_contact_info(self): """ 填写联系人信息 :return: """ # 填写邮箱 email_xpath = '//div[@class="vnl-payment-contact-information"]//div[@class="right"]/dl[1]/dd//input' self.fill_input(xpath=email_xpath, content=self.contact["linkEmail"]) email_sure_xpath = '//div[@class="vnl-payment-contact-information"]//div[@class="right"]/dl[2]/dd//input' self.fill_input(xpath=email_sure_xpath, content=self.contact["linkEmail"]) # 联系人区号 area_code_xpath = '//div[@class="vnl-payment-contact-information"]//div[@class="left"]/dl[4]/dd//input' self.fill_input(xpath=area_code_xpath, content=100000) # 联系人电话 phone_num_xpath = '//div[@class="vnl-payment-contact-information"]//div[@class="left"]/dl[5]/dd//input' self.fill_input(xpath=phone_num_xpath, content=self.contact["linkPhone"]) logger.debug("联系人信息填写完毕")
def fill_input(self, content, xpath, single_input=False, el=None): """ 获取到xpath表达式,定位元素,输入内容 :param args: :param kwargs: :return: """ try: if not el: input_content = self.wait.until( EC.presence_of_element_located((By.XPATH, xpath))) if input_content.is_enabled(): # 一个一个字母输入 input_content.clear() if single_input: for item in content: input_content.send_keys(item) time.sleep(0.7) else: input_content.send_keys(content) else: logger.debug(f"fill_input:{xpath}该元素不可操作") self.run_status = False else: if not single_input: el.find_element_by_xpath(xpath).send_keys(content) else: for item in content: el.find_element_by_xpath(xpath).send_keys(item) time.sleep(0.3) except TimeoutException: logger.error("填写信息超时") self.run_status = False raise StopException("填写信息超时") except Exception as e: logger.error(f"定位{xpath}时,填写{content}时出错,错误信息:{str(e)}") self.run_status = False
def main(): pur = Purchase() # 初始化浏览器,选择航班 if pur.run_status: pur.select_flight() else: # 初始化失败 logger.error("初始化失败") pur.result["status"] = 401 pur.result["errorMessage"] = "因为网络波动,浏览器初始化失败" # 选择航班成功,选择行李,座位等 if pur.run_status: if pur.child_num: # 需要选择座位 click_seat = pur.select_site() if isinstance(click_seat, list): # 点击座位 pur.click_select_seat(click_seat) # 选择行李 pur.select_packages() else: pur.select_packages() else: logger.debug("选择航班错误") # 选择结束,点击继续, 进行登陆 if pur.run_status: pur.loggin() else: logger.debug("选择行李或选择座位错误") # 登陆成功,进行支付 if pur.run_status: pur.fill_payment_info() else: logger.debug("登陆失败") if pur.run_status: pur.check_out_price() # 进行支付 if pur.run_status: pur.payment() else: logger.debug("填写支付信息出现错误") return pur.result
def select_flight(self, flight_num): """ 选择对应的航班号 :param flight_num: :return: """ try: flight_info_xpath = '/html/body/div[1]/div[2]/div/div/div[1]/div/div[1]/div[3]/div[2]/div/div[4]/div/div[2]/dl' dl_list = self.get_ele_list(flight_info_xpath) # 匹配航班号 for index, item in enumerate(dl_list): # 获取页面的航班号 if index == 0: continue page_flight_num_xpath = './dt/span[2]' page_flight_num, page_flight_date = self.get_text( page_flight_num_xpath, item).split() if page_flight_num == flight_num: # 点击选中航班 # 点击原味香草simple simple_xpath = './dd[2]' self.click_btn(simple_xpath, item) time.sleep(2) # 关闭提示框 tip_xpath = './dd[2]/div/span/span[2]/div/div/div[1]/a' self.click_btn(tip_xpath, item) time.sleep(1) # 获取价格校验航班价格 page_flight_price_xpath = './dd[2]/div/span/span[2]' currency, page_flight_price = self.get_text( page_flight_price_xpath, item).split() if float(page_flight_price ) > self.target_price: # 页面价格大于目标价格,中断程序 logger.info( f"目标价格小于页面价格, 页面价格为:{page_flight_price}, 任务目标价格为:{self.target_price}" ) self.fill_back["status"] = 403 raise StopException("目标价格小于页面价格") # 回填数据 # 。。。。。。 # 点击下一步 next_step_xpath = '/html/body/div[1]/div[2]/div/div/div[1]/div/div[1]/div[4]/div[3]/div[2]/a' self.click_btn(next_step_xpath) time.sleep(2) # 记录日志 logger.debug(f"选择航班结束, 航班号为{page_flight_num}") break else: self.fill_back["status"] = 402 logger.debug("没有找到航班") raise StopException("没有查询到航班") except StopException as e: logger.error(f"{e}导致程序中断") raise StopException(e) except Exception as e: logger.error(f"选择航班发生错误,错误提示:{e}") raise StopException(e)
def select_luggages(self): if sum(self.luggages_weight) > 0: # 有乘客携带行李,选择行李 add_luggage_xpath = '//div[@class="vnl-service-options-box vnl-service-options-box--jp-b baggage"]' self.click_btn(add_luggage_xpath) passenger_luggage_list_xpath = '//div[@class="vnl-seat-select-box"]/div/div[2]/div/div[2]/div' passenger_luggage_list = self.get_ele_list( passenger_luggage_list_xpath) # 对每个乘客进行行李的点击 for index, passenger in enumerate(self.passengers): # 滑动到可见元素 self.scroll_screen(passenger_luggage_list[index]) time.sleep(2) if passenger["baggageWeight"] == 0: continue # 计算获取对应的重量 quotient, residue = divmod(passenger["baggageWeight"], 5) if residue > 0: quotient += 1 # 行李重量li标签的索引 if quotient < 4: select_weight_index = 2 else: select_weight_index = quotient - 2 # 51KG一下的 # 点击选择行李重量 if select_weight_index < 10: weight_xpath = './div[@class="vnl-baggage-select-box--flight_baggage-one-person"]/div[2]/ol/li[{}]' self.click_btn( xpath=weight_xpath.format(select_weight_index), el=passenger_luggage_list[index]) else: # 501KG以上的行李选择 # 先点击更多 more_xpath = './div[@class="vnl-baggage-select-box--flight_baggage-one-person"]/div[2]/ol/li[@class="more"]' # 滑动屏幕 # self.scroll_screen() self.click_btn(more_xpath, el=passenger_luggage_list[index]) time.sleep(1) select_weight_index -= 8 weight_xpath = './div[@class="vnl-baggage-select-box--flight_baggage-one-person"]/div[2]/ol/li[@class="more"]/div/ul/li[{}]' self.click_btn( xpath=weight_xpath.format(select_weight_index), el=passenger_luggage_list[index]) pass else: # 点击完成 over_xpath = '//a[@ng-click="selectBaggageCtl.close()"]' self.click_btn(over_xpath) time.sleep(2) # 无行李,选择空过 # 点击下一步 next_xpath = '//a[@data-id="act-next"]' self.click_btn(next_xpath) logger.debug("选择行李完毕") time.sleep(2) # 选择不用了,谢谢 no_thanks_js = "document.querySelector('body > div.vnl-modal-popup.vnl-modal-popup_wrap > div > div > div > div.vnl-popup_btn > a.no.action_close').click()" self.driver.execute_script(no_thanks_js)
def select_flight_info(self): """ 选择航班信息出发地,目的地,出发时间,人数,先按照单程票购买 :return: """ logger.info("输入航班信息") # 点击One way,选择单程票 oneway_btn = '//*[@id="siteContent"]/div[1]/div[3]/div[1]/label[2]' self.click_btn(oneway_btn) # 选择出发地 depAir_xpath = '//*[@id="origin"]' self.click_btn(xpath=depAir_xpath) # 输入查询出发地 depAir_input_xpath = '//*[@id="siteContent"]/div[3]/div[3]/div/input' self.fill_input(self.depAirport, xpath=depAir_input_xpath, single_input=True) time.sleep(2) # 选择查询到的第一个结果点击选择, 点击第一个会不准确 result_xpath = '//*[@id="siteContent"]/div[3]/div[4]/ul[2]/li[@data-code="{}"]'.format( self.depAirport) self.click_btn(xpath=result_xpath) logger.debug("输入出发地") time.sleep(2) # 选择目的地 arrAir_xpath = '//*[@id="destination"]' self.click_btn(xpath=arrAir_xpath) # 输入查询目的地 arrAir_input_xpath = '//*[@id="siteContent"]/div[4]/div[3]/div/input' self.fill_input(self.arrAirport, xpath=arrAir_input_xpath, single_input=True) time.sleep(2) # 准确选择目的地 result_xpath = '//*[@id="siteContent"]/div[4]/div[4]/ul/li[@data-code="{}"]'.format( self.arrAirport) self.click_btn(xpath=result_xpath) logger.debug("输入目的地") # 选择出发日期 departure_xpath = '//*[@id="departuredate"]' self.click_btn(xpath=departure_xpath) # //*[@id="datepicker"]/div/div[1]/table/tbody/tr[5]/td[4]/a # 尝试点击日期 year, month, day = self.depDate.split("-") # 获取当前月份 current_month = datetime.now().month div_num = str(int(month) - int(current_month) + 1) # 选择出发日期 self.select_date(div_num=div_num, day=day) # 点击选择乘客 passenger_btn = '//*[@id="travellers"]' self.click_btn(xpath=passenger_btn) # 计算成人,儿童,婴儿的个数 self.calculation_age(self.passengerVOList) # 点击+,选择人数 adult_xpath = '//*[@id="siteContent"]/div[6]/div[2]/div/div[1]' children_xpath = '//*[@id="siteContent"]/div[6]/div[3]/div/div[1]' infants_xpath = '//*[@id="incInfant"]' all_li = { "adult": adult_xpath, "children": children_xpath, "infants": infants_xpath } for category in all_li: for item in range(getattr(self, category)): self.click_btn(xpath=all_li[category]) # 点击确定 done_btn = '//*[@id="setTravellerData"]' self.click_btn(xpath=done_btn) # 信息填写完毕,点击search按钮,查询航班信息 search_btn = '//*[@id="SearchViewControlGroupFlightSelection_SearchViewFlightSearchControl_ButtonSubmit"]' self.click_btn(search_btn) logger.info("查询信息填写完毕") # 等待查询结果 time.sleep(5)