当我们希望在网页上托管来自外部来源的内容时,我们更喜欢使用IFrame。它可以是图像,视频,其他供应商的广告,突出显示某些信息等。 HTML提供了“ <iframe> </ iframe>”标签来标识HTML文档中的IFrame。 使用Selenium Python在Iframe之间切换…
python: selenium获取验证码, selenium-webdriver 登录验证码的处理, Selenium获取验证码并识别,图片验证码反爬解决思路
获取验证码有两种思路:
1.获取页面源代码,提取验证码图片
2.利用selenium截取页面,定位验证码元素的位置,利用Image进行处理,获取其中验证码部分
下面进行解析:
1.获取页面源代码,提取验证码图片
如何获取源代码并提取验证码图片过程就不在分析了,既然看到这篇文章,相信这些工作都不在话下了。
这里只分析一下缺点:当提取验证码url后发现每次打开该验证码图片,其内容不断变化,
以搜狗验证码为例:http://weixin.sogou.com/antispider/util/seccode.php?tc=1486691901,该验证码是单独加载进页面,而非嵌入,这时候,单纯提取验证码url会发现当前验证码和提取验证码地址打开的内容不一样。这时候,我们需要一个更方便简单的方法。
2.利用selenium截取页面
selenium.webdriver 内置了截取当前页面的功能,其中:
a.WebDriver.Chrome自带的方法只能对当前窗口截屏,若是需要截取的窗口超过了一屏,就只能另辟蹊径了。 b.WebDriver.PhantomJS自带的方法支持对整个网页截屏。
在这里,我们利用两种方法均可,因为验证码界面通常比较简单。
结果如图:

#打开验证码界面 driver = webdriver.Chrome() url = "http://weixin.sogou.com/antispider/?from=%2fweixinwap%3Fpage%3d2%26_rtype%3djson%26ie%3dutf8%26type%3d2%26query%3d%E6%91%A9%E6%8B%9C%E5%8D%95%E8%BD%A6%26pg%3dwebSearchList%26_sug_%3dn%26_sug_type_%3d%26" driver.set_window_size(1200, 800) cookies = driver.get_cookies() #处理cookies driver.get(url) for k,v in cookies.iteritems(): cookie_dict ={'name':k,'value':v} driver.add_cookie(cookie_dict) driver.get(url) #获取截图 driver.get_screenshot_as_file('CrawlResult/screenshot.png') #获取指定元素位置 element = driver.find_element_by_id('seccodeImage') left = int(element.location['x']) top = int(element.location['y']) right = int(element.location['x'] + element.size['width']) bottom = int(element.location['y'] + element.size['height']) #通过Image处理图像 im = Image.open('CrawlResult/screenshot.png') im = im.crop((left, top, right, bottom)) im.save('CrawlResult/code.png')
截取的图案:

到这里,我们的验证码就拿下来啦,怎么处理呢?
1.pytesser,tesseract,OCR 等库处理
2.验证码不多,并为了提高识别效率和简化操作,我采用了调用打码平台(ruokuai)API方法,价格大概是1块钱打100-150个(根据验证码位数和是否数字/字母混合)
验证码类型和价格介绍若快答题 – 价格类型
有用的代码片段:
1、对登录界面进行截图
url = '' driver = webdriver.PhantomJS() driver.get(url) driver.set_window_size(1200, 800) #此处一定要设置固定值,在其他的机器上面运行的时候可能会有问题 # 暂停10s确保登录界面加载完成 time.sleep(10) # 截取登录界面的屏幕 screenshot_path = 'screenshot.png' if os.path.exists(screenshot_path): os.remove(screenshot_path) driver.save_screenshot(screenshot_path)
2、从截图中扣取验证码
# 找到验证码元素,并获取到位置坐标
element = driver.find_element_by_xpath("//*[@id=\"login-content\"]/div[2]/div[4]/img")
left = int(element.location['x'])
top = int(element.location['y'])
right = int(element.location['x'] + element.size['width'])
bottom = int(element.location['y'] + element.size['height'])
# 从截图中抠出来验证码的区域
captcha_path = 'captcha.png'
if os.path.exists(captcha_path):
os.remove(captcha_path)
img = Image.open(screenshot_path)
img = img.crop((left, top, right, bottom))
img.save(captcha_path)
3、调用在线API进行验证码识别
以下代码来源于:聚合数据-验证码识别的示例代码,具体的可以参考官方的文档:
def captcha_recognition(appkey, codeType, imagePath): """ 调用验证码在线识别的API :param appkey: 平台申请的appkey :param codeType: 验证码类型 :param imagePath: 验证码图片路径 :return: 查询结果 """ if not os.path.exists(imagePath): return '' captcha_result = '' submitUrl = 'http://op.juhe.cn/vercode/index' # 接口地址 # buld post body data boundary = '----------%s' % hex(int(time.time() * 1000)) data = [] data.append('--%s' % boundary) data.append('Content-Disposition: form-data; name="%s"\r\n' % 'key') data.append(appkey) data.append('--%s' % boundary) data.append('Content-Disposition: form-data; name="%s"\r\n' % 'codeType') data.append(codeType) data.append('--%s' % boundary) fr = open(imagePath, 'rb') data.append('Content-Disposition: form-data; name="%s"; filename="b.png"' % 'image') data.append('Content-Type: %s\r\n' % 'image/png') data.append(fr.read()) fr.close() data.append('--%s--\r\n' % boundary) http_body = '\r\n'.join(data) try: req = urllib2.Request(submitUrl, data=http_body) req.add_header('Content-Type', 'multipart/form-data; boundary=%s' % boundary) req.add_header('User-Agent', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36') req.add_header('Referer', 'http://op.juhe.cn/') resp = urllib2.urlopen(req, timeout=60) qrcont = resp.read() result = json.loads(qrcont, 'utf-8') error_code = result['error_code'] if (error_code == 0): data = result['result'] captcha_result = data else: errorinfo = u"错误码:%s,描述:%s" % (result['error_code'], result['reason']) print errorinfo except Exception as e: print e return captcha_result
参考资料
下面分析一下怎样使用打码平台:
1.先把开发文档贴上:Main Page – 若快开发者文档
2.官方的调用方法(有两种:DOS版和普通版,下面贴的普通版,基本原理一样)
原理: 将验证码图片,打码平台账号,密码等按照指定格式调用API(访问URL)
class RClient(object): def __init__(self, username, password, soft_id, soft_key): self.username = username self.password = md5(password).hexdigest() self.soft_id = soft_id self.soft_key = soft_key self.base_params = { 'username': self.username, 'password': self.password, 'softid': self.soft_id, 'softkey': self.soft_key, } self.headers = { 'Connection': 'Keep-Alive', 'Expect': '100-continue', 'User-Agent': 'ben', } def rk_create(self, im, im_type, timeout=60): """ im: 图片字节 im_type: 题目类型 """ params = { 'typeid': im_type, 'timeout': timeout, } params.update(self.base_params) files = {'image': ('a.jpg', im)} r = requests.post('http://api.ruokuai.com/create.json', data=params, files=files, headers=self.headers) return r.json() def rk_report_error(self, im_id): """ im_id:报错题目的ID """ params = { 'id': im_id, } params.update(self.base_params) r = requests.post('http://api.ruokuai.com/reporterror.json', data=params, headers=self.headers) return r.json() rc = RClient('username', 'password', 'soft_id', 'soft_key') imagePath = 'CrawlResult/code.png' im = open(imagePath, 'rb').read() code_json = rc.rk_create(im, '验证码类型')# 类型和价格介绍http://www.ruokuai.com/home/pricetype
实例1:
pip install Pillow pip install pytesseract # 由于Python-tesseract是一个基于google's Tesseract-OCR的独立封装包,那么我们需要下载Tesseract-OCR进行安装,window下安装记住需要配置环境变量
#-*- coding:utf-8 -*- import time from selenium import webdriver from PIL import Image,ImageEnhance import pytesseract def get_auth_code(driver,codeEelement): '''获取验证码''' driver.save_screenshot('login/login.png') #截取登录页面 imgSize = codeEelement.size #获取验证码图片的大小 imgLocation = imgElement.location #获取验证码元素坐标 rangle = (int(imgLocation['x']),int(imgLocation['y']),int(imgLocation['x'] + imgSize['width']),int(imgLocation['y']+imgSize['height'])) #计算验证码整体坐标 login = Image.open("login/login.png") frame4=login.crop(rangle) #截取验证码图片 frame4.save('login/authcode.png') authcodeImg = Image.open('login/authcode.png') authCodeText = pytesseract.image_to_string(authcodeImg).strip() return authCodeText def pandarola_login(driver,account,passwd,authCode): '''登录pandarola系统''' driver.find_element_by_id('loginname').send_keys(account) driver.find_element_by_id('password').send_keys(passwd) driver.find_element_by_id('code').send_keys(authCode) driver.find_element_by_id('to-recover').click() time.sleep(2) title = driver.find_element_by_id('menuName-h').text #获取登录的标题 '''验证是否登录成功''' try: assert title == u'桌面' return '登录成功' except AssertionError as e: return '登录失败' if __name__ == '__main__': driver = webdriver.Chrome() driver.get('http://pandarola.pandadata.cn') driver.maximize_window() imgElement = driver.find_element_by_id('codeImg') authCodeText = get_auth_code(driver,imgElement) pandarola_login(driver,'admin','1',authCodeText) driver.quit()
由于我们系统属于内部系统,验证码比较简单,所以很轻松的识别了,但是有时候2和Z无法识别,只要登录失败重新获取再次登录即可。毕竟自己写的验证码识别,识别的成功的几率还时比较低,所以我这边从阿里云的云市场找到了一家公司,用他们的接口来识别,最起码公司的几个系统的验证码问题全部解决了,再也不用求开发了。
ps:
这里包含了接口的说明文档,大概可以自己看一下,https://market.aliyun.com/products/57126001/cmapi014396.html#sku=yuncode839600006,我这边演示下通过这个接口来识别验证码。
#-*- coding:utf-8 -*- import base64 import requests import json def read_picture_base64(fileNmae): '''验证码图片 base64加密格式''' with open(fileNmae,'rb') as f: base64Picture = base64.b64encode(f.read()) return base64Picture.decode() def authcode_picture_convert_string(appCode,querys,base64Picture): #appCode 接口的认证key,querys 验证码类型 '''通过第三方结果获取验证码''' header = { 'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8', #根据API的要求,定义相对应的Content-Type "Authorization":"APPCODE "+ appCode } url = 'http://jisuyzmsb.market.alicloudapi.com/captcha/recognize' #调用地址 bodys = {'type':querys,'pic':base64Picture} #请求参数 res = requests.post(url,headers=header,data=bodys) return res.text if __name__ == '__main__': appCode = '377e5f0fe10146ef9aa88bae756a3904' querys = 'e4' base64Picture = read_picture_base64('login/20170629232535.png') text = authcode_picture_convert_string(appCode,querys,base64Picture) authCode = json.loads(text)['result']['code'] #解析返回的结果 print(authCode)
本文:python: selenium获取验证码, selenium-webdriver 登录验证码的处理, Selenium获取验证码并识别,图片验证码反爬解决思路