使用selenium 和图片验证码识别 对12306的模拟登录+12306查询车次

Posted sdosun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用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: 159401687279425123588472,
 6     JSESSIONID: 15006E403A3D220629819AD7CBBDC853,
 7     tk: GeK-NUkoQ0CpE04s8roM0QwonK0LuKPFBGo4CQubs1s0,
 8     BIGipServerotn: 619708938.50210.0000,
 9     RAIL_EXPIRATION: 1594209286751,
10     RAIL_DEVICEID: XFErASnds56Lfkayxga7TQCa8eqhX3uplkHzXHt27hvD9IXlmSarYXr_dNF2LHYODwopchRRbJVaDwgVmxySz91dqk0_u2RqSsUyobeiBCBaSOyM-3V8R1V4U8pRsbNOlewj8vgv5A1pHtWi-zvUXq0kfvQdEdO1,
11     BIGipServerpool_passport: 216859146.50215.0000,
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: %u5317%u4EAC%2CBJP,
19     _jc_save_toStation: %u4E0A%u6D77%2CSHH,
20     uKey: 1963dac98c2756cd1312380545a9b8cefb73be533c5ed6aca3704252393a480f,
21     _jc_save_fromDate: 2020-07-15,
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()

 

以上是关于使用selenium 和图片验证码识别 对12306的模拟登录+12306查询车次的主要内容,如果未能解决你的问题,请参考以下文章

selenium 对验证码处理

python selenium-webdriver 登录验证码的处理

最新selenium+验证码识别模拟登陆B站

java selenium tess4j识别验证码模拟登陆。

selenium 自动化测试登录,登录页面有一个随机图片,应该怎么处理?

Selenium-WebDriver实战篇Java丨验证码图片去除干扰像素,方便验证码的识别(转)