Python3 爬虫实战 — 模拟登陆12306【点触验证码对抗】


  • 登陆时间:2019-10-21
  • 实现难度:★★★☆☆☆
  • 请求链接:https://kyfw.12306.cn/otn/resources/login.html
  • 实现目标:模拟登陆中国铁路12306,攻克点触验证码
  • 涉及知识:点触验证码的攻克、自动化测试工具 Selenium 的使用、对接在线打码平台
  • 完整代码:https://github.com/TRHX/Python3-Spider-Practice/tree/master/AutomationTool/12306-login
  • 其他爬虫实战代码合集(持续更新):https://github.com/TRHX/Python3-Spider-Practice
  • 爬虫实战专栏(持续更新):https://itrhx.blog.csdn.net/article/category/9351278

文章目录

  • 【1x00】思维导图
  • 【2x00】打码平台选择
  • 【3x00】初始化模块
    • 【3x01】初始化函数
    • 【3x02】账号密码输入函数
  • 【4x00】验证码处理模块
    • 【4x01】验证码图片剪裁函数
    • 【4x02】验证码坐标解析函数
    • 【4x03】模拟点击验证码函数
  • 【5x00】登录模块
  • 【6x00】完整代码
    • 【6x01】12306.py
    • 【6x02】chaojiying.py
  • 【7x00】效果实现动图


【1x00】思维导图

01

  • 利用自动化测试工具 Selenium 直接模拟人的行为方式来完成验证

  • 发送请求,出现验证码后,剪裁并保存验证码图片

  • 选择在线打码平台,获取其API,以字节流格式发送图片

  • 打码平台人工识别验证码,返回验证码的坐标信息

  • 解析返回的坐标信息,模拟点击验证码,完成验证后点击登陆

02


【2x00】打码平台选择

关于打码平台:在线打码平台全部都是人工在线识别,准确率非常高,原理就是先将验证码图片提交给平台,平台会返回识别结果在图片中的坐标位置,然后我们再解析坐标模拟点击即可,常见的打码平台有超级鹰、云打码等,打码平台是收费的,拿超级鹰来说,1元 = 1000题分,识别一次验证码将花费一定的题分,不同类型验证码需要的题分不同,验证码越复杂所需题分越高,比如 7 位中文汉字需要 70 题分,常见 4 ~ 6 位英文数字只要 10 题分,其他打码平台价格也都差不多,本次实战使用超级鹰打码平台

使用打码平台:在超级鹰打码平台注册账号,官网:http://www.chaojiying.com/ ,充值一块钱得到 1000 题分,在用户中心里面申请一个软件 ID ,在价格体系里面确定验证码的类型,先观察 12306 官网,发现验证码是要我们点击所有满足条件的图片,一般有 1 至 4 张图片满足要求,由此可确定在超级鹰打码平台的验证码类型为 9004(坐标多选,返回1~4个坐标,如:x1,y1|x2,y2|x3,y3), 然后在开发文档里面获取其 Python API,下载下来以备后用


【3x00】初始化模块

【3x01】初始化函数

# 12306账号密码
USERNAME = '155********'
PASSWORD = '***********'# 超级鹰打码平台账号密码
CHAOJIYING_USERNAME = '*******'
CHAOJIYING_PASSWORD = '*******'# 超级鹰打码平台软件ID
CHAOJIYING_SOFT_ID = '********'
# 验证码类型
CHAOJIYING_KIND = '9004'class CrackTouClick():def __init__(self):self.url = 'https://kyfw.12306.cn/otn/resources/login.html'# path是谷歌浏览器驱动的目录,如果已经将目录添加到系统变量,则不用设置此路径path = r'F:\PycharmProjects\Python3爬虫\chromedriver.exe'chrome_options = Options()chrome_options.add_argument('--start-maximized')self.browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)self.wait = WebDriverWait(self.browser, 20)self.username = USERNAMEself.password = PASSWORDself.chaojiying = ChaojiyingClient(CHAOJIYING_USERNAME, CHAOJIYING_PASSWORD, CHAOJIYING_SOFT_ID)

