Beispiel #1
0
def send_mail(content, html=False):
    """
    邮件通知
    :param content:str email content
    :param html:boolean 是否为 html 格式
    :return:
    """
    try:
        sender = config.email['sender']
        receivers = config.email["receivers"]
        subject = '恭喜,您已订票成功!'
        username = config.email["username"]
        password = config.email["password"]
        host = config.email["host"]
        if html:
            msg = MIMEMultipart()
            msg.attach(MIMEText(content, 'html', 'utf-8'))
        else:
            msg = MIMEText(content, 'plain', 'utf-8')  # 中文需参数 utf-8,单字节字符不需要
        msg['Subject'] = Header(subject, 'utf-8')
        msg['From'] = sender
        msg['To'] = ",".join(receivers)
        try:
            smtp = smtplib.SMTP_SSL(host)
            smtp.connect(host)
        except socket.error:
            smtp = smtplib.SMTP()
            smtp.connect(host)
        smtp.connect(host)
        smtp.login(username, password)
        smtp.sendmail(sender, receivers, msg.as_string())
        smtp.quit()
        console.print(u"邮件已通知, 请查收", style="bold green")
    except Exception as e:
        console.print(u"邮件配置有误{}".format(e), style="bold red")
Beispiel #2
0
 def checkForView(self):
     try:
         console.print('Capturing screen', 0)
         self.getScreen()
     except ValueError:
         console.print(
             f'{console.icho.bold}{console.icho.red}'
             'Error capturing screen'
             f'{console.icho.normal}', 0)
         return False
     idx = len(self.viewList)
     for view in self.viewList:
         position = (len(self.viewList) - idx) // len(self.viewList)
         console.print(f'View: {view.name}', position)
         idx -= 1
         if view.isView(self._tmpImage):
             console.print(
                 f'View: {view.name} '
                 f'{console.icho.bold}'
                 f'{console.icho.cyan}'
                 f'OK{console.icho.normal}', 1)
             for touch in view.touchArray:
                 self.touchScreen(touch)
                 sleep(view.touchDelay / 1000)
             return True
     console.print('No view found', 1)
     return False
Beispiel #3
0
def show_alert_info(text=None, split='  '):
    """
    @param text 添加到打印信息头部
    @param split 打印信息分隔符
    打印通知警告信息
    """
    print_info = []
    if text:
        print_info.append(text)

    # 对话框
    check_el = browser.find_el_if_exist('content_checkticketinfo_id', by=By.ID)
    if check_el and check_el.is_displayed():
        check_text = check_el.find_element_by_id('sy_ticket_num_id').text
        console.print(check_text, style="bold red")
    # 提示框
    notice_el = browser.find_el_if_exist('content_transforNotice_id', by=By.ID)
    if notice_el and notice_el.is_displayed():
        el = browser.find_el_if_exist('#orderResultInfo_id > div > span')
        if el:
            print_info.append(f'[red]{el.text}[/red]')
        el = find_el_if_exist(notice_el, 'p', by=By.TAG_NAME)
        if el:
            print_info.append(el.text)
    console.print(split.join(print_info))
Beispiel #4
0
 def check_error(self):
     """
     检查是否被关小黑屋 。。。
     """
     from core import error_page
     if self.current_url.startswith(error_page):
         # IP 被限制,等待五分钟后重试
         console.print('[yellow]网络错误,五分钟后重试![/yellow]')
         self.wait_unblock()
Beispiel #5
0
 def _check_ticket(self):
     """
     检查是否有余票
     """
     train_trs = tk.find_train_info_trs()
     if len(train_trs) == 0:
         console.print(':raccoon: [yellow]未查询到车次信息[/yellow]: ',
                       config.from_time,
                       f"[{str.join(',', config.trains)}]")
     else:
         self._try_submit(train_trs)
