项目前言

自学python差不多有一年半载了,这两天利用在甲方公司搬砖空闲之余写了个小项目——【12306-tiebanggg-master】注:本项目仅供学习研究,如若侵犯到贵公司权益请联系我第一时间进行删除;切忌用于一切非法行为,否则后果自行承担!

项目描述

通过分析123O6,车次列表信息无需登录即可获取,但是如果我们想要使用代码代替手动为自己进行购票时,则需要进行登录网站;为了不给对方服务器造成压力,本项目并未开启多线程。项目为全自动进行车票的购买,包括(登录、验证码识别、刷票、判断是否有票、预购、下单、邮箱通知)本项目思路——使用selenium登录网站获取到cookie(供后续购票使用),定时检查cookie是否有效,获取列车列表信息及购票流程均使用requests的方式进行。

之前我们学习了如何获取与到列车信息数据,本篇将讲解【使用selenium结合超级鹰登录验证】。如果没有看过之前的文章请移步:python爬取12306列车信息自动抢票并自动识别验证码(一)列车数据获取篇

下一章节【python爬取12306列车信息自动抢票并自动识别验证码(三)购票篇】 已完结

本项目结构如图:

技术手段及第三方打码平台的使用

本项目思路、过程过于复杂,共分为【列车数据获取篇】【登录验证篇】【购票篇】【项目结束】,本篇文章只讲第一点【登录验证篇】
涉及到的第三方包:
① selenium
② requests
③ lxml
④ PIL
⑤chromedriver.exe(谷歌浏览器驱动)
⑥google浏览器

chromedriver.exe 需要[点击此处下载]与google浏览器[点击此处下载]的版本相对应。

打码平台:超级鹰

这里不做过多介绍,详情请移步第一篇文章,好了,正片开始~

一. Selenium登录

本次“幸运”对象地址:aHR0cHM6Ly9reWZ3LjEyMzA2LmNuL290bi9yZXNvdXJjZXMvbG9naW4uaHRtbA==

1. 登录起始页
①首先进入登录起始页

可以看到,网页中有两种登录方式,分别是扫码登录和账号登录。网站默认显示扫码登录页面,我们使用账号登录。所以接下来使用selenium+chromedriver来对界面进行操作。

大致流程:打开登录页-选择账号登录-输入账号密码-图片点选验证-点击登录-滑块验证-成功登录

selenium 的使用方法在这里不做过多介绍,安装方法:pip install selenium

创建一个包:Chepiao_Models.py

首先在Chepiao_Models中定义一个类:class MonestFunctionSeleniumSpider(object),通过这个类实现整体的登录验证流程
创建一个初始化信息配置包:setting_class.py

  1. selenium打开登录页
    先在setting_class.py 中定义一个初始化信息类:setting,并定义需要的初始化信息变量(账号 密码 chromedriver.exe路径 网站链接等)

    然后我们在Chepiao_Models.py中定义一个MonestFunctionSeleniumSpider类并导入相应的包
    首先在类中定义初始化参数:
    def __init__(self):self.lourl = setting.login_url      # 登录地址self.driver_path = setting.driver_path  # chromedriver路径self.username = setting.username        # 账号self.password = setting.password        # 密码# 实例化一个Options对象,作用是为给浏览器(driver)对象添加一些参数,防止网站监测到我们为机器对象self.chrome_options = Options()self.chrome_options.add_argument('disable-infobars')self.chrome_options.add_experimental_option('excludeSwitches', ['enable-automation'])# 实例化一个浏览器对象(driver)self.driver = webdriver.Chrome(executable_path=self.driver_path, options=self.chrome_options)# 设置隐式等待self.wait = WebDriverWait(self.driver, 15, 0.3)# 浏览器窗口最大化self.driver.maximize_window()

接下来定义一个登录函数login,通过代码打开浏览器,并请求登陆地址:

    def login(self):self.driver.get(self.logurl)time.sleep(2)print("正在登录12306平台 请稍后...")

运行效果

可以看到程序自动打开浏览器并且访问登录页,接下来我们需要选择账号登录,上代码

self.wait.until(EC.visibility_of_element_located((By.LINK_TEXT, '账号登录'))).click()

输入账号和密码

self.wait.until(EC.visibility_of_element_located((By.ID, 'J-userName'))).send_keys(self.username)
time.sleep(0.5)
self.wait.until(EC.visibility_of_element_located((By.ID, 'J-password'))).send_keys(self.password)

运行效果

