zoukankan      html  css  js  c++  java
  • 使用selenium 和图片验证码识别 对12306的模拟登录+12306查询车次

     首先总结坑:

    1、关于浏览器分辨率有关的截图

    2、关于浏览器位置点击(分辨率)

    3、注意当前时间的 请求 URL 的cookie 值!!

    思路:

    1、首先分析12306 登录界面

    2、将页面截取为图片

    3、取得图片的位置 通过 先打开图片Image.open(), crop(左,上,右,下)-->用.save('文件名')存此图片

    4、使用超级鹰下载的示例模块(记得传入9004)

    5、超级鹰的结果用循环点击(此处显式等待 每0.5s检查一次,设定时间)

      ActionChains(driver) 使用动作链操作鼠标去移动点击,执行

    6、登录成功--验证成功

    7、移动到查询页面

    8、去查 ajax出来的  copy 将其url 打开 (工具网站:https://curl.trillworks.com/)可以看到cookie值,一般此操作和日期有关如果当前时间已经没有车次,会导致过期!

      一定要及时修改

    9、找到中文对应的英文链接

     (js)的URL!!

    做一个中文英文互相转换的模块

    10、分析ajax的 url打开的数据 在这里面可以通过 选取字典索引值找到 这里是js格式 所以使用  json.loads(   返回.text)[' 索引']

    11、分析里面要取的数据 的位置 通过遍历选取

    12、是否 有票!大坑!!! 记得索引位置,有部分车次的索引位置完全相反!!!我晕,暂时没想到怎么搞

    13、isdigit()函数判断其中有数字或特殊符号则为ture! 有用 

    其中的URL:

     1 class API_URL(object):
     2 
     3     # 下载验证码的URL
     4     GET_YZM_URL = "https://kyfw.12306.cn/passport/captcha/captcha-image?login_site=E&module=login&rand=sjrand&"
     5     # 校验验证码的URL(post)
     6     CHECK_YZM_URL = "https://kyfw.12306.cn/passport/captcha/captcha-check"
     7     # 登录首页的URL get
     8     Login_URL_1 = "https://kyfw.12306.cn/otn/login/init"
     9     Login_URL_2 = "https://kyfw.12306.cn/otn/login/init#"
    10     # 车票站点查询的URL
    11     SELECT_URL = "https://kyfw.12306.cn/otn/resources/js/framework/station_name.js?station_version=1.9150"

    此处headers 和cookies 自己获取,记得加!这里肯定会失效

     1 headers = {
     2     'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36'
     3 }
     4 cookies = {
     5     '_uab_collina': '1',
     6     'JSESSIONID': '',
     7     'tk': '',
     8     'BIGipServerotn': '',
     9     'RAIL_EXPIRATION': '',
    10     'RAIL_DEVICEID': '',
    11     'BIGipServerpool_passport': '',
    12     'route': '9036359bb8a8a461c164a04f8f50b252',
    13     'ten_key': '1gJOinE9bHcaIId53q5LyxGn2Y/Z4ERq',
    14     'ten_js_key': '1gJOinE9bHcaIId53q5LyxGn2Y%2FZ4ERq',
    15     '_jc_save_toDate': '2020-07-06',
    16     '_jc_save_wfdc_flag': 'dc',
    17     'current_captcha_type': 'Z',
    18     '_jc_save_fromStation': '',
    19     '_jc_save_toStation': '',
    20     'uKey': '',
    21     '_jc_save_fromDate': '',
    22 }
     1 # 登录模块
     2     def login(self):
     3 
     4 
     5         # IP : port
     6         proxy = '110.243.16.20 : 9999'
     7 
     8         # 设置代理IP
     9         chrome_options = webdriver.ChromeOptions()
    10         # 代理服务器n
    11         chrome_options.add_argument('--proxy-server = %s'%proxy)
    12 
    13 
    14         driver = webdriver.Chrome(chrome_options=chrome_options)
    15         # 请求
    16         driver.get(API_URL.Login_URL_1)
    17 
    18         driver.maximize_window()
    19         time.sleep(2)
    20 
    21         driver.find_element_by_xpath('//*[@id="username"]').send_keys(self.user_name)
    22         time.sleep(2)
    23         driver.find_element_by_xpath('//*[@id="password"]').send_keys(self.password)
    24         time.sleep(2)
    25 
    26         # 思路!截屏获取验证码
    27 
    28         # 获取图片所在位置
    29         copy_img = driver.find_element_by_xpath('//*[@id="loginForm"]/div/ul[2]/li[4]/div/div/div[3]/img')
    30         # 图片的左上角位置
    31         location = copy_img.location
    32         print(location)
    33         size = copy_img.size
    34         print(size)
    35         time.sleep(2)
    36         # 要截取的坐标大小  !!!!重坑警告!!! 在这里困好久 打开的默认浏览器分辨率毫无问题
    37         # 但是但是!!其实需要 *1.50 才能正确取到验证码截图
    38         location_code=(int(location['x']*1.50), int(location['y']*1.50), int(location['x']+size['width'])*1.50, int(location['y']+size['height'])*1.50)
    39         print(location_code)
    40         # 定位完后截屏
    41         driver.save_screenshot("screen.png")
    42         i = Image.open("screen.png")
    43 
    44         # 在此要元组类型 (left, upper, right, lower)-tuple
    45         # (左上右下)
    46         ver_code_img = i.crop(location_code)
    47         ver_code_img.save("验证码.png")
    48         # 自动识别
    49         result_cjy = user_cjy("验证码.png")
    50         print(result_cjy)
    51 
    52         result_cjy = result_cjy.get('pic_str').split('|')
    53         # 遍历 列表['247,67', '105,138']将其取出 所有都用 , 分隔
    54         points = [[int(number) for number in numbers.split(',')] for numbers in result_cjy]
    55         print(points)  # 此处获取到二维列表!!!
    56 
    57         for point in points:
    58             # 显示等待,定位图片对象
    59             element = WebDriverWait(driver, 20).until(
    60                 EC.presence_of_element_located((By.CLASS_NAME, "touclick-image")))
    61             # 模拟点击  !!!!此处模拟点击又有 坑 因为刚刚放大截取1.5 所以这里缩小点击0.6
    62             ActionChains(driver).move_to_element_with_offset(element,point[0]*0.66,point[1]*0.66).click().perform()  # perform(发送进行执行)
    63             time.sleep(2)
    64         time.sleep(5)
    65         driver.find_element_by_xpath('//*[@id="loginSub"]').click()
    66         time.sleep(5)
    67         if driver.current_url not in [API_URL.Login_URL_1,API_URL.Login_URL_2]:
    68             print("登录成功!")
    69         else:
    70             print("登录失败,请重试!")
     1 # 查询火车余票模块
     2     def select(self):
     3         # 请求站点转换 js
     4         req = requests.get(API_URL.SELECT_URL)
     5 
     6         self.station_data = req.text.lstrip("var station_names ='").rstrip("'").split('@')
     7         # print(self.station_data)
     8 
     9         time_now = input("请输入日期(格式:2020-07-06):")
    10         from_station = self.get_station(input("请输入出发地:"))
    11         to_station = self.get_station(input("请输入目的地:"))
    12         # 注意 一定要知道什么站到什么站是由车次的 如果搜寻过程中没有对应车 是报错
    13         url = "https://kyfw.12306.cn/otn/leftTicket/query?leftTicketDTO.train_date={}&leftTicketDTO.from_station={}&leftTicketDTO.to_station={}&purpose_codes=ADULT".format(time_now,from_station,to_station)
    14         print(url)
    15 
    16         req_2 = requests.get(url,headers=headers,cookies=cookies)
    17         req_2.encoding='utf-8'
    18         # print(req_2.text)
    19 
    20         result = json.loads(req_2.text)['data']['result']
    21         # print(result)
    22 
    23         # 是否有座?? 此处的第一个元素 我取寻找 请求的url 中的位置每个位置不同!!大坑!!
    24         data_seats = [(32,'商务座'),(31,'一等座'),(30,'二等座'),(29,'高级软卧'),(28,'一等卧'),(27,'动卧'),(26,'二等卧'),(25,'软座'),(24,'硬座'),(23,'无座')]
    25         for item in result:
    26             item = item.split("|")
    27             dict_item = {
    28                 '编号':item[2], '车次':item[3], '出发地':self.get_station_CN(item[4]), '目的地':self.get_station_CN(item[5]), '出发站':self.get_station_CN(item[6]), '终点站':self.get_station_CN(item[7]), '出发时间':item[8], '抵达时间':item[9], '总耗时': str(int(item[10][:item[10].index(':')]))+'小时'+str(int(item[10][item[10].index(':')+1:]))+'分钟',
    29                 '商务座': "",
    30                 '一等座': "",
    31                 '二等座': "",
    32                 '高级软卧': "",
    33                 '一等卧': "",
    34                 '动卧': "",
    35                 '二等卧': "",
    36                 '软座': "",
    37                 '硬座': "",
    38                 '无座': ""
    39             }
    40             for index in range(10):
    41                 #
    42                 if item[data_seats[index][0]] == '' or item[data_seats[index][0]].isdigit():   # isdigit()函数判断其中有数字或特殊符号则为ture
    43                     dict_item[data_seats[index][1]]=item[data_seats[index][0]]
    44                 else:
    45                     del dict_item[data_seats[index][1]]
    46 
    47             print(dict_item)
     1 # 车站的英文缩写函数!(中文转英文)
     2     def get_station(self, city):
     3         for item in self.station_data:
     4             if city in item:
     5                 return item.split('|')[2]
     6 
     7     # 车站的中文缩写函数!
     8     def get_station_CN(self, city_CN):
     9         for item in self.station_data:
    10             if city_CN in item:
    11                 return item.split('|')[1]
    1     def __init__(self, user_name, password):
    2         self.user_name = user_name
    3         self.password = password
    4         self.station_data = ''
    1 if __name__ == '__main__':
    2     # username = input("请输入用户名:")
    3     # password = input("请输入密码:")
    4     user = UserPass("username", "password")
    5     # user.login()
    6     user.select()
  • 相关阅读:
    python socket练习
    python异常处理
    python类的反射
    类的特殊成员方法
    staticmethod classmethod property方法
    类的多态
    类的析构、继承
    python subprocess模块
    python面向对象
    discuz 使模板中的函数不解析 正常使用
  • 原文地址:https://www.cnblogs.com/sdosun/p/13258406.html
Copyright © 2011-2022 走看看