Beispiel #6
0
    def _try_submit(self, index, left_tr, train_name):
        """
        尝试提交
        :param index: 表格索引
        :param left_tr: 列车信息行
        :param train_name: 列车车次名称
        """
        left_tr_id = left_tr.get_attribute('id')
        submit_btn = browser.find_el_if_exist('#' + left_tr_id +
                                              ' > td.no-br > a')
        if submit_btn:
            ss, es, st, et, du = _get_train_info(index)
            if str.startswith(left_tr_id, 'ticket_'):
                serial_num = left_tr_id[len('ticket_'):].split('_')[0]
                # 座位类型是否匹配
                for seat_type in config.seat_types:
                    if seat_type_dict[seat_type]:
                        seat_selector = '#' + seat_type_dict[
                            seat_type] + '_' + serial_num
                        seat = left_tr.find_element_by_css_selector(
                            seat_selector)
                        if seat:
                            ticket_info = {
                                "address": f"{ss} - {es}",
                                "time": f"{st} - {et}",
                                "duration": du,
                                'train': train_name
                            }

                            # 判断是否有余票,如果有余票就尝试提交
                            def try_submit(seat_el):
                                if seat_el.text and seat_el.text is not '无' \
                                        and seat_el.text is not '--':
                                    self._running = False
                                    ticket_info['seat_type'] = seat_type
                                    ticket_info['value'] = seat_el.text
                                    self._ticket_info = ticket_info
                                    console.print(
                                        _table_ticket_info(ticket_info))
                                    submit_btn.click()
                                    return True
                                return False

                            seat_div = browser.find_el_if_exist(seat_selector +
                                                                ' > div')
                            if try_submit(seat_div if seat_div else seat):
                                return True
                    console.print(
                        f":vampire: {config.from_time} - {train_name} - {seat_type}",
                        "[red]暂无余票[/red]")
        else:
            console.print(f":vampire: {config.from_time} - {train_name}",
                          "[red]暂无余票[/red]")
        return False
Beispiel #7
0
def _init():
    # 跳转到登录页
    browser.get(login_page)
    time.sleep(1)
    if browser.find_el_if_exist('ERROR', by=By.ID) is not None:
        console.print('[red]12306请求过于频繁,请稍等重试 . . . [/red]')
        browser.wait_unblock()
    # 等待用户密码登录按钮可以点击,切换到用户密码登录 Tab
    wait.WebDriverWait(browser, 5).until(
        ec.element_to_be_clickable(
            (By.CLASS_NAME, 'login-hd-account'))).click()
    time.sleep(1)
Beispiel #8
0
 def try_submit(seat_el):
     if seat_el.text and seat_el.text is not '无' \
             and seat_el.text is not '--':
         self._running = False
         ticket_info['seat_type'] = seat_type
         ticket_info['value'] = seat_el.text
         self._ticket_info = ticket_info
         console.print(
             _table_ticket_info(ticket_info))
         submit_btn.click()
         return True
     return False
Beispiel #9
0
 def _try_submit(self, train_trs):
     """
     尝试提交
     :param train_trs: 列车车次名称
     """
     for train_tr in train_trs:
         submit_btn = find_el_if_exist(train_tr, 'td.no-br > a')
         ss, es, st, et, du, tn = tk.get_train_info(train_tr)
         if submit_btn and submit_btn:
             left_tr_id = train_tr.get_attribute('id')
             serial_num = left_tr_id[len('ticket_'):].split('_')[0]
             # 座位类型是否匹配
             for seat_type in config.seat_types:
                 if tk.seat_type_dict[seat_type]:
                     seat_type_id = tk.seat_type_dict[seat_type][
                         "code"] + '_' + serial_num
                     seat = train_tr.find_element_by_id(seat_type_id)
                     if seat:
                         ticket_info = {
                             "address": f"{ss} - {es}",
                             "time": f"{st} - {et}",
                             "duration": du,
                             'train': tn
                         }
                         # 判断是否有余票,如果有余票就尝试提交
                         seat_text = seat.text
                         if seat_text == '有' or re.match(
                                 "^\\d+$", seat_text):
                             self._running = False
                             ticket_info['seat_type'] = seat_type
                             ticket_info['value'] = seat_text
                             self._ticket_info = ticket_info
                             console.print(
                                 tk.table_ticket_info(ticket_info))
                             submit_btn.click()
                             return True
                     else:
                         console.print(f"未找到坐席类型 :[red] {seat_type} [/red]")
                         console.print("请按照要求配置坐席类型:",
                                       ",".join(tk.seat_type_dict.keys()))
                         sys.exit(-1)
                 console.print(
                     f":vampire: {config.from_time} - {tn} - {seat_type}",
                     "[red]暂无余票[/red]")
         else:
             console.print(f":vampire: {config.from_time} - {tn}",
                           "[red]暂无余票[/red]")
     return False