接下来是关键,想要登陆必须通过图片点选验证码才能够点击登录按钮
所以,接下来我们将使用超级鹰打码平台来进行图片点选的验证。首先需要到超级鹰官方网站注册一个账号,并且关注公众号之后,平台会送你1000个积分,这里每次验证需要消耗25点积分,用来测试已经足够。这里我们使用的是返回坐标类型的验证方式:

选择开发文档中的python语言Demo下载
下载到的代码如图

将代码拷贝到我们项目下的Cjy_Pythons文件夹chaojy_chapts.py

具体的操作请自行阅读相关文档,在这里不做过多介绍,接下来进行验证码的识别。

超级鹰验证码识别思路为:将验证码图片上传至对应接口,平台识别后返回验证码中正确图片的位置坐标(坐标在之后的验证识别过程中是必须的)。

所以这里我们想要识别验证,就得先把网页中的验证码图片获取到。经过多次观察,网页的验证码会在每次请求的时候进行更换,也就是说我们使用selenium这种方式来登录的话,就不能使用requests来对验证码图片的获取,所以这里提供一种解决方案:selenium打开网页并填写账号密码后,通过截屏的方式来获取验证码图片。
上代码:

# 首先获取验证码图片的对应element对象
code_img_ele = self.driver.find_element_by_id('J-loginImgArea')
# 定义一个截取全屏的保存路径
img_path = 'images/全屏图像.png'
# 通过get_screenshot_as_file方法截取全屏图像并保存
self.driver.get_screenshot_as_file(img_path)
# 使用location 对element对象获取验证码左上角的坐标
location = code_img_ele.location  # 验证码左上角坐标
# size获取验证码图片的长和宽
size = code_img_ele.size  # 验证码长和宽(需要比例缩放)
"""
这里来想要在网页上截取一张图片需要知道这个图片的左上角坐标和右下角坐标,通过PIL的
Image方法进行获取,传入对象为rangle,rangle中需要含有验证码左上角坐标以及右下角坐标
的四个数值,下面为构造计算四个值的方法。可以看到我在每一个坐标值后面都乘了1.25,这里
是因为我的电脑缩放比例为125%,乘以多少根据个人情况而定,100%缩放比例时乘以1.
"""
rangle = (int(location['x'] * 1.25), int(location['y'] * 1.25),int(location['x'] * 1.25 + size['width'] * 1.25),int(location['y'] * 1.25 + size['height'] * 1.25)
)
# 打开之前我们截取的全屏图像
i = Image.open(img_path)
# 定义验证码保存路径
base_path1 = 'images/验证码图片.png'
# 通过crop方法对全屏图像进行验证码的截取
frame = i.crop(rangle)
# 保存至本地
frame.save(base_path1)

运行结果:

可以看到,在images文件夹下生成了两张图:全屏图像.png、验证码图片.png,而我们需要的是【验证码图片.png】这张验证码图片。接下来将验证码图片.png上传到超级鹰进行打码并获取正确图片坐标点,代码:

chaojiying = Chaojiying_Client(setting.cjy_user, setting.cjy_pwd, '910269')
im = open(base_path1, 'rb').read()
x_y = chaojiying.PostPic(im, 9004)['pic_str']all_list = []
if '|' in x_y:list_1 = x_y.split('|')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(int(x/1.25))xy_list.append(int(y/1.25))all_list.append(xy_list)
else:xy_list = []x = int(x_y.split(',')[0])y = int(x_y.split(',')[1])xy_list.append(int(x/1.25))xy_list.append(int(y/1.25))all_list.append(xy_list)
print('验证码坐标:', all_list)

运行结果:
验证码图片:
可以看到验证码图片中有两个蒸笼,所以验证码坐标点返回了两个坐标点,这两个坐标点就是我们需要进行点击验证的坐标位置。接下来进行验证码的点击验证:

for rangle in all_list:x = rangle[0]y = rangle[1]ActionChains(self.driver).move_to_element_with_offset(code_img_ele, x, y).click().perform()time.sleep(0.2)
time.sleep(0.3)
self.driver.find_element_by_xpath('//*[@id="J-login"]').click()
time.sleep(1)

运行效果:

图片点选验证搞定!!!!!当图片点选验证通过之后,会弹出一个滑块验证码,接下来就来搞一搞这个滑块。[/手动滑稽]先看看滑块验证码:
只需要定位到红框中的滑块,计算出需要滑动到最右边的最大距离,并使用ActionChains调用click_and_hold().perform()方法对小滑块进行点击,使用move_by_offset进行滑动即可:
这里可以使用小滑块的id特征进行定位