定义 12306 账号(USERNAME)、密码(PASSWORD)、超级鹰用户名(CHAOJIYING_USERNAME)、超级鹰登录密码(CHAOJIYING_PASSWORD)、超级鹰软件 ID(CHAOJIYING_SOFT_ID)、验证码类型(CHAOJIYING_KIND),登录页面 url ,谷歌浏览器驱动的目录(path),浏览器启动参数等,将超级鹰账号密码等相关参数传递给超级鹰 API


【3x02】账号密码输入函数

def get_input_element(self):# 登录页面发送请求self.browser.get(self.url)# 登录页面默认是扫码登录,所以首先要点击账号登录login = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-hd-account')))login.click()time.sleep(3)# 查找到账号密码输入位置的元素username = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-userName')))password = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-password')))# 输入账号密码username.send_keys(self.username)password.send_keys(self.password)

分析页面可知,登陆页面默认出现的是扫描二维码登陆,所以要先点击账号登录,找到该 CSS 元素为 login-hd-account,调用 click() 方法实现模拟点击,此时出现账号密码输入框,同样找到其 ID 分别为 J-userNameJ-password,调用 send_keys() 方法输入账号密码


【4x00】验证码处理模块

def crack(self):# 调用账号密码输入函数self.get_input_element()# 调用验证码图片剪裁函数image = self.get_touclick_image()bytes_array = BytesIO()image.save(bytes_array, format='PNG')# 利用超级鹰打码平台的 API PostPic() 方法把图片发送给超级鹰后台,发送的图像是字节流格式,返回的结果是一个JSONresult = self.chaojiying.PostPic(bytes_array.getvalue(), CHAOJIYING_KIND)print(result)# 调用验证码坐标解析函数locations = self.get_points(result)# 调用模拟点击验证码函数self.touch_click_words(locations)# 调用模拟点击登录函数self.login()try:# 查找是否出现用户的姓名,若出现表示登录成功success = self.wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.welcome-name'), '谭先生'))print(success)cc = self.browser.find_element(By.CSS_SELECTOR, '.welcome-name')print('用户' + cc.text + '登录成功')# 若没有出现表示登录失败,继续重试,超级鹰会返回本次识别的分值except TimeoutException:self.chaojiying.ReportError(result['pic_id'])self.crack()

crack() 为验证码处理模块的主函数

调用账号密码输入函数 get_input_element(),等待账号密码输入完毕

调用验证码图片剪裁函数 get_touclick_image(),得到验证码图片

利用超级鹰打码平台的 API PostPic() 方法把图片发送给超级鹰后台,发送的图像是字节流格式,返回的结果是一个JSON,如果识别成功,典型的返回结果类似于:

{'err_no': 0, 'err_str': 'OK', 'pic_id': '6002001380949200001', 'pic_str': '132,127|56,77', 'md5': 
'1f8e1d4bef8b11484cb1f1f34299865b'}

其中,pic_str 就是识别的文字的坐标,是以字符串形式返回的,每个坐标都以 | 分隔

调用 get_points() 函数解析超级鹰识别结果

调用 touch_click_words() 函数对符合要求的图片进行点击

调用模拟点击登录函数 login(),点击登陆按钮模拟登陆

使用 try-except 语句判断是否出现了用户信息,判断依据是是否有用户姓名的出现,出现的姓名和实际姓名一致则登录成功,如果失败了就重试,超级鹰会返回该分值


【4x01】验证码图片剪裁函数

def get_touclick_image(self, name='12306.png'):# 获取验证码的位置element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code')))time.sleep(3)location = element.locationsize = element.sizetop, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width']# 先对整个页面截图screenshot = self.browser.get_screenshot_as_png()screenshot = Image.open(BytesIO(screenshot))# 根据验证码坐标信息,剪裁出验证码图片captcha = screenshot.crop((left, top, right, bottom))captcha.save(name)return captcha

首先查找到验证码的坐标信息,先对整个页面截图,然后根据验证码坐标信息,剪裁出验证码图片

location 属性可以返回该图片对象在浏览器中的位置,坐标轴是以屏幕左上角为原点,x 轴向右递增,y 轴向下递增,size 属性可以返回该图片对象的高度和宽度,由此可以得到验证码的位置信息


