Selenium之模拟登录铁路12306的示例代码


Posted in Python onJuly 31, 2020

最近接触了一些selenium模块的相关知识,觉得还挺有意思的,于是决定亲自尝试写一些爬虫程序来强化selenium模块(一定要多尝试、多动手、多总结)。本文主要使用python爬虫来模拟登录铁路12306官网。这儿得吐槽一句,铁路12306网站的反爬机制做的还是比较好。

话不多说,下面跟小墨一起来学习如何通过爬虫来实现铁路12306的登录。

一、 验证码破解

当我们输入账号和密码后,在点击登录按钮之前,还需要对验证码进行操作。对验证码的识别,已经有相关的处理平台,我们只需要借助第三方平台即可。

1.注册并登录超级鹰账号:点击链接进行注册https://www.chaojiying.com/user/login/;
2.点击购买题分,并进行充值;
3.点击软件id,创建一个软件Id(程序中会用到);
4.下载示例代码(开发文档—>选择相应的语言?>下载示例demo),python示例代码如下所示:

class Chaojiying_Client(object):
 def __init__(self, username, password, soft_id):
 self.username = username
 password = password.encode('utf8')
 self.password = md5(password).hexdigest()
 self.soft_id = soft_id
 self.base_params = {
  'user': self.username,
  'pass2': self.password,
  'softid': self.soft_id,
 }
 self.headers = {
  'Connection': 'Keep-Alive',
  'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
 }
 def PostPic(self, im, codetype):
 """
 im: 图片字节
 codetype: 题目类型 参考 http://www.chaojiying.com/price.html
 """
 params = {
  'codetype': codetype,
 }
 params.update(self.base_params)
 files = {'userfile': ('ccc.jpg', im)}
 r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
 return r.json()
 def ReportError(self, im_id):
 """
 im_id:报错题目的图片ID
 """
 params = {
  'id': im_id,
 }
 params.update(self.base_params)
 r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
 return r.json()

二、Selenium功能简介

Selenium模块和爬虫之间的关联:
?便捷的获取网站中的动态加载数据
?便捷实现模拟登录

Selenium模块的使用流程:
?环境安装:pip install selenium
?下载浏览器的驱动程序(谷歌浏览器):
?下载路径:http://chromedriver.storage.googleapis.com/index.html
? 驱动程序和浏览器的映射关系:映射链接
?将下载好的驱动程序放在当前项目目录下

Selenium模块的相关方法:https://3water.com/article/192259.htm

上述内容完成后,我们就可以正式进入正题了,是不是很期待,那就跟着小墨往下走吧。

三、模拟登录

1. 进入官网

#创建对象
#executable_path=path:下载好的驱动程序的路径
bro = webdriver.Chrome(executable_path='chromedriver.exe')
#12306的登录网址
bro.get('https://kyfw.12306.cn/otn/resources/login.html')
#窗口最大化
bro.maximize_window()

2、进入登录界面并获取验证码

#save_screenshot就是将当前页面进行截图且保存
bro.save_screenshot('aa.png')
#确定验证码图片对应的左上角和右下角的坐标(裁剪的区域就确定)
code_img_ele = bro.find_element_by_xpath('//*[@id="J-loginImg"]')
location = code_img_ele.location # 验证码图片左上角的坐标 x,y
#print('location:',location)
size = code_img_ele.size #验证码标签对应的长和宽
#print('size:',size)
#左上角和右下角坐标
rangle = (
int(location['x']), int(location['y']), int(location['x'] + size['width']), int(location['y'] + size['height']))
#至此验证码图片区域就确定下来了
i = Image.open('./aa.png')
code_img_name = './code.png'
#crop根据指定区域进行图片裁剪
frame = i.crop(rangle)
frame.save(code_img_name)
#将验证码图片提交给超级鹰进行识别
chaojiying = Chaojiying_Client('########', '#######', '#######')	#用户账号>>密码>>软件ID 
im = open('code.png', 'rb').read()									#本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
id=chaojiying.PostPic(im, 9004)['pic_id']    #截取的验证码照片以及验证码的类别代号
result = chaojiying.PostPic(im, 9004)['pic_str']   #识别结果
all_list = [] #要存储即将被点击的点的坐标 [[x1,y1],[x2,y2]]
#识别错误后,会返回题分,示例代码并没有这个,就是想让你花钱
chaojiying.ReportError(id)
if '|' in result:
 list_1 = result.split('|')
 print(list_1)
 count_1 = len(list_1)
 for i in range(count_1):
 xy_list = []
 x = int(list_1[i].split(',')[0])
 y = int(list_1[i].split(',')[1])
 xy_list.append(x)
 xy_list.append(y)
 all_list.append(xy_list)
