• 登陆时间: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】思维导图

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

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

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

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

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


【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】效果实现动图

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

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

  1. Python3 爬虫实战 — 模拟登陆哔哩哔哩【滑动验证码对抗】

    登陆时间:2019-10-21 实现难度:★★★☆☆☆ 请求链接:https://passport.bilibili.com/login 实现目标:模拟登陆哔哩哔哩,攻克滑动验证码 涉及知识:滑动验证 ...

  2. 湖南工业大学教务系统爬虫(模拟登陆篇)

    湖南工业大学教务系统爬虫(模拟登陆篇) 之前写了一个教务系统的爬虫程序,可以根据用户要求爬取任何一部分的数据,也可以模拟提交数据,可能这也是部分工大计算机学生比较感兴趣的,所以今天就在这分享一下整个的 ...

  3. 使用Python模拟登陆12306并全自动下单

    最近一段时间一直在研究用Python模拟登陆12306网站并自动刷票下单,经过一段时间的摸索,终于完成了代码,实现了12306刷票的功能.话不多说,先给大伙儿看看成果.我录制了一段时间,展示了自动刷票 ...

  4. python3爬虫实战:requests库+正则表达式爬取头像

    python3爬虫实战:requests库+正则表达式爬取头像 网站url:https://www.woyaogexing.com/touxiang/qinglv/new/ 浏览网页:可以发现每个图片 ...

  5. selenium模拟登陆之截屏验证码位置跑偏

    在用selenium做爬虫的模拟登陆时遇到验证码图片截取下来的和目标区域的图片不匹配 截到的: 目标是这样的: 明显坐标写的不对,导致截屏区域不是验证码的区域. 只能一步一步取修改坐标,来确定具体的图 ...

  6. Python爬虫之模拟登陆

    女神找我倾诉,实验室实验选不上,刚出来就被秒了,让我帮她选实验,我想我这万年单身的手速估计还是抢不过我这些师兄们,干脆写一个脚本吧,这样以后女神就找我选实验了,废话少说,切入主题,看这篇教程首先得保证 ...

  7. python爬虫(一):模拟登陆微博

    最近花了不少时间来学python爬虫,觉得还是有很多问题的,比如说requests.get获得Pixiv的网页源代码,一直获取不到,不过我猜测大概是headers的问题,准备之后处理. 废话少说我们先 ...

  8. 【Python3爬虫】最新的12306爬虫

    一.写在前面 我在以前写过一次12306网站的爬虫,当时实现了模拟登录和查询车票,但是感觉还不太够,所以对之前的代码加以修改,还实现了一个订购车票的功能. 二.主要思路 在使用Selenium做模拟登 ...

  9. socket模拟http的登陆_Python网络爬虫之模拟登陆 !

    为什么要模拟登陆? Python网络爬虫应用十分广泛,但是有些网页需要用户登陆后才能获取到信息,所以我们的爬虫需要模拟用户的登陆行为,在登陆以后保存登陆信息,以便浏览该页面下的其他页面. 保存用户信息 ...

最新文章

  1. 一年75次上微博热搜!宇宙首富Tony老师上线!马斯克DIY发型
  2. Python 进阶之路 (九) 再立Flag, 社区最全的itertools深度解析(上)
  3. 用Javascript模拟微信飞机大战游戏
  4. tcp的无延时发送_腾讯网红程序员,详解带宽、延时、吞吐率、PPS 这些都是啥?...
  5. prometheus的搭建与使用
  6. go执行二进制文件的方法:通过shell脚本来调用二进制文件,直接执行go的二进制文件会存在参数传递问题
  7. vue 3D旋转木马轮播图
  8. MFC工作笔记0011---atoi的用法
  9. windows ping 间隔_对于常用的ping命令,这些小技巧你不一定知道
  10. 【转】HeadFirst 组合模式+迭代器错误原因以及解决代码
  11. MS windows下的网络访问设置经典--仅来宾
  12. VBA连接Excel数据库
  13. MySQL Workbench报错说 seems to be a different OS
  14. “凡客好声音”摇滚派对专场 正火热抢票中!
  15. webview 上打开QQ客户端,QQ登入后自动打开添加QQ群的方法
  16. 经典C语言编程100例——题目+答案代码(21-30)
  17. ChatGPT国产平替出现了:APP商店就能下载,还可给AI加人设,背后公司刚成立3个月...
  18. 2.3【Linux系统移植之三】:使用BusyBox构建根文件系统(rootfs)
  19. 东方联盟为何那么团结?郭盛华是怎样做到的?
  20. Kubernetes v1.22.0 正式发布,众多 API 和功能被移除

热门文章

  1. nutch2.1在windows平台上使用eclipsedebug 存储在mysql的搭建过程
  2. android 9格式吗,Android Studio中关于9-patch格式图片的编译错误
  3. 我的世界javamod怎么装_装暖气片,10个有8个人都想知道的这点事儿!
  4. drools动态配置规则_关于规则引擎
  5. MongoDB数据库设计备忘
  6. java长连接转短连接_java原生程序redis连接(连接池/长连接和短连接)选择问题...
  7. python dataframe删除重复行_详解pandas使用drop_duplicates去除DataFrame重复项参数
  8. linux 下查看程序依赖的库
  9. Wince6.0 cleartype
  10. matlab求零空间,matlab求矩阵的零空间的一组整数基,该怎样操作?