Beispiel #10
0
 def _parse_captcha(self):
     # 这里也可以使用自建打码服务器
     code_url = f'{config.code_server["scheme"]}://{config.code_server["host"]}{config.code_server["path"]}'
     data = {"imageFile": self._code_img}
     count = 0
     while count < 10:
         count += 1
         try:
             resp = requests.post(code_url, data=data)
             if resp.status_code is 200:
                 resp_json = resp.json()
                 if resp_json and resp_json.get("code") is 0:
                     return code_xy(resp_json.get("data")).split(',')
         except Exception as e:
             console.print('[red]打码失败[/red]')
             print(e)
             raise e
Beispiel #11
0
 def login(self):
     # 初始化
     _init()
     # 自动打码
     self.captcha.code_captcha()
     # 开始登陆
     browser.find_element_by_id('J-userName').send_keys(config.username)
     browser.find_element_by_id('J-password').send_keys(config.password)
     browser.find_element_by_id('J-login').click()
     time.sleep(0.5)
     # 验证滑块验证码
     slider_captcha()
     try:
         # 等待跳转到首页
         wait.WebDriverWait(browser, 10).until(
             lambda driver: driver.current_url.startswith(index_page))
     except TimeoutException:
         console.print("登录失败,请稍后重试 . . .", style="bold red")
         browser.wait_unblock()
Beispiel #12
0
 def _check_ticket(self):
     """
     检查是否有余票
     """
     exist_train = False
     left_tr_list = browser.find_els_if_exist('#queryLeftTable > tr')
     for index in range(len(left_tr_list)):
         # 跳过空表格行
         if index % 2 == 0:
             left_tr = left_tr_list[index]
             train_tr = left_tr_list[index + 1]
             train_name = train_tr.get_attribute('datatran')
             for t in config.trains:
                 if t == train_name:
                     exist_train = True
                     if self._try_submit(index, left_tr, train_name):
                         return
     if not exist_train:
         console.print(':vampire: [yellow]未查询到车次信息[/yellow]: ',
                       config.from_time,
                       f"[{str.join(',', config.trains)}]")
Beispiel #13
0
 def check(self):
     count = 0
     sleep_time = 0
     while self._running:
         random_time = random.randint(300, 1000) / 1000
         time.sleep(random_time)
         now = datetime.now().strftime('%H:%M:%S')
         # 判断是否在预约时间内
         if config.start_time <= now <= config.end_time:
             count += 1
             browser.find_element_by_id('query_ticket').click()
             time.sleep(0.5)
             console.print(f':vampire: 第 [red]{count}[/red] 次点击查询 . . .')
             self._check_ticket()
         else:
             sleep_time += 1
             # 为了防止长时间不操作,session 失效
             if sleep_time >= 60:
                 sleep_time = 0
                 console.print(f':raccoon: [{now}] 刷新浏览器')
                 browser.refresh()
     self._create_order()
     message = "恭喜您,抢到票了,请及时前往12306支付订单!"
     console.print(f":smiley: {message}")
     if config.mail['enable'] is True:
         send_mail(message)
     if config.ftqq_server['enable'] is True:
         send_ftqq(_md_ticket_info(self._ticket_info))