else:
 x = int(result.split(',')[0])
 y = int(result.split(',')[1])
 xy_list = []
 xy_list.append(x)
 xy_list.append(y)
 all_list.append(xy_list)
#遍历列表,使用动作链对每一个列表元素对应的x,y指定的位置进行点击操作
for l in all_list:
 x = l[0]
 y = l[1]
 ActionChains(bro).move_to_element_with_offset(code_img_ele, x, y).click().perform()
 time.sleep(0.5)

这样我们就实现了验证码的识别操作。

3、输入账号和密码,并点击登录按钮

#输入账号和密码
 put1=bro.find_element_by_id('J-userName')
 #当验证码识别错误后,需要清空账号重新输入
 put1.clear()
 #输入账号
 put1.send_keys('########')
 time.sleep(1)
 put2=bro.find_element_by_id('J-password')
 put2.clear()
 #输入密码
 put2.send_keys('##########')
 time.sleep(1)
 #点击登录按钮
 bro.find_element_by_id('J-login').click()

点击登录按钮后,会出现如下图所示的弹框

Selenium之模拟登录铁路12306的示例代码

因此,我们需要定位到该提示框,并实现滑块的向右滑动

4、滑块滑动

#处理提示框
time.sleep(0.5)
span=bro.find_element_by_xpath('//*[@id="nc_1_n1z"]')
action = ActionChains(bro)
#点击长按指定的标签
action.click_and_hold(span).perform()
action.drag_and_drop_by_offset(span,400,0).perform()

有的时候,当滑块移动后,会出现如下图所示的情况:

Selenium之模拟登录铁路12306的示例代码

因此,我们需要点击刷新,并重新进行滑块的移动,所以对代码做稍微的改动:

while True:
 try:
  info=bro.find_element_by_xpath('//*[@id="J-slide-passcode"]/div/span').text
  print(info)
  if info=='哎呀,出错了,点击刷新再来一次':
  	 #点击刷新
  bro.find_element_by_xpath('//*[@id="J-slide-passcode"]/div/span/a').click()
  time.sleep(0.2)
  #重新移动滑块
  span = bro.find_element_by_xpath('//*[@id="nc_1_n1z"]')
  action = ActionChains(bro)
  # 点击长按指定的标签
  action.click_and_hold(span).perform()
  action.drag_and_drop_by_offset(span, 400, 0).perform()
  time.sleep(7)
 except:
  print('ok!')
  break

至此,我们便实现了铁路12306的登录,如下图所示

Selenium之模拟登录铁路12306的示例代码

是不是觉得很简单啊。

5、完整代码

# -*- coding: utf-8 -*-

