感觉12306自动化登录是所有学习爬虫爱好者都必须经过的一道练习题,但是由于12306的版本更新,很多之前的自动化登录代码或多或少有些纰漏,所以我写了2021年最新的12306自动化登录,希望对你会有所启发。
需要导入的库如下:
from selenium import webdriver
from selenium.webdriver import ActionChains
import chaojiying
import base64
import re
from lxml import etree
from time import sleep
言不多说,直接进入正题,对于12306自动化登录,首先我们需要进入12306的官网首页,即如下图片
url='https://www.12306.cn/index/'
bro=webdriver.Chrome(executable_path='输入自己电脑的webdriver的绝对位置即可')
bro.get(url=url)
我们在这个图片里面的右上角可以看到登录与注册两个选项,我们需要选择登录这一选项,从而进入他的登陆页面,具体代码实现如下:
bro.find_element_by_xpath('//*[@id="J-header-login"]/a[1]').click()
代码中我们采用webdriver的find系列,通过xpath解析从而找到‘登录’的位置。实现过程如下:
按照图片里面数字的顺序,先左击第一个箭头所指的位置,然后左击第二个箭头所指的位置,在之后,我们需要在第三个箭头所指的位置右击,出来选项框之后,在copy那一栏里面就可以找到copy xpath,从而可以得到到‘登录’的xpath解析
进入登录页面之后,我们会发现是二维码登录,我们不采用二维码登录,我们使用的是账号登录,所以我们需要点击账号登录,从而进入他的账号登录页面。 具体代码如下:
bro.find_element_by_xpath('/html/body/div[2]/div[2]/ul/li[2]/a').click() # 点击账号登录
具体图片如下:
同理,也是按照图片里面数字的顺序,先左击第一个箭头所指的位置,然后左击第二个箭头所指的位置,在之后,我们需要在第三个箭头所指的位置右击,出来选项框之后,在copy那一栏里面就可以找到copy xpath,从而可以得到到‘账号登录’的xpath解析
从这个账号登录页面来看,我们需要填入用户名,用户密码以及需要识别验证码图片,接下来我们分步操作,分别完成这三项任务
首先是用户名的输入,我们需要先定位到用户名这一栏,然后输入我们的用户名即可。具体代码如下:
bro.find_element_by_xpath('//*[@id="J-userName"]').send_keys('******账号') # 输入账号
这个代码前面半段可以不用说,就是为了定位到用户名这一栏,而后面的send_keys的作用是将内容输入到我们定位好的浏览器的文本框里
具体操作流程如下:
这个图片操作流程前面已经多次重复了,这次就不讲了
然后是用户密码的输入,其实和用户名的输入步骤一模一样
代码如下:
bro.find_element_by_xpath('//*[@id="J-password"]').send_keys('****自己的账户密码即可') # 输入密码
图片如下:
最后是验证码的识别,验证码的识别我采用的是超级鹰第三方软件识别,可能会有其他更好更快的识别方式,这里为了简便,我用的是自己最熟悉的超级鹰识别。
准备工作:
将超级鹰模块封装到一个包里面,其实也很简单,就是在pycharm里面新建一个包,即python package,然后将超级鹰的模块代码复制到这个包下面就可以了,完成之后,其实就是这样子
有个最重要的一点,就是在封装之后呢,需要按照图示如下操作
这样pycharm就可以调用我们自己设置的包了,如果没有这个操作的话,前面我所说的导入的库里面import chaojiying这个语句就会报错,所以这个图片一定要执行哦
实现原理:
完成准备工作之后呢,我们来解释一下如何让第三方软件去识别验证码?
其实原理就是我们将验证码下载下来保存到指定的位置上,在保存之后,我们将保存的图片信息传递给第三方软件,在他识别之后呢?他会返回给我们x,y坐标,这些x,y坐标就是图片需要点击的x,y坐标,有一点需要注意,这些x,y是相对坐标,是相对于图片左上角(0,0)坐标而言的
最后一个知识点:
在理解这份代码之前,还有一个知识点也需要讲,就是我们在定位到验证码图片的时候,会有这样一段乱码
<img id="J-loginImg" `class="imgCode" alt="" src="data:image/jpg;base64,/9j/4AAQSkZJRgABAgAAAQABAAD/2wB........"
这个乱码太长了,后面的我就用省略号代替了
其实第一眼见到这个乱码我也很懵逼,查询之后才知道他就是将这个图片的信息以base64的编码格式直接保存到这个html中,这样在这个html打开图片的时候,就不用再动态加载了。然后我们继续看,在这个src属性里面,‘data:image/jpg;base64,’这些符号只是告诉我们这个图片的格式为jpg,他是采用了base64编码保存的,所以其实他并不是图片信息的二进制转换成的base64编码。了解这些之后,我们就只需要将base64编码的数据解码,然后以二进制的格式保存,这样就可以得到图片原来的样子了
要使用base64解码,需要用到库为:
import base64
需要用到的指令为:
base64.b64decode()
ok,接下来我们就先将这个图片保存到本地,具体代码如下
page = bro.page_source
# 获得浏览器此时的html信息
tree = etree.HTML(page)
pic_url = tree.xpath('//*[@id="J-loginImg"]/@src')[0]
# 通过xpath解析,将编码后的图片信息提取出来,保存到pic_url中
pic_url3 = re.sub('data:image/jpg;base64,', '', pic_url1)
# 使用正则的替换函数,将'data:image/jpg;base64,'替换成啥都没有,相当于删除这些内容,至此,我们得到了图片的base64编码的所有字符
pic = base64.b64decode(pic_url3)
#然后将字符进行解码
with open('code.jpg', 'wb') as fp:
fp.write(pic)
# 将解码后的字符以code的文件名保存到当前文件夹中,保存格式为二进制
ok,现在我们已经把图片保存下来了,接下来就只需要将图片接入第三方软件中,让他帮我们去识别,我们只是要等着就好了,具体代码如下:
chaojiying1 = chaojiying.Chaojiying_Client('自己注册的用户名', '用户名对应的密码', '自己注册的软件ID')
# 用户中心>>软件ID 生成一个替换 96001
im = open('./code.jpg', 'rb').read()
# 本地图片文件路径 来替换 code.jpg 有时WIN系统须要//
xy = chaojiying1.PostPic(im, 9004)['pic_str']
#9004是我们发送的验证码的格式,然后这个指令的返回值是一个字典,在键为['pic_str']的值的时候,保存地视返回的需要点击的坐标
我们获得坐标之后呢,就要想办法分别获取每个点的x,y,上面超级鹰验证码识别可以看到他返回形式是x1,y1|x2,y2|x3,y3这样子的,所以我们可以先以’|‘符号来切片,获得各个点的列表的值,然后对于列表,我们直接使用for in循环,在使用’,'来切片获得对应点的x,y的坐标值。对于每一个坐标值,我们直接先定位到图片上,然后使用指令
action.move_to_element_with_offset(目标元素, x, y).click().perform()
指令中,目标元素就是我们图片的xpath定位元素,x,y为我们获得的x,y的坐标值,这个指令会将鼠标移动到举例定位元素x方向有x的偏移量,y方向有y的偏移量,然后点击执行
具体代码如下:
xy = xy.split(sep='|')
# 通过使用符号'|'来进行劈分,将各个点保存在设置的xy列表里面
for i in xy:
cl = bro.find_element_by_xpath('//*[@id="J-loginImg"]')
# 需要先定位到图片地址,cl其实就是click的简写,我就图个简单
action.move_to_element(cl).perform() # ——鼠标移动到某个元素
# 将鼠标移动dao二维码图片中
xy1 = (i.split(sep=','))
x=int(xy1[0])
y=int(xy1[1])
# 分别获取x,y的值
action.move_to_element_with_offset(cl, x, y).click().perform()
# 就是通过刚才获得的x,y的值点击相应的位置
其实代码里面将鼠标移动到某个元素处,并不是我们所见的鼠标箭头就移动过去了,而是他就会定位到这个坐标处,鼠标其实压根没动
至此,相应的验证码识别即告一段落,接下来我们需要点击登录,其实点击登录特别简单,代码如下
bro.find_element_by_xpath('//*[@id="J-login"]').click()
ok,这样一来我们就相当于完成了验证码识别以及后续的点击登录操作,但是在运行过程中,你会发现有时候这个第三方软件并不能特别准确的识别验证码,有时也会出错,或者在图片保存的时候,有时也会出现图片没有正确保存的情况,对于这种情况,我们就想设置一个循环,将这个操作放到循环里面,只有验证码正确识别之后,我们才给它进行下一步
然后我们会发现,在他验证成功之后呢,还会出现一个滑块验证,那我们就可以检测在我们识别二维码点击登录之后,会不会出现滑块验证,然后我们定位到滑块这里,然后我们会发现在二维码识别之前和识别之后,这个滑块代码其实一直都在html中,只不过没有正确识别之前,里面的属性display的值为none,而正确识别之后呢,display属性就没了。所以其实我们只需要判断这个里面的display属性值是不是none,我们就可以判断有没有正确识别成功了
第一张图片是验证码正确验证之后滑块代码情况,第二张图片是正确验证之前的滑块代码情况
然后我们需要在一个循环里面不停的去判断,然后去执行,代码如下
while(True):
t2 = tree.xpath('//*[@id="login_slide_box"]/@style')[0]
# 获取到滑块验证的style的属性值,因为我们所讲的display在style属性中,所以需要这样判断
t=re.search(‘none’,t2)
# 使用正则搜索,判断none是否在style属性中,注意返回值不是str,所以我们需要强制转换
t=str(t)
action = ActionChains(bro)
if t!='None':
chaojiying1 = chaojiying.Chaojiying_Client('*****用户名', '***用户密码', '软件id') # 用户中心>>软件ID 生成一个替换 96001
im = open('./code.jpg', 'rb').read() # 本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
xy = chaojiying1.PostPic(im, 9004)['pic_str']
try:
print(xy)
xy = (xy.split(sep='|'))
for i in xy:
cl = bro.find_element_by_xpath('//*[@id="J-loginImg"]')
action.move_to_element(cl).perform() # ——鼠标移动到某个元素
xy1 = (i.split(sep=','))
x=int(xy1[0])
y=int(xy1[1])
action.move_to_element_with_offset(cl, x, y).click().perform()
bro.find_element_by_xpath('//*[@id="J-login"]').click()
except ValueError:
print('验证失败,正在重新验证')
# 我们在这里使用try expect是因为有时候,图片出错导致没有办法识别处我们需要点的坐标,会报错,我们将错误输出一下,然后让他重新验证就好了,不然错误会导致程序无法运行下去
这个代码其他部分上面已经讲过,所以这边不在赘复
在执行完这些之后呢,我们只需要完成最后一步,进行滑块码验证,首先我们需要用到一下指令
action.click_and_hold(drag1).perform()
# 这个其实就是长按点击目标元素
action.move_to_element_with_offset(drag1, 400, 0).perform()
# 这个其实就是拖动目标元素相对偏移量x,y
action.release().perform()
# 这个其实就是释放刚才长按的鼠标
其中呢,perform都表示执行的意思,然后action其实就是我之前的
action = ActionChains(bro) 即动作连
所以要完成滑块码验证,我们需要先定位到滑块码位置处,然后长按点击滑块码位置处,然后拖动滑块码位置进行偏移量,最后释放长按的鼠标
具体代码如下:
drag = bro.find_element_by_xpath('//*[contains(@class,"nc_iconfont btn_slid")]')
# 这个就是定位到滑块出,并且赋值给drag
action.click_and_hold(drag).perform()
# 这个就是长按滑块的意思
action.move_to_element_with_offset(drag, 400, 0).perform()
# 这个就是拖动滑块的意思
action.release().perform()
# 这个就是释放滑块
在完成这些之后呢,12306的登录程序就已经完成了,下面看我详细代码
#作业人:zhuangzhou
#作业时间:2021/2/20 12:32
from selenium import webdriver
from selenium.webdriver import ActionChains
import chaojiying
import base64
import re
from lxml import etree
from time import sleep
url='https://www.12306.cn/index/'
bro=webdriver.Chrome(executable_path='***相对地址')
bro.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
"source": """
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
})
"""
})
#这段也特别重要,是为了让webdriver规避被检测的风险
bro.get(url=url)
bro.maximize_window()
bro.find_element_by_xpath('//*[@id="J-header-login"]/a[1]').click()
bro.find_element_by_xpath('/html/body/div[2]/div[2]/ul/li[2]/a').click()
bro.find_element_by_xpath('//*[@id="J-userName"]').send_keys('****用户名')
bro.find_element_by_xpath('//*[@id="J-password"]').send_keys('***用户密码')
gd="window.scrollTo(0,document.body.scrollHeight=500)"
# '''用于设置鼠标滚动500像素点,'''
bro.execute_script(gd)
while(True):
page = bro.page_source
tree = etree.HTML(page)
pic_url = tree.xpath('//*[@id="J-loginImg"]/@src')[0]
pic_url3 = re.sub('data:image/jpg;base64,', '', pic_url)
pic = base64.b64decode(pic_url3)
with open('code.jpg', 'wb') as fp:
fp.write(pic)
t2 = tree.xpath('//*[@id="login_slide_box"]/@style')[0]
t=re.search('display',t2)
t=str(t)
action = ActionChains(bro)
if t!='None':
chaojiying1 = chaojiying.Chaojiying_Client('***用户名', '***用户密码', '软件ID') # 用户中心>>软件ID 生成一个替换 96001
im = open('./code.jpg', 'rb').read() # 本地图片文件路径 来替换 a.jpg 有时WIN系统须要//
xy = chaojiying1.PostPic(im, 9004)['pic_str']
try:
xy = (xy.split(sep='|'))
for i in xy:
cl = bro.find_element_by_xpath('//*[@id="J-loginImg"]')
action.move_to_element(cl).perform() # ——鼠标移动到某个元素
xy1 = (i.split(sep=','))
x=int(xy1[0])
y=int(xy1[1])
action.move_to_element_with_offset(cl, x, y).click().perform()
sleep(5)
bro.find_element_by_xpath('//*[@id="J-login"]').click()
sleep(5)
except ValueError:
print('验证失败,在重新验证')
else:
drag = bro.find_element_by_xpath('//*[contains(@class,"nc_iconfont btn_slid")]')
sleep(2)
action.move_to_element(drag).perform()
sleep(1)
action.click_and_hold(drag).perform()
sleep(1)
action.move_to_element_with_offset(drag, 400, 0).perform()
sleep(1)
action.release().perform()
break
至此,自动化登录程序就完成了,希望本文对大家有用,如果可以,一键三联哦