Beispiel #14
0
def code_xy(select):
    """
    根据打码服务器返回的 offset 获取页面中真实的坐标
    """
    post = []
    offsets_x = 0  # 选择的答案的left值,通过浏览器点击8个小图的中点得到的,这样基本没问题
    offsets_y = 0  # 选择的答案的top值
    for offset in select:
        if offset == '1':
            offsets_y = 77
            offsets_x = 40
        elif offset == '2':
            offsets_y = 77
            offsets_x = 112
        elif offset == '3':
            offsets_y = 77
            offsets_x = 184
        elif offset == '4':
            offsets_y = 77
            offsets_x = 256
        elif offset == '5':
            offsets_y = 149
            offsets_x = 40
        elif offset == '6':
            offsets_y = 149
            offsets_x = 112
        elif offset == '7':
            offsets_y = 149
            offsets_x = 184
        elif offset == '8':
            offsets_y = 149
            offsets_x = 256
        else:
            pass
        post.append(offsets_x)
        post.append(offsets_y)
    rand_code = str(post).replace(']', '').replace('[', '').replace("'", '').replace(' ', '')
    console.print(f":thumbs_up: 验证码识别成功: [{rand_code}]")
    return rand_code
Beispiel #15
0
def slider_captcha():
    try_time = 1
    div = browser.find_el_if_exist('nc_1_n1z', by=By.ID)
    while div and div.is_displayed():
        if try_time > 10:
            console.print('滑块验证码验证失败 . . .', style='bold red')
            sys.exit(-1)
        # 动作链
        action = ActionChains(browser)
        # 点击长按指定的标签
        action.click_and_hold(div)
        # 处理滑动模块
        for i in range(5):
            # perform()立即执行动作链操作
            # move_by_offset(x,y):x水平方向 y竖直方向
            try:
                action.move_by_offset(350, 0).perform()  # 速度为30mm
            except WebDriverException:
                time.sleep(0.5)
        time.sleep(0.5)
        action.release()
        try_time += 1
        div = browser.find_el_if_exist('nc_1_n1z', by=By.ID)
Beispiel #16
0
 def _create_order(self):
     """
     创建订单
     """
     time.sleep(0.5)
     passenger_list = browser.find_els_if_exist('#normal_passenger_id > li')
     # 选择乘坐人
     passenger_index = 1
     self._ticket_info['passengers'] = []
     for passenger in config.passengers:
         for passenger_li in passenger_list:
             passenger_input = passenger_li.find_element_by_tag_name(
                 'input')
             if passenger_li.find_element_by_tag_name(
                     'label').text == passenger:
                 console.print(f"选择乘坐人 [{passenger}] . . .")
                 self._ticket_info['passengers'].append(passenger)
                 passenger_input.click()
                 warning_alert = browser.find_el_if_exist(
                     'content_defaultwarningAlert_id', by=By.ID)
                 if warning_alert:
                     warning_alert.find_element_by_id(
                         'qd_closeDefaultWarningWindowDialog_id').click()
                     time.sleep(0.5)
                 # 选择席座
                 console.print(
                     f"开始选择席座 [{self._ticket_info['seat_type']}] . . .")
                 seat_select = browser.find_element_by_id(
                     f"seatType_{str(passenger_index)}")
                 _, seat_type_value = _get_seat_type_index_value(
                     self._ticket_info['seat_type'])
                 if seat_type_value != 0:
                     Select(seat_select).select_by_value(
                         str(seat_type_value))
                 passenger_index += 1
                 time.sleep(0.5)
     if passenger_index == 1:
         console.print("未找到乘客信息 ... 提交订单失败", style="bold red")
     print('正在提交订单 . . .')
     wait.WebDriverWait(browser, 5).until(
         ec.element_to_be_clickable((By.ID, 'submitOrder_id'))).click()
     print('正在确认订单 . . .')
     wait.WebDriverWait(browser, 5).until(
         ec.element_to_be_clickable((By.ID, 'qr_submit_id'))).click()
     # 截取屏幕,订单信息页
     print('预订成功,正在截取屏幕 . . .')
     time.sleep(5)
     browser.screenshot(
         f'{self.fs}_{self.ts}_{config.from_time.replace("-", "")}_{self._ticket_info["train"].lower()}.png'
     )
     time.sleep(60)