【4x02】验证码坐标解析函数

def get_points(self, captcha_result):# 超级鹰识别结果以字符串形式返回,每个坐标都以|分隔groups = captcha_result.get('pic_str').split('|')# 将坐标信息变成列表的形式locations = [[int(number) for number in group.split(',')] for group in groups]return locations

get_points() 方法将超级鹰的验证码识别结果变成列表的形式


【4x03】模拟点击验证码函数

def touch_click_words(self, locations):element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code')))# 循环点击正确验证码的坐标for location in locations:print(location)ActionChains(self.browser).move_to_element_with_offset(element, location[0], location[1]).click().perform()

循环提取正确的验证码坐标信息,依次点击验证码


【5x00】登录模块

def login(self):submit = self.wait.until(EC.element_to_be_clickable((By.ID, 'J-login')))submit.click()

分析页面,找到登陆按钮的 ID 为 J-login,调用 click() 方法模拟点击按钮实现登录


【6x00】完整代码

【6x01】12306.py

# =============================================
# --*-- coding: utf-8 --*--
# @Time    : 2019-10-21
# @Author  : TRHX
# @Blog    : www.itrhx.com
# @CSDN    : https://blog.csdn.net/qq_36759224
# @FileName: 12306.py
# @Software: PyCharm
# =============================================import time
from io import BytesIO
from PIL import Image
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from chaojiying import ChaojiyingClient
from selenium.common.exceptions import TimeoutException# 12306账号密码
USERNAME = '155********'
PASSWORD = '***********'# 超级鹰打码平台账号密码
CHAOJIYING_USERNAME = '********'
CHAOJIYING_PASSWORD = '********'# 超级鹰打码平台软件ID
CHAOJIYING_SOFT_ID = '******'
# 验证码类型
CHAOJIYING_KIND = '9004'class CrackTouClick():def __init__(self):self.url = 'https://kyfw.12306.cn/otn/resources/login.html'# path是谷歌浏览器驱动的目录,如果已经将目录添加到系统变量,则不用设置此路径path = r'F:\PycharmProjects\Python3爬虫\chromedriver.exe'chrome_options = Options()chrome_options.add_argument('--start-maximized')self.browser = webdriver.Chrome(executable_path=path, chrome_options=chrome_options)self.wait = WebDriverWait(self.browser, 20)self.username = USERNAMEself.password = PASSWORDself.chaojiying = ChaojiyingClient(CHAOJIYING_USERNAME, CHAOJIYING_PASSWORD, CHAOJIYING_SOFT_ID)def crack(self):# 调用账号密码输入函数self.get_input_element()# 调用验证码图片剪裁函数image = self.get_touclick_image()bytes_array = BytesIO()image.save(bytes_array, format='PNG')# 利用超级鹰打码平台的 API PostPic() 方法把图片发送给超级鹰后台,发送的图像是字节流格式,返回的结果是一个JSONresult = self.chaojiying.PostPic(bytes_array.getvalue(), CHAOJIYING_KIND)print(result)# 调用验证码坐标解析函数locations = self.get_points(result)# 调用模拟点击验证码函数self.touch_click_words(locations)# 调用模拟点击登录函数self.login()try:# 查找是否出现用户的姓名,若出现表示登录成功success = self.wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '.welcome-name'), '谭先生'))print(success)cc = self.browser.find_element(By.CSS_SELECTOR, '.welcome-name')print('用户' + cc.text + '登录成功')# 若没有出现表示登录失败,继续重试,超级鹰会返回本次识别的分值except TimeoutException:self.chaojiying.ReportError(result['pic_id'])self.crack()# 账号密码输入函数def get_input_element(self):# 登录页面发送请求self.browser.get(self.url)# 登录页面默认是扫码登录,所以首先要点击账号登录login = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-hd-account')))login.click()time.sleep(3)# 查找到账号密码输入位置的元素username = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-userName')))password = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'input#J-password')))# 输入账号密码username.send_keys(self.username)password.send_keys(self.password)# 验证码图片剪裁函数def get_touclick_image(self, name='12306.png'):# 获取验证码的位置element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code')))time.sleep(3)location = element.locationsize = element.sizetop, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size['width']# 先对整个页面截图screenshot = self.browser.get_screenshot_as_png()screenshot = Image.open(BytesIO(screenshot))# 根据验证码坐标信息,剪裁出验证码图片captcha = screenshot.crop((left, top, right, bottom))captcha.save(name)return captcha# 验证码坐标解析函数,分析超级鹰返回的坐标def get_points(self, captcha_result):# 超级鹰识别结果以字符串形式返回,每个坐标都以|分隔groups = captcha_result.get('pic_str').split('|')# 将坐标信息变成列表的形式locations = [[int(number) for number in group.split(',')] for group in groups]return locations# 模拟点击验证码函数def touch_click_words(self, locations):element = self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.login-pwd-code')))# 循环点击正确验证码的坐标for location in locations:print(location)ActionChains(self.browser).move_to_element_with_offset(element, location[0], location[1]).click().perform()# 模拟点击登录函数def login(self):submit = self.wait.until(EC.element_to_be_clickable((By.ID, 'J-login')))submit.click()if __name__ == '__main__':crack = CrackTouClick()crack.crack()