#验证码识别示例
import requests
from hashlib import md5
class Chaojiying_Client(object):
 def __init__(self, username, password, soft_id):
 self.username = username
 password = password.encode('utf8')
 self.password = md5(password).hexdigest()
 self.soft_id = soft_id
 self.base_params = {
  'user': self.username,
  'pass2': self.password,
  'softid': self.soft_id,
 }
 self.headers = {
  'Connection': 'Keep-Alive',
  'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
 }
 def PostPic(self, im, codetype):
 """
 im: 图片字节
 codetype: 题目类型 参考 http://www.chaojiying.com/price.html
 """
 params = {
  'codetype': codetype,
 }
 params.update(self.base_params)
 files = {'userfile': ('ccc.jpg', im)}
 r = requests.post('http://upload.chaojiying.net/Upload/Processing.php', data=params, files=files, headers=self.headers)
 return r.json()
 def ReportError(self, im_id):
 """
 im_id:报错题目的图片ID
 """
 params = {
  'id': im_id,
 }
 params.update(self.base_params)
 r = requests.post('http://upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
 return r.json()

#使用selenium打开登录页面
from selenium import webdriver
import time
from PIL import Image
from selenium.webdriver import ActionChains
from selenium.webdriver.support import expected_conditions as EC, wait

#创建对象
#executable_path=path:下载好的驱动程序的路径
bro = webdriver.Chrome(executable_path='chromedriver.exe')
#12306的登录网址
bro.get('https://kyfw.12306.cn/otn/resources/login.html')
#窗口最大化
bro.maximize_window()
#点击账号登录
bro.find_element_by_xpath('/html/body/div[2]/div[2]/ul/li[2]/a').click()
time.sleep(1)
while True:
 try:
 #save_screenshot就是将当前页面进行截图且保存
 bro.save_screenshot('aa.png')
 #确定验证码图片对应的左上角和右下角的坐标(裁剪的区域就确定)
 code_img_ele = bro.find_element_by_xpath('//*[@id="J-loginImg"]')
 location = code_img_ele.location # 验证码图片左上角的坐标 x,y
 #print('location:',location)
 size = code_img_ele.size #验证码标签对应的长和宽
 #print('size:',size)
 #左上角和右下角坐标
 rangle = (
 int(location['x']), int(location['y']), int(location['x'] + size['width']), int(location['y'] + size['height']))
 #至此验证码图片区域就确定下来了
 i = Image.open('./aa.png')
 code_img_name = './code.png'
 #crop根据指定区域进行图片裁剪
 frame = i.crop(rangle)
 frame.save(code_img_name)
 #将验证码图片提交给超级鹰进行识别
 chaojiying = Chaojiying_Client('#####', '#######', '######')	#用户账号>>密码>>软件ID
 im = open('code.png', 'rb').read()									#本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
 id=chaojiying.PostPic(im, 9004)['pic_id']    #截取的验证码照片以及验证码的类别代号
 result = chaojiying.PostPic(im, 9004)['pic_str']   #识别结果
 all_list = [] #要存储即将被点击的点的坐标 [[x1,y1],[x2,y2]]
 #识别错误后,会返回题分,官网给的demo并没有这一句,哈哈哈,坑吧,就是让你多花钱
 chaojiying.ReportError(id)
 if '|' in result:
  list_1 = result.split('|')
  print(list_1)
  count_1 = len(list_1)
  for i in range(count_1):
  xy_list = []
  x = int(list_1[i].split(',')[0])
  y = int(list_1[i].split(',')[1])
  xy_list.append(x)
  xy_list.append(y)
  all_list.append(xy_list)
 else:
  x = int(result.split(',')[0])
  y = int(result.split(',')[1])
  xy_list = []
  xy_list.append(x)
  xy_list.append(y)
  all_list.append(xy_list)
 #遍历列表,使用动作链对每一个列表元素对应的x,y指定的位置进行点击操作
 for l in all_list:
  x = l[0]
  y = l[1]
  ActionChains(bro).move_to_element_with_offset(code_img_ele, x, y).click().perform()
  time.sleep(0.5)
 #输入账号和密码
 put1=bro.find_element_by_id('J-userName')
 #当验证码识别错误后,需要清空账号重新输入
 put1.clear()
 put1.send_keys('username') #你的账号
 time.sleep(1)
 put2=bro.find_element_by_id('J-password')
 put2.clear()
 put2.send_keys('password') #你的密码
 time.sleep(1)
 bro.find_element_by_id('J-login').click()
 #处理提示框
 time.sleep(3)
 span=bro.find_element_by_xpath('//*[@id="nc_1_n1z"]')
 action = ActionChains(bro)
 #点击长按指定的标签
 action.click_and_hold(span).perform()
 action.drag_and_drop_by_offset(span,400,0).perform()
 time.sleep(8)
 while True:
  try:
  info=bro.find_element_by_xpath('//*[@id="J-slide-passcode"]/div/span').text
  print(info)
  if info=='哎呀,出错了,点击刷新再来一次':
   bro.find_element_by_xpath('//*[@id="J-slide-passcode"]/div/span/a').click()
   time.sleep(0.2)
   span = bro.find_element_by_xpath('//*[@id="nc_1_n1z"]')
   action = ActionChains(bro)
   # 点击长按指定的标签
   action.click_and_hold(span).perform()
   action.drag_and_drop_by_offset(span, 400, 0).perform()
   time.sleep(7)
  except:
  print('ok!')
  break
 #释放动作链
 action.release()
 break
 except:
 time.sleep(3)
time.sleep(12)
#登录成功
bro.find_element_by_link_text('确定').click()
time.sleep(0.5)
bro.find_element_by_link_text('首页').click()
#输入起点、终点以及时间,查询车票
start_city='北京'
end_city='上海'
date='2020-08-05'
#选择起点
bro.find_element_by_xpath('//*[@id="fromStationText"]').click()
time.sleep(2)
#这只遍历了热门城市,要是想遍历其他城市,自己写一个循环就行
city_list=bro.find_elements_by_xpath('//*[@id="ul_list1"]/li')
for city in city_list:
 if city.text==start_city:
 city.click()
 break
time.sleep(2)
#选择终点
bro.find_element_by_xpath('//*[@id="toStationText"]').click()
for city in city_list:
 if city.text==end_city:
 city.click()
 break
time.sleep(2)
js = "$('input[id=train_date]').removeAttr('readonly')"
bro.execute_script(js)
dt=bro.find_element_by_id('train_date')
dt.clear()
dt.send_keys(date)
time.sleep(2)
bro.find_element_by_xpath('/html/body/div[3]/div[2]/div/div[1]/div/div[1]/ul/li[1]/a').click()
time.sleep(0.5)
bro.find_element_by_xpath('//*[@id="isStudentDan"]/i').click()
time.sleep(2)
bro.find_element_by_id('search_one').click()
time.sleep(2)

到此这篇关于Selenium之模拟登录铁路12306的示例代码的文章就介绍到这了,更多相关Selenium 模拟登录12306内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Python 相关文章推荐
python的即时标记项目练习笔记
Sep 18 Python
python中利用xml.dom模块解析xml的方法教程
May 24 Python
Python基于回溯法子集树模板解决选排问题示例
Sep 07 Python
Python3实现发送QQ邮件功能(附件)
Dec 23 Python
详解python3中tkinter知识点
Jun 21 Python
python 内置函数汇总详解
Sep 16 Python
pytorch查看torch.Tensor和model是否在CUDA上的实例
Jan 03 Python
基于Python共轭梯度法与最速下降法之间的对比
Apr 02 Python
Python request操作步骤及代码实例
Apr 13 Python
pycharm激活方法到2099年(激活流程)
Sep 22 Python
pytorch简介
Nov 11 Python
pytest进阶教程之fixture函数详解
Mar 29 Python
python的flask框架难学吗
Jul 31 #Python
使用PyCharm安装pytest及requests的问题
Jul 31 #Python
django和flask哪个值得研究学习
Jul 31 #Python
Windows下PyCharm配置Anaconda环境(超详细教程)
Jul 31 #Python
Python如何输出百分比
Jul 31 #Python
PyCharm配置anaconda环境的步骤详解
Jul 31 #Python
Pycharm及python安装详细教程(图解)
Jul 31 #Python
You might like
桌面中心(三)修改数据库
2006/10/09 PHP
apache+php完美解决301重定向的两种方法
2011/06/08 PHP
php中用于检测一个地理IP地址是否可用的代码
2012/02/19 PHP
php实现四舍五入的方法小结
2015/03/03 PHP
php支持断点续传、分块下载的类
2016/05/02 PHP
php-msf源码详解
2017/12/25 PHP
Thinkphp整合阿里云OSS图片上传实例代码
2019/04/28 PHP
getElementsByTagName vs selectNodes效率 及兼容的selectNodes实现
2010/02/26 Javascript
jquery 与NVelocity 产生冲突的解决方法
2011/06/13 Javascript
JS继承 笔记
2011/07/13 Javascript
Javascript中 关于prototype属性实现继承的原理图
2013/04/16 Javascript
jQuery实现长文字部分显示代码
2013/05/13 Javascript
jquery控制display属性为none或block
2014/03/31 Javascript
IE8中使用javascript动态加载CSS的解决方法
2014/06/17 Javascript
Nodejs为什么选择javascript为载体语言
2015/01/13 NodeJs
JavaScript中操作Mysql数据库实例
2015/04/02 Javascript
jQuery动画效果图片轮播特效
2016/01/12 Javascript
详解从零搭建 vue2 vue-router2 webpack3 工程
2017/11/22 Javascript
详解Node.js一行命令上传本地文件到服务器
2019/04/22 Javascript
解决vuex刷新状态初始化的方法实现
2019/08/15 Javascript
vuex页面刷新导致数据丢失的解决方案
2020/12/10 Vue.js
一篇文章快速了解Python的GIL
2018/01/12 Python
python如何读写csv数据
2018/03/21 Python
Python双向循环链表实现方法分析
2018/07/30 Python
python全栈知识点总结
2019/07/01 Python
Python在cmd上打印彩色文字实现过程详解
2019/08/07 Python
将python字符串转化成长表达式的函数eval实例
2020/05/11 Python
html5的新玩法——语音搜索
2013/01/03 HTML / CSS
如何向接受结构参数的函数传入常数值
2016/02/17 面试题
数据库方面面试题
2012/04/22 面试题
中医专业应届生求职信
2013/11/17 职场文书
高级方案规划工程师岗位职责
2013/11/29 职场文书
竞选大队长演讲稿
2014/04/29 职场文书
相亲大会策划方案
2014/06/05 职场文书
叶问观后感
2015/06/15 职场文书
Ruby序列化和持久化存储 Marshal和Pstore介绍
2022/04/18 Ruby