nc_1_n1z = self.driver.find_element_by_xpath('//*[@id="nc_1_n1z"]')

计算滑动距离并进行滑动操作:
可以发现当我鼠标放到整块滑块中时,很容易能得到他的宽=340,但是代码块尽量不使用写死的变量,对于后续网页改版之后更改会比较麻烦,这里我使用的是通过获取他的element对象并计算出长和宽,图中箭头位置就是整个长条滑块的id特征id=nc_1__scale_text :

# 获取滑块直线距离
nc_1__scale_text = self.driver.find_element_by_id("nc_1__scale_text")
size = nc_1__scale_text.size   # 获取滑块框的(宽度和高度)
width = size['width']        # 获取直线滑动距离(宽)
print("滑块直线移动距离:", width)# 实例话一个ActionChains对象
action = ActionChains(self.driver)
# 模拟点击小滑块并且按住不放
action.click_and_hold(nc_1_n1z).perform()
# 滑块在x轴上滑动距离为width, y轴上不变
action.move_by_offset(width, 0)
# 立即执行取消点击小滑块
action.release().perform()

运行结果:


可以看到这里我们得到了滑动距离,但是细心的同学可以发现滑块验证并未通过,来看看提示:

经过我n次试验,都是这个提示。原因是因为selenium在进行滑动操作时速度过快,一步到胃,被网页识别出是机器经行操作,所以滑块识别未通过。

解决方案:将滑动距离进行切割,比如这里我将距离width=340切割成tracks列表,tracks=[40, 60, 70, 80, 90],遍历x for x in tracks: 每次进行x距离的滑动,并且计算在其滑动到总长度的4/5时进行一个减速操作(模拟人为)。上代码:
定义一个get_tracks函数,切割并计算滑动速度、距离

def get_tracks(self, width):v = 0t = 0.5tracks = []current = 0mid = width * 4 / 5while current < width:if current < mid:a = 2else:a = -3s = v * t + 0.5 * a * (t ** 2)v = v + a * tcurrent += stracks.append(round(s))return {'tracks': tracks}

再将原来的滑动模块稍作修改:

# 获取滑块直线距离(长)
nc_1__scale_text = self.driver.find_element_by_id("nc_1__scale_text")
size = nc_1__scale_text.size
width = size['width']
print("滑块直线移动距离:", width)tracks = self.get_tracks(width)
print(tracks)
# 实例话一个ActionChains对象
action = ActionChains(self.driver)
# 模拟点击小滑块并且按住不放
action.click_and_hold(nc_1_n1z).perform()
# 滑块在x轴上滑动距离为width, y轴上不变
for track in tracks['tracks']:action.move_by_offset(track, 0)
# 立即执行取消点击小滑块
action.release().perform()

运行效果:


可以看到还是未通过hhhhh,不慌,有问题先上百度搞不定再来csdn再搞不定就还有google、知乎啥的一个一个来[/手动滑稽]~~

经过我一番查阅文档最终找到解决方案,虽然在初始化参数定义时我添加了Options对象,也添加了一些参数避免被识别,但还是被网站识别我们是机器进行操作,so~,只能搞搞js这个样子来勉强通过了,这里js文件不提供,自己也可以查阅相关文档进行编写获取,也可以v我获取。这里在init函数中添加代码:

再运行:


好了,图形点选和滑块搞定,看到提示为账号或密码错误,我们填写正确的账号密码再进行登录:

OKK!登录成功,接下来编写cookie保存代码:

import pickle# 保存cookie
time.sleep(3)
cookies = self.driver.get_cookies()
pickle.dump(cookies, open("cookied/{}_cookie".format(setting.username), 'wb'))cookie_dict = {}for cookie in cookies:cookie_dict[cookie["name"]] = cookie["value"]
print(cookie_dict)

需要注意的是登陆成功之后需要time.sleep(3),这里休眠3秒钟,等待网页加载完成后再获取cookie保存至本地。运行结果:

这里成功返回cookie

并且在cooked文件下生成了一个cookie文件,后续购票就可以从本地进行cookie的获取了。

登录篇到此结束。

下一章节【python爬取12306列车信息自动抢票并自动识别验证码(三)购票篇】 已完结

码字不容易,如果本篇文章对你有帮助请点个赞8,谢谢~
有不明白的地方可以私信我。
合作及源码获取vx:tiebanggg 【注明来意】
QQ交流群:735418202
需项目【12306-tiebanggg-master】源码请关注微信公众号 :

*注:本文为原创文章,转载文章请附上本文链接,谢谢!

python爬取12306列车信息自动抢票并自动识别验证码(二)selenium登录验证篇相关推荐

  1. 2021最新 python爬取12306列车信息自动抢票并自动识别验证码(三)购票篇

    项目前言 tiebanggg又来更新了,项目--[12306-tiebanggg-master]注:本项目仅供学习研究,如若侵犯到贵公司权益请联系我第一时间进行删除:切忌用于一切非法途径,否则后果自行 ...

  2. 2021最新python爬取12306列车信息自动抢票并自动识别验证码

    项目描述 项目前言 tiebanggg又来更新了,项目--[12306-tiebanggg-master]注:本项目仅供学习研究,如若侵犯到贵公司权益请联系我第一时间进行删除:切忌用于一切非法途径,否 ...

  3. python爬取12306列车信息自动抢票并自动识别验证码(一)列车数据获取篇

    项目前言 自学python差不多有一年半载了,这两天利用在甲方公司搬砖空闲之余写了个小项目--[12306-tiebanggg-master].注:本项目仅供学习研究,如若侵犯到贵公司权益请联系我第一 ...

  4. Python爬取12306车票信息

    Python3爬取12306车票信息 第一次写爬虫,咱从入门级--12306车票爬取 开始 我们要爬取的信息是https://www.12306.cn/index/上的车票信息 当我们选择出发地和目的 ...

  5. 12306自动抢票及自动识别验证码功能(二)

    这几天用keras+tensorflow训练了下验证码,弄了一个gpu的显卡,训练速度杠杠的^_^, 准度直接提升了几个档次,(小白阶段只会用框架^_^),图片识别准度基本达到96%,文字识别无限接近 ...

  6. python爬取12306_python爬取12306列车信息

    #!/usr/bin/env python #coding=utf8 #12306查票爬虫 import requests,json,sys #获取地址代码 #https://kyfw.12306.c ...

  7. 12306自动抢票及自动识别验证码功能(一)

    其实12306抢票之前有做过,近年来随着技术的发展AI的兴起,我也随波逐流,研究了下python深度学习,来实现12306全自动抢票工具. 1. 实现12306自动识别验证码,我这里用的比较简单,目前 ...

  8. 12306自动抢票及自动识别验证码功能(三)--分流

    这几天研究了下12306分流,之前12306pass不停切换CDN来刷新数据,这样减少缓存时间,写了个脚本从官网上抓了一批CDN服务器来测试 验证cdn脚本: #! /usr/bin/env pyth ...

  9. python爬取12306_Python爬取12306车次信息代码详解

    详情查看下面的代码: 如果被识别就要添加一个cookie如果没有被识别的话就要一个user-agent就好了.如果出现乱码就设置编码格式为utf-8 #静态的数据一般在elements中(复制文字到s ...

最新文章

  1. MySQL中Order By与Limit不要一起用
  2. 安装MMCV和MMDET
  3. 扩展用户体验之操作栏ActionBar
  4. kotlin 用协程做网络请求_Android使用Kotlin协程封装网络库
  5. 模拟赛-20190228-随机数(random)
  6. python工作目录_如何使用python 3获取当前工作目录?
  7. C#开发微信门户及应用(28)--微信“摇一摇·周边”功能的使用和接口的实现
  8. asp.net---jquery--ajax 实现滚动条滚动到底部分页显示
  9. php社工源码,社工库源码搜集
  10. 显示计算机硬盘驱动器更改,Win10专业版识别不了第二个硬盘驱动器的处理方法...
  11. 使用clusterProfiler进行KEGG富集分析
  12. Selenium-WEB自动化学习笔记--更新ing
  13. FCN(全卷积网络)部分函数方法更新说明
  14. 初体验微信小程序记事本
  15. openwrt路由器接华为E3372(E8372)网卡实现4G转有线和WIFI
  16. 在线编辑excel文件实现服务器后台存储,及页面还原
  17. usercity 小程序_微信小程序API 用户信息
  18. postgresql tips
  19. 魏则西事件与百度医疗竞价排名引发的伦理与道德问题
  20. 添加网站(虚拟主机)

热门文章

  1. 用Python编写自动下载网络小说的脚本
  2. docker 配置国内镜像源不起作用
  3. 标准日本语初级 语法整理
  4. MeSH 医学主题词数据库
  5. 实用!7个强大的Python机器学习库!⛵
  6. 19【字节流、字符流】
  7. 如何进入BIOS设置界面
  8. IO复用模型同步,异步,阻塞,非阻塞及实例详解
  9. java split 双竖线_HIVE 常用函数及实例
  10. mybatis源码(一)