Beispiel #17
0
 def wait_unblock(self):
     """
     关小黑屋后,在这里等待。。。
     """
     from core import ticket_url
     console.print("欢迎来到小黑屋 。。。")
     console.print(
         """|-----------------------------------------------------------------------|"""
     )
     console.print(
         """|    o   \ o /  _ o         __|    \ /     |__        o _  \ o /   o    |"""
     )
     console.print(
         """|   /|\    |     /\   ___\o   \o    |    o/    o/__   /\     |    /|\   |"""
     )
     console.print(
         """|   / \   / \   | \  /)  |    ( \  /o\  / )    |  (\  / |   / \   / \   |"""
     )
     console.print(
         """|-----------------------------------------------------------------------|"""
     )
     time.sleep(config.block_time)
     self.get(ticket_url)
     time.sleep(1)
Beispiel #18
0
    def check(self):
        browser.get(ticket_url)
        time.sleep(1)
        check_alert()
        count = 0
        while self._running:
            # 检查时候登录失效, 判断是否存在登出按钮
            if not has_login():
                return
            # 检查是否出错
            browser.check_error()

            # 随机休眠 0.3-1 秒
            # random_time = random.randint(300, 1000) / 1000
            # time.sleep(random_time)

            # 把开始时间,结束时间字符串转换为日期
            now = datetime.now()

            def convert_time_to_datetime(time_str):
                now_date_str = now.strftime(date_format_str)
                return datetime.strptime(f"{now_date_str} {time_str}",
                                         datetime_format_str)

            start_ts = convert_time_to_datetime(config.start_time).timestamp()
            end_ts = convert_time_to_datetime(config.end_time).timestamp()
            now_ts = now.timestamp()
            # 判断是否在预约时间内
            if (start_ts - 120) <= now_ts <= end_ts:
                try:
                    wait.WebDriverWait(browser, 5).until(
                        ec.element_to_be_clickable(
                            (By.ID, 'query_ticket'))).click()
                except ElementClickInterceptedException:
                    continue
                time.sleep(0.5)
                count += 1
                console.print(":raccoon:", f'第 [red]{count}[/red] 次点击查询 . . .')
                self._check_ticket()
                # 每查询 1000 次刷新一次浏览器
                if count % 1000 == 0:
                    browser.refresh()
                    time.sleep(1)
                    # 检测是否出现提醒框
                    check_alert()
            else:
                # 使用睡眠方案,提前两分钟唤醒,登录超时会自动登录
                now_time_str = now.strftime(time_format_str)
                # 取今天日期 当前时间小于开始抢票时间
                if now_time_str < config.start_time:
                    date_str = now.strftime(date_format_str)
                # 取明天的日期
                else:
                    tomorrow = datetime.fromtimestamp(now.timestamp() +
                                                      60 * 60 * 24)
                    date_str = tomorrow.strftime(date_format_str)
                next_run_datetime = datetime.strptime(
                    f"{date_str} {config.start_time}", datetime_format_str)
                sleep_second = next_run_datetime.timestamp() - now.timestamp(
                ) - 120
                console.print("未到达开始开放抢票时间,程序即将休眠 ... ", style="bold yellow")
                console.print(
                    f"程序将于 [{next_run_datetime.strftime(datetime_format_str)}] 重新启动 !",
                    style="bold green")
                time.sleep(sleep_second)
        # 创建订单
        if self._create_order():
            message = "恭喜您,抢到票了,请及时前往12306支付订单!"
            console.print(f":smiley: {message}")
            # 发送通知邮件
            if config.email['enable'] is True:
                send_mail(tk.html_ticket_info(self._ticket_info), html=True)
            # 发送 server 酱通知
            if config.server_chan['enable'] is True:
                send_server_chan(tk.md_ticket_info(self._ticket_info))
            sys.exit(0)
        self.check()