【6x02】chaojiying.py

import requests
from hashlib import md5class ChaojiyingClient(object):def __init__(self, username, password, soft_id):self.username = usernamepassword = password.encode('utf8')self.password = md5(password).hexdigest()self.soft_id = soft_idself.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()

【7x00】效果实现动图

最终实现效果图:(关键信息已经过打码处理)
02

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/437893.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SharePoint无代码工作流设计开发实例——交通费报销流程(一)

开发环境: (1)SharePoint Designer 2010 (2)SharePoint Online(Office 365) 关于Office 365和SharePoint Online的相关信息请参见以下链接 http://blog.csdn.net/miragesky2049/article/details/7242982 1、需求分析 组织结构: 用户类型&#xff1a…

Python3 爬虫实战 — 58同城武汉出租房【加密字体对抗】

爬取时间:2019-10-21爬取难度:★★★☆☆☆请求链接:https://wh.58.com/chuzu/爬取目标:58同城武汉出租房的所有信息涉及知识:网站加密字体的攻克、请求库 requests、解析库 Beautiful Soup、数据库 MySQL 的操作完整代…

SharePoint无代码工作流设计开发实例——交通费报销流程(二)

3、InfoPath表单的设计及发布 (1)数据源和页面设计 域(元素)和域(属性)如下: 白色为域(元素),该类型的字段,发布时,提升成字段后,列表库的数据是…

SharePoint无代码工作流设计开发实例——交通费报销流程(三)

