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实现的归并排序算法示例
Nov 21 Python
PyCharm设置每行最大长度限制的方法
Jan 16 Python
python最小生成树kruskal与prim算法详解
Jan 17 Python
python enumerate内置函数用法总结
Jan 07 Python
Python实现AI自动抠图实例解析
Mar 05 Python
Python3自定义json逐层解析器代码
May 11 Python
Django如何使用redis作为缓存
May 21 Python
python如何快速拼接字符串
Oct 28 Python
快速创建python 虚拟环境
Nov 28 Python
python 模拟登陆github的示例
Dec 04 Python
Python 恐龙跑跑小游戏实现流程
Feb 15 Python
python数字图像处理:图像的绘制
Jun 28 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
使用无限生命期Session的方法
2006/10/09 PHP
php 常用类汇总 推荐收藏
2010/05/13 PHP
PHP人民币金额数字转中文大写的函数代码
2013/02/27 PHP
php通过文件头判断格式的方法
2016/05/28 PHP
LBS blog sql注射漏洞[All version]-官方已有补丁
2007/08/26 Javascript
getElementsByTagName vs selectNodes效率 及兼容的selectNodes实现
2010/02/26 Javascript
JavaScript高级程序设计 阅读笔记(十三) js定义类或对象
2012/08/14 Javascript
document.documentElement和document.body区别介绍
2013/09/16 Javascript
jQuery中使用Ajax获取JSON格式数据示例代码
2013/11/26 Javascript
node.js中的console.timeEnd方法使用说明
2014/12/09 Javascript
JavaScript中的立即执行函数表达式介绍
2015/03/15 Javascript
JavaScript处理解析JSON数据过程详解
2015/09/11 Javascript
js实现仿微博滚动显示信息的效果
2015/12/21 Javascript
AngularJS中实现显示或隐藏动画效果的方式总结
2015/12/31 Javascript
深入理解JavaScript中Ajax
2016/08/02 Javascript
微信小程序 图片宽高自适应详解
2017/05/11 Javascript
Python使用matplotlib的pie函数绘制饼状图功能示例
2018/01/08 Python
tensorflow创建变量以及根据名称查找变量
2018/03/10 Python
python中yield的用法详解——最简单,最清晰的解释
2019/04/04 Python
Python数据类型之Set集合实例详解
2019/05/07 Python
Python多版本开发环境管理工具介绍
2019/07/03 Python
python 动态迁移solr数据过程解析
2019/09/04 Python
Python 装饰器@,对函数进行功能扩展操作示例【开闭原则】
2019/10/17 Python
python批量处理txt文件的实例代码
2020/01/13 Python
python使用docx模块读写docx文件的方法与docx模块常用方法详解
2020/02/17 Python
python实现猜拳游戏
2020/03/04 Python
python中pandas库中DataFrame对行和列的操作使用方法示例
2020/06/14 Python
Python爬虫教程知识点总结
2020/10/19 Python
canvas实现圆形进度条动画的示例代码
2017/12/26 HTML / CSS
介绍一下SQL中union,intersect和minus
2012/04/05 面试题
成品库仓管员岗位职责
2014/04/06 职场文书
法院授权委托书范文
2014/08/02 职场文书
2014年学生党支部工作总结
2014/12/20 职场文书
学籍证明模板
2015/06/18 职场文书
行为习惯主题班会
2015/08/14 职场文书
SQL实现LeetCode(175.联合两表)
2021/08/04 MySQL