Selenium之模拟登录铁路12306
最近接触了一些selenium模块的相关知识,觉得还挺有意思的,于是决定亲自尝试写一些爬虫程序来强化selenium模块(一定要多尝试、多动手、多总结)。本文主要使用python爬虫来模拟登录铁路12306官网。这儿得吐槽一句,铁路12306网站的反爬机制做的还是比较好。
话不多说,下面跟小墨一起来学习如何通过爬虫来实现铁路12306的登录。
一、 验证码破解
当我们输入账号和密码后,在点击登录按钮之前,还需要对验证码进行操作。对验证码的识别,已经有相关的处理平台,我们只需要借助第三方平台即可。
- 注册并登录超级鹰账号:点击链接进行注册https://www.chaojiying.com/user/login/;
- 点击购买题分,并进行充值;
- 点击软件id,创建一个软件Id(程序中会用到);
- 下载示例代码(开发文档—>选择相应的语言–>下载示例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://www.jianshu.com/p/3aa45532e179
上述内容完成后,我们就可以正式进入正题了,是不是很期待,那就跟着小墨往下走吧。
三、模拟登录
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()
点击登录按钮后,会出现如下图所示的弹框
因此,我们需要定位到该提示框,并实现滑块的向右滑动
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()
有的时候,当滑块移动后,会出现如下图所示的情况:
因此,我们需要点击刷新,并重新进行滑块的移动,所以对代码做稍微的改动:
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的登录,如下图所示
是不是觉得很简单啊。
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模块的基本使用,剩下的你可以尝试实现购票等功能,有什么问题可以私信或留言哦
如果小墨的文章对您有帮助的话,记得点赞和关注哦!!!!!!
本文地址:https://blog.csdn.net/wangbiao9982019/article/details/107634479