Beispiel #19
0
from core.login import Login
from core.ticket import Ticket
from utils import console
from utils.file import read_as_str


def main():
    Login().login()
    Ticket().check()


if __name__ == '__main__':
    console.print(read_as_str('./banner.txt'))
    while True:
        main()
Beispiel #20
0
 def _create_order(self):
     """
     创建订单
     """
     time.sleep(1)
     passenger_list = browser.find_els_if_exist('#normal_passenger_id > li')
     # 选择乘坐人
     passenger_index = 1
     self._ticket_info['passengers'] = []
     # 检查余票数量
     ticket_value = self._ticket_info['value']
     surplus_ticket_number = int(
         ticket_value) if ticket_value != '有' else 9999
     for passenger in config.passengers[:surplus_ticket_number]:
         for passenger_li in passenger_list:
             passenger_input = passenger_li.find_element_by_tag_name(
                 'input')
             if passenger_li.find_element_by_tag_name(
                     'label').text == passenger:
                 console.print(f"选择乘坐人 [{passenger}] . . .")
                 self._ticket_info['passengers'].append(passenger)
                 passenger_input.click()
                 warning_alert = browser.find_el_if_exist(
                     'content_defaultwarningAlert_id', by=By.ID)
                 if warning_alert:
                     warning_alert.find_element_by_id(
                         'qd_closeDefaultWarningWindowDialog_id').click()
                     time.sleep(0.5)
                 # 选择席座
                 console.print(
                     f"开始选择席座 [{self._ticket_info['seat_type']}] . . .")
                 seat_select = browser.find_element_by_id(
                     f"seatType_{str(passenger_index)}")
                 seat_type = tk.seat_type_dict[
                     self._ticket_info['seat_type']]
                 if not seat_type:
                     console.print(f"未找到坐席类型 :[red] {seat_type} [/red]")
                     sys.exit(-1)
                 seat_type_value = seat_type['value']
                 if seat_type_value != '0':
                     Select(seat_select).select_by_value(seat_type_value)
                 passenger_index += 1
                 time.sleep(0.5)
     if passenger_index == 1:
         console.print("未找到乘客信息 ... 无法选择乘客和坐席", style="bold red")
     try:
         print('正在提交订单 . . .')
         wait.WebDriverWait(browser, 10).until(
             ec.element_to_be_clickable((By.ID, 'submitOrder_id'))).click()
         time.sleep(1)
         # 确认提交订单
         print('正在确认订单 . . .')
         wait.WebDriverWait(browser, 10).until(
             ec.element_to_be_clickable((By.ID, 'qr_submit_id'))).click()
         # 等待跳转到支付页面, 如果超时未跳转, 说明订单生成失败
         wait.WebDriverWait(browser, 10).until(
             lambda driver: driver.current_url.startswith(pay_order_url))
         submit_btn = browser.find_el_if_exist('qr_submit_id', by=By.ID)
         if not submit_btn or not submit_btn.is_displayed():
             console.print(f'该车次无法提交[{self._ticket_info["train"]}]。',
                           style="bold yellow")
             config.trains.remove(self._ticket_info["train"])
             show_alert_info('该车次无法提交[{self._ticket_info["train"]}]。', '\n')
             return False
     except WebDriverException as e:
         browser.screenshot(
             f'{datetime.now().strftime(datetime_format_str)}-error.png')
         show_alert_info('提交订单失败:\n')
         raise e
     # 截取屏幕,订单信息页
     print('预订成功,正在截取屏幕 . . .')
     browser.screenshot(
         f'{self.fs}_{self.ts}_{config.from_time.replace("-", "")}_{self._ticket_info["train"].lower()}.png'
     )
     return True