python: selenium获取验证码, selenium-webdriver 登录验证码的处理, Selenium获取验证码并识别,图片验证码反爬解决思路

 

获取验证码有两种思路

1.获取页面源代码,提取验证码图片

2.利用selenium截取页面,定位验证码元素的位置,利用Image进行处理,获取其中验证码部分

 

下面进行解析

1.获取页面源代码,提取验证码图片

如何获取源代码并提取验证码图片过程就不在分析了,既然看到这篇文章,相信这些工作都不在话下了。
这里只分析一下缺点:当提取验证码url后发现每次打开该验证码图片,其内容不断变化,
以搜狗验证码为例:weixin.sogou.com/antisp,该验证码是单独加载进页面,而非嵌入,这时候,单纯提取验证码url会发现当前验证码和提取验证码地址打开的内容不一样。这时候,我们需要一个更方便简单的方法。

2.利用selenium截取页面
selenium.webdriver 内置了截取当前页面的功能,其中:

a.WebDriver.Chrome自带的方法只能对当前窗口截屏,若是需要截取的窗口超过了一屏,就只能另辟蹊径了。

b.WebDriver.PhantomJS自带的方法支持对整个网页截屏。

在这里,我们利用两种方法均可,因为验证码界面通常比较简单。

结果如图:

python: selenium获取验证码,  selenium-webdriver 登录验证码的处理, Selenium获取验证码并识别,图片验证码反爬解决思路
python: selenium获取验证码, selenium-webdriver 登录验证码的处理, Selenium获取验证码并识别,图片验证码反爬解决思路

 

    #打开验证码界面
    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')

 

截取的图案:

python: selenium获取验证码,  selenium-webdriver 登录验证码的处理, Selenium获取验证码并识别,图片验证码反爬解决思路
python: selenium获取验证码, selenium-webdriver 登录验证码的处理, Selenium获取验证码并识别,图片验证码反爬解决思路

 

到这里,我们的验证码就拿下来啦,怎么处理呢?

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、聚合数据-验证码识别

 

下面分析一下怎样使用打码平台:

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获取验证码并识别,图片验证码反爬解决思路

Leave a Reply