大家好,好久不见。又到每日分享Python小技能的时候了。最近因为疫情影响,所以更新内容比较慢…今天周一,就来更新一波,心血来潮,是时候上线经营了。其实也没想到有啥好分享的,不如分享一些干货给大家,今天就分享一下如何识别图片验证码并发送手机验证码的思路和代码(基于python,对于过程中的涉及的浏览器技术原理我不过多赘述,不懂且想了解的小伙伴请自行搜索学习)。
首先,我们的程序分为三部分:
1、代理部分(防止频繁请求被封,非必须)
2、识别部分(主要是某些验证码识别网站的demo,自己下载后进行相应改进)
3、执行部分
选取一个网站作为例子,例如这个网站,它的验证形式为图片验证码验证,且图片比较复杂,对打码平台的要求较高:
我们打开开发者模式,切换到network窗口,手动输入手机号码,并输入图片验证码的内容,点击发送,进行抓包分析(框出或者标识部分都是重要部分,由于部分字段涉及个人及网站隐私安全,已做马赛克处理):
上面就是我们模拟发送短信验证码抓到的包,这个表单数据(form data)还是相对简单,除了phone(输入的手机号)和Verify(输入的图片验证码)是变化的,其他都是固定格式。有些相对复杂的,会有其他的加密方式,如md5对手机号加某些信息加密后传输,这些往往需要我们进行js解析(此处不过多赘述,请自行了解)。现在我们要做的就是识别验证码后将数据传入并模拟发送,话不多说,我们开始。
什么代理:
代理服务器(Proxy Server)的功能是代理网络用户去取得网络信息。形象地说,它是网络信息的中转站,是个人网络和Internet服务商之间的中间代理机构,负责转发合法的网络信息,对转发进行控制和登记。
代理服务器作为连接Internet与Intranet的桥梁,在实际应用中发挥着极其重要的作用,它可用于多个目的,最基本的功能是连接,此外还包括安全性、缓存、内容过滤、访问控制管理等功能。更重要的是,代理服务器是Internet链路级网关所提供的一种重要的安全功能,它的工作主要在开放系统互联(OSI)模型的对话层。
(更多详情,如专享ip、隧道ip这些请自行了解)
我们这里做代理ip,主要是为了安全起见,由于我们的频繁访问,网站管理者发现异常后极有可能将我们的ip封禁,所以我们需要使用代理ip,通过代理ip作为桥梁去访问。
市面上有很多的代理ip提供,有免费的、收费的,当然,免费的代理ip体验上是很差的,很多都不能用。我们自己可以去购买一些网站的套餐使用会流畅很多。至于哪家网站的代理ip好用,可以自行查找相关信息。我这里使用的是 芝麻HTTP。
为了使代码看起来更简单,我使用单个ip进行演示,代码如下:
#proxy.pyproxy = '113.128.28.102:4231'proxies = { 'http':'http://'+proxy}
识别验证码我们仍然使用的是第三方的网站的代码,python有Tesseract-OCR模块供识别图片,但识别率低。
市面上很多打码平台,识别率最好的是超级鹰,但收费较贵。其他的还有 图鉴、斐斐打码 之类的平台,收费较为亲民,但识别率不是很高。
对于我们这个例子,验证码较为复杂,一般的打码平台识别不出,所以我们选择使用超级鹰。
对于这类的打码平台,我们一般是注册充值后,复制他们提供的demo代码,将账号信息填入使用。每次使用需要下载图片验证码到本地,由于验证码具有时效性,每次访问都会变化,我们为了保证两次访问一致,主要保证两次访问的cookies一致。
代码如下:
####Python学习交流群:906715085####
!/usr/bin/env python# coding:utf-8
#这个代码是超级鹰提供的demo的基础上,本人为了方便调用自行改造后的代码
import osfrom proxy
import proxiesimport requestsfrom hashlib
import md5headers={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36'}class Chaojiying_Client(object):
def __init__(self, username, password, soft_id):
self.username = username
password = password.encode('utf8')
self.password = md5(password).hexdigest()
self.soft_id = soft_id
self.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()
def main(imgPath,CodeType):
#这一行填写自己在超级鹰官网的账号信息,记得把我在代码标的中文替换
chaojiying = Chaojiying_Client('账号', '密码', '校验码')
im = open(imgPath, 'rb').read()
res=chaojiying.PostPic(im, CodeType)
code=res['pic_str']
return code
#imgDispose这一函数是执行代码和识别代码之间调用的桥梁,不使用代理请将 ,proxies=proxies 删除def imgDispose(img_url,val):
response=requests.get(url=img_url,headers=headers,proxies=proxies)
img_data=response.content
cookies=response.cookies.get_dict()
#每次识别先将图片保存到本地,识别完删除
with open('img\CJYcodeImg.png',mode='wb') as f:
f.write(img_data) code = main('img\CJYcodeImg.png', val)
os.remove('img\CJYcodeImg.png')
return code,cookies
执行部分会用到我们抓包的信息,并相关信息填对就行,请自行举一反三。
mport reimport time
import chaojiyingDemofrom proxy
import proxiesimport requests
phoneNumber='手机号'
headers2={ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36'}userAgent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.93 Safari/537.36'def identify(code_url,typeId,sendCode_url,headers,data,dataCode,funcName): try:
result=chaojiyingDemo.imgDispose(code_url,typeId)
imgCode=result[0]
cookies=result[1]
data[dataCode]=imgCode
response = requests.post(url=sendCode_url,headers=headers,cookies=cookies,data=data, proxies=proxies, timeout=10) return '函数'+funcName+' 执行返回的结果为>>>>>>'+response.content.decode('utf-8')
except Exception as e:
return '函数' + funcName + ' 发生错误!' + str(e.args)def cjy2():#上学吧
headers = { 'Accept': 'text/plain, */*; q=0.01', 'Accept-Encoding': 'gzip, deflate, br', 'Accept-Language': 'zh-CN,zh;q=0.9',
'Connection': 'keep-alive',
'Content-Length': '60',
'Content-Type': 'application/x-www-form-urlencoded',
'Host': '',
'Origin': '',
'Referer': '', 'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="90", "Google Chrome";v="90"', 'sec-ch-ua-mobile': '?0', 'Sec-Fetch-Dest': 'empty', 'Sec-Fetch-Mode': 'cors', 'Sec-Fetch-Site': 'same-origin',
'User-Agent': userAgent, 'X-Requested-With': 'XMLHttpRequest' }
data={ 'phone': phoneNumber, 'dataid': '0', 'action': 'SMSSend', 'typeid': '0', }
#6601是打码平台识别的验证码类型码
identify('图片验证码的url',6001,'发送的url(抓包得到的)',headers,data,'Verify','cjy2')
cjy2()
7、最后
最近各地疫情爆发,大家一定要保护好自己。这一篇的小技巧还是比较简单的,学会了就可以开始练练手了。码代码还是有点辛苦(点赞),到这里就跟大家说再见了,不会的可以告诉我,我一般看见都会回复。