4、SharePoint Designer无代码工作流的设计 开发工具为SharePoint Designer 2010 新建“列表工作流”,绑定“交通费报销单”表单库 设置工作流启动选项“创建项目时自动启动工作流” 编辑工作流 (关于以下工作流详细设计可参考http://blog.csdn.net/mi…

SharePoint学习札记[2] — MOSS2007体系结构概述

熟悉微软Office产品线的人都知道,MOSS 2007的上一个版本SPS 2003的全称是 Office Sharepoint Portal Server,在新版本中,微软去掉了Portal一个词。从这个角度很清楚地看到微软对MOSS 2007的定位,是的,MOSS 已经不再仅仅…

SharePoint学习札记[3] — Office SharePoint Server 2007部署

MOSS的第一步就是部署啦!《0ffice SharePoint Server 2007部署图示指南》图文并茂的展示了OSS2007的部署过程。虽然并不难,但对MOSS还摸不清方向的人来说是个不错的指导。手册的作者没有署名,也查不到出自谁手,在这里对他的劳动成果表示感谢…

SharePoint学习札记[4] — 创建SharePoint站点

为减少创建SharePoint站点过程中的麻烦,先打开IIS管理器,将“默认站点”停止或删除。后面的创建过程中,创建向导会自动创建端口为80的网站。 现在将: 1. 创建两个Web应用程序。 2. 创建共享服务。 3. 创建…

【Python CheckiO 题解】Sun Angle

CheckiO 是面向初学者和高级程序员的编码游戏,使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务,从而提高你的编码技能,本博客主要记录自己用 Python 在闯关时的做题思路和实现代码,同时也学习学习其他大神写的代码。 Chec…

Python CheckiO 题解系列 丨 博客目录索引

CheckiO 是面向初学者和高级程序员的编码游戏,使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务,从而提高你的编码技能,本题解系列主要记录自己在用 Python 闯关时的做题思路和实现代码,同时也学习学习其他大神写的代码。 …

文带你深入了解 Redis 内存模型

前言 Redis是目前最火爆的内存数据库之一,通过在内存中读写数据,大大提高了读写速度,可以说Redis是实现网站高并发不可或缺的一部分。 我们使用Redis时,会接触Redis的5种对象类型(字符串、哈希、列表、集合、有序集合…

【Python CheckiO 题解】Pawn Brotherhood

CheckiO 是面向初学者和高级程序员的编码游戏,使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务,从而提高你的编码技能,本博客主要记录自己用 Python 在闯关时的做题思路和实现代码,同时也学习学习其他大神写的代码。 Chec…

【Python CheckiO 题解】Xs and Os Referee

CheckiO 是面向初学者和高级程序员的编码游戏,使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务,从而提高你的编码技能,本博客主要记录自己用 Python 在闯关时的做题思路和实现代码,同时也学习学习其他大神写的代码。 Chec…

c#多线程总结(纯干货)

线程基础 创建线程 static void Main(string[] args) {Thread t new Thread(PrintNumbers);t.Start();//线程开始执行PrintNumbers();Console.ReadKey(); }static void PrintNumbers() {Console.WriteLine("Starting...");for (int i 1; i < 10; i){Console.Wr…

Hexo 博客优化之实用功能添加系列(持续更新)

2022-01-25 更新&#xff1a;博客新地址&#xff1a;https://www.itbob.cn/&#xff0c;文章距上次编辑时间较远&#xff0c;部分内容可能已经过时&#xff01; 本文将讲述一些 Hexo 博客实用功能的添加&#xff0c;本文以作者 luuman 的 spfk 主题和作者 xaoxuu 的 Material X…

【Python CheckiO 题解】The Warriors

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本博客主要记录自己用 Python 在闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 Chec…

【Python CheckiO 题解】Multiply (Intro)

CheckiO 是面向初学者和高级程序员的编码游戏&#xff0c;使用 Python 和 JavaScript 解决棘手的挑战和有趣的任务&#xff0c;从而提高你的编码技能&#xff0c;本博客主要记录自己用 Python 在闯关时的做题思路和实现代码&#xff0c;同时也学习学习其他大神写的代码。 Chec…

关于DateTime的一点记录 ToString(yyyy-MM-dd HH:mm:ss)

DateTime dt DateTime.Now; string z dt.ToString("yyyy-MM-dd HH:mm:ss");//你知道这个是“年月日时分秒”的格式吧? string a dt.ToString("yyyy-MM-dd HH:mm:ss ms");//这个你认为一定是 毫秒的格式? string b dt.ToString("yyyy-MM-dd HH:…

SharePoint List item数量超过5000的解决办法

SharePoint一个list里面的item数量超过5000会提示“视图无法显示&#xff0c;因为超过管理员限制设定的列表视图阈值” 在CSDN里面有比较好的解决方案&#xff0c;在这里先记录下来&#xff0c;以后有用 方案一&#xff0c;定期自动归档 不用写TimerJOb&#xff0c; 可以用cont…

什么是SharePoint?

在聊SharePoint开发之前&#xff0c;有必要说下什么是SharePoint. 在我工作的过程中&#xff0c;经常遇到客户对SharePoint不太了解的情况。有客户说&#xff0c;SharePoint太烂了&#xff0c;DropBox能做到的什么什么功能&#xff0c;SharePoint竟然做不到&#xff0c;很明显…

SharePoint 2013的REST编程基础

1. SharePoint 2013对REST编程的支持 自从SharePoint2013开始&#xff0c; SharePoint开始了对REST 编程的支持&#xff0c;这样除了.NET , Silverlight&#xff0c; Powershell之外&#xff0c; 又多了一种可以和SharePoint Server进行CSOM编程的方式。那么&#xff0c;问题来…