100字范文,内容丰富有趣,生活中的好帮手!
100字范文 > python爬取12306列车信息自动抢票并自动识别验证码(二)selenium登录验证篇

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

时间:2024-03-12 16:46:36

相关推荐

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

项目前言

自学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

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.sizewidth = 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# 保存cookietime.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】源码请关注微信公众号 :

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

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。