[记录]Python爬虫过程中遇到的简单带干扰线验证码处理方法

前言:

这些天,人力资源部有个需求:需要定期检查短信猫平台账号余额,于是乎,我向短信平台提交这个查询需求,对方给我答复是没办法。如此一来,只能看看能否通过用爬虫的方法去爬取后台数据了。

一、观察目标站点

使用开发者模式看了下目标站点,登陆是非常简单的三个选项:用户名,密码,验证码。验证码是由4位纯数字组成,每次通过get请求随机验证码的方法来刷新验证码。好了,观察到这,大致思路有了:get请求验证码图片,保存到本地进行识别,识别到的验证码加上用户名密码提交,去查询短信平台账号余额。到这里又有一个问题,如何识别验证码?通过谷歌查了下,谷歌有个识别库,非常好用:pytesser(见附件,将.rar去除,使用7z解压)

二、使用pytesser

有关于pytesser安装方法:http://blog.csdn.net/lanfan_11/article/details/45558573

以下代码,pytesser的使用方法。

import requests
from pytesser import *
myRequests = requests.Session()
headers = {‘User-agent‘: ‘Mozilla/5.0 (Windows NT 6.1; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0‘}
myRequests.headers.update(headers)

class IMG(object):

    def __init__(self):
        self.codeImg = ‘codes.png‘
        self.iMg = self._openImg(self.codeImg)
        self.Im = self._openImg(self.codeImg.capitalize())
        self.w,self.h = self.Im.size
        self.cookies = 

    def _openImg(self,name):
        try:
            im = Image.open(name)
            return im
        except:
            print ‘[!] Open %s failed‘ % name
            exit()
            
    def Pytess(self,name):
        threshold = 140
        table = []

        for i in range(256):
            if i < threshold:
                table.append(0)
            else:
                table.append(1)
                
                
        #识别到一些其他字符,进行转换
        rep = {‘O‘:‘0‘,
            ‘I‘:‘1‘,
            ‘L‘:‘1‘,
            ‘Z‘:‘2‘,
            ‘S‘:‘8‘,
            ‘Q‘:‘0‘,
            ‘}‘:‘7‘,
            ‘*‘:‘‘,
            ‘E‘:‘6‘,
            ‘]‘:‘0‘,
            ‘`‘:‘‘,
            ‘B‘:‘8‘,
            ‘\\‘:‘‘,
            ‘ ‘:‘‘
        }

        data = self._openImg(name)

 
        try:
            text = image_to_string(data)
            text = text.strip()
            text = text.upper()
        except :
            text = 0

        for r in rep:

            text = text.replace(r,rep[r])

        return text

上面代码运行后,无法有效正确的识别,识别率非常非常低。那问题出在哪呢?

仔细观察验证码图片,发现上面带了一些其他颜色的小斑点,而且还有一些不规则的干扰线:

一看到这个头就大了,能力有限,没有图像处理的经验,于是想到了个笨办法:能不能对每个像素点进行操作?一想到这,立马google起来,发现PIL就是可以处理图片像素点的!!

三、优化图片

首先对PIL转换成黑白模式,将图片转换成简单的黑白两种颜色:

        data = self._openImg(name)
        imgry = data.convert(‘L‘)
        out = imgry.point(table,‘1‘)

处理完像是这样:

剩下的是干扰线问题了,通过观察干扰线,发现干扰线有一定规律:

1)横线干扰线是由并排像素点组成的,上下均是空白。

2)竖线干扰线也是由并排像素点组成,左右均是空白。

3)单点及斜线干扰线,上下左右像素点为空白。

    def pIx(self):
        data = self.Im
        #图片的长宽
        w = self.w
        h = self.h
        
        #data.getpixel((x,y))获取目标像素点颜色。
        #data.putpixel((x,y),255)更改像素点颜色,255代表颜色。
        
        
        try:
            for x in xrange(1,w-1):
                if x > 1 and x != w-2:
                    #获取目标像素点左右位置
                    left = x - 1
                    right = x + 1

                for y in xrange(1,h-1):
                    #获取目标像素点上下位置
                    up = y - 1
                    down = y + 1

                    if x <= 2 or x >= (w - 2):
                        data.putpixel((x,y),255)

                    elif y <= 2 or y >= (h - 2):
                        data.putpixel((x,y),255)

                    elif data.getpixel((x,y)) == 0:
                        if y > 1 and y != h-1:
                        
                            #以目标像素点为中心点,获取周围像素点颜色
                            #0为黑色,255为白色
                            up_color = data.getpixel((x,up))
                            down_color = data.getpixel((x,down))
                            left_color = data.getpixel((left,y))
                            left_down_color = data.getpixel((left,down))
                            right_color = data.getpixel((right,y))
                            right_up_color = data.getpixel((right,up))
                            right_down_color = data.getpixel((right,down))
                            
                            #去除竖线干扰线
                            if down_color == 0:
                                if left_color == 255 and left_down_color == 255 and                                     right_color == 255 and right_down_color == 255:
                                    data.putpixel((x,y),255)
                                    
                            #去除横线干扰线
                            elif right_color == 0:
                                if down_color == 255 and right_down_color == 255 and                                     up_color == 255 and right_up_color == 255:
                                    data.putpixel((x,y),255)
 

                        #去除斜线干扰线
                        if left_color == 255 and right_color == 255                                 and up_color == 255 and down_color == 255:
                            data.putpixel((x,y),255)
                    else:
                        pass
                        
                    #保存去除干扰线后的图片
                    data.save("test.png","png")
        except:
            return False

因为循环结构是从左到右 从上到下的顺序去循环,所以要匹配的时候不需要匹配左上角位置像素点。

效果:

黑白模式:

去边以及处理线段干扰线:

去除斜线干扰线:

处理完的图片再识别一下,完美!从原来不到10%的识别率,到现在已经能够达到80%!

四、抓取所要的信息

将抓取到的数据用bs4 + 正则去匹配,使用邮箱发送抓取到的信息。

完成这个需求所有代码看起来是这样:

#-*- coding:utf-8 -*-
import requests,os,re,smtplib,time
from pytesser import *
from bs4 import BeautifulSoup
from email.mime.text import MIMEText
from email.MIMEMultipart import MIMEMultipart

myRequests = requests.Session()
headers = {‘User-agent‘: ‘Mozilla/5.0 (Windows NT 6.1; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0‘}
myRequests.headers.update(headers)

webSite = ‘‘
webUser = ‘‘
webPass = ‘‘

CodesImg = os.path.join(os.getcwd(), ‘codes.png‘)

def _transcoding(data):
    if not data:
        return data
    result = None
    if type(data) == unicode:
        result = data
    elif type(data) == str:
        result = data.decode(‘utf-8‘)
    return result

    
sender = _transcoding(‘电信短信平台余额‘)
receiver = [‘‘]
#receiver = [‘‘]
subject = ‘电信短信平台余额‘
smtpserver = ‘‘
username = ‘‘
password = ‘‘

def send_mail(balance):
    msg = MIMEMultipart(‘alternative‘)
    msg[‘Subject‘] = subject
    html = """
    <h4>短信剩余余额:</h4>

    <h2><b>%s</b> ¥</h2>
    """ % balance
    part = MIMEText(html,‘html‘,‘utf-8‘)
    msg.attach(part)

    smtp = smtplib.SMTP()
    smtp.connect(‘smtp.exmail.qq.com‘)
    smtp.login(username,password)
    smtp.sendmail(sender,receiver,msg.as_string())
    smtp.quit()

class IMG(object):

    def __init__(self):
        self.codeImg = ‘codes.png‘
        self.iMg = self._openImg(self.codeImg)
        self.Im = self._openImg(self.codeImg.capitalize())
        self.w,self.h = self.Im.size
        self.cookies = ‘‘

    def _bs4(self,soup):
        list = []
        Soup = BeautifulSoup(soup,"html.parser")
        for i in Soup.find_all(‘td‘):
             list.append(i)
        return list

    def _openImg(self,name):
        try:
            im = Image.open(name)
            return im
        except:
            print ‘[!] Open %s failed‘ % name
            exit()

    def _processImg(self,name):
        threshold = 140
        table = []
        for i in range(256):
            if i < threshold:
                table.append(0)
            else:
                table.append(1)
        img = self._openImg(name)
        imgry = img.convert(‘L‘)
        out = imgry.point(table,‘1‘)
        filename = self.codeImg.capitalize()
        out.save(filename)

    def getCodes(self):
        self.Cookies =[]
        url = "%s/verifyCode.jsp" % webSite
        r = myRequests.get(url=url)
        if r.cookies:
            self.Cookies = str(r.cookies).split(‘ ‘)[1]
        f = open(CodesImg,‘wb‘)
        f.write(r.content)
        f.close()

    def pIx(self):
        data = self.Im
        w = self.w
        h = self.h
        try:
            for x in xrange(1,w-1):
                if x > 1 and x != w-2:
                    left = x - 1
                    right = x + 1

                for y in xrange(1,h-1):
                    up = y - 1
                    down = y + 1

                    if x <= 2 or x >= (w - 2):
                        data.putpixel((x,y),255)

                    elif y <= 2 or y >= (h - 2):
                        data.putpixel((x,y),255)

                    elif data.getpixel((x,y)) == 0:
                        if y > 1 and y != h-1:
                            up_color = data.getpixel((x,up))
                            down_color = data.getpixel((x,down))
                            left_color = data.getpixel((left,y))
                            left_down_color = data.getpixel((left,down))
                            right_color = data.getpixel((right,y))
                            right_up_color = data.getpixel((right,up))
                            right_down_color = data.getpixel((right,down))

                            if down_color == 0:
                                if left_color == 255 and left_down_color == 255 and                                     right_color == 255 and right_down_color == 255:
                                    data.putpixel((x,y),255)
                                    data.save("text2.png","png")

                            elif right_color == 0:
                                if down_color == 255 and right_down_color == 255 and                                     up_color == 255 and right_up_color == 255:
                                    data.putpixel((x,y),255)
                                    data.save("text3.png","png")

                        if left_color == 255 and right_color == 255                                 and up_color == 255 and down_color == 255:
                            data.putpixel((x,y),255)
                    else:
                        pass
                    data.save("test.png","png")
        except:
            return False

    def Pytess(self,name):
        threshold = 140
        table = []

        for i in range(256):
            if i < threshold:
                table.append(0)
            else:
                table.append(1)

        rep = {‘O‘:‘0‘,
            ‘I‘:‘1‘,
            ‘L‘:‘1‘,
            ‘Z‘:‘2‘,
            ‘S‘:‘8‘,
            ‘Q‘:‘0‘,
            ‘}‘:‘7‘,
            ‘*‘:‘‘,
            ‘E‘:‘6‘,
            ‘]‘:‘0‘,
            ‘`‘:‘‘,
            ‘B‘:‘8‘,
            ‘\\‘:‘‘,
            ‘ ‘:‘‘
        }

        data = self._openImg(name)
        imgry = data.convert(‘L‘)
        out = imgry.point(table,‘1‘)
        try:
            text = image_to_string(out)
            text = text.strip()
            text = text.upper()
        except :
            text = 0

        for r in rep:

            text = text.replace(r,rep[r])

        return text

    def loginSite(self,loginname,passwd,randnum,cookies):
        url = ‘‘
        params = {
            ‘loginname‘:loginname,
            ‘password‘:passwd,
            ‘randnum‘:randnum,
          #  ‘returnUrl‘:‘/admin/index/index.action‘
        }
        r = myRequests.post(url=url,data=params)
        r.encoding = ‘utf-8‘
        loginUrl = ‘‘
        r2 = myRequests.get(url=loginUrl)
        html = r2.text.encode(‘utf-8‘)
        return html

if __name__ == ‘__main__‘:
    i = 0
    while True:
        time.sleep(5)

        i += 1
        print ("[!]第%d次尝试发送"%i)

        I = IMG()

        #获取验证码
        I.getCodes()

        #验证码图片处理
        I._processImg(I.codeImg)

        #去除干扰线
        I.pIx()

        #获取验证码
        codes = I.Pytess(‘test.png‘)

        #cookies
        cookies = I.cookies

        #登陆
        htmlSoup = I.loginSite(webUser,webPass,codes,cookies)

        List = I._bs4(htmlSoup)

        if List:
            st = List[3]
            text = re.search("\d{1,}",str(st))
            Balance = text.group(0)
            print (‘[!]短信余额为:%s,正在发送!‘ % Balance)
            send_mail(Balance)
            print (‘[!]发送成功,正在退出程序...‘)
            time.sleep(2)
            exit()

由于我的是在windows下环境写的,遂,直接放到windows下运行,写个bat脚本,在放到任务计划上执行,OK!搞定!

时间: 2024-10-26 13:16:05

[记录]Python爬虫过程中遇到的简单带干扰线验证码处理方法的相关文章

Python 爬虫过程中的中文乱码问题

python+mongodb 在爬虫的过程中,抓到一个中文字段,encode和decode都无法正确显示 注:以下print均是在mongodb中截图显示的,在pythonshell中可能会有所不同 比如中文 “余年”,假设其为变量a 1. print a 结果如下: 使用type查询之后,显示的确是unicode编码(正常情况下讲unicode编码内容直接存入mongodb中是可以正常显示的) 2. print type(a) 结果如下: 3. print a.encode('utf-8')

python爬虫scrapy框架——人工识别登录知乎倒立文字验证码和数字英文验证码(2)

操作环境:python3 在上一文中python爬虫scrapy框架--人工识别登录知乎倒立文字验证码和数字英文验证码(1)我们已经介绍了用Requests库来登录知乎,本文如果看不懂可以先看之前的文章便于理解 本文将介绍如何用scrapy来登录知乎. 不多说,直接上代码: import scrapy import re import json class ZhihuSpider(scrapy.Spider): name = 'zhihu' allowed_domains = ['www.zhi

spring加载过程中jar包加载不了,解决方法

当我们在开发spring项目时,一般会将jar包放到webInf/lib下,这样是myeclipse自动将jar包加载到tomcat中webapps下,但是当我们新建一个lib文件夹的情况下,我们add building Path时就会出错,这时候我们有个技巧供使用. 1.项目上点击右键搜索de,找到deployment assembly 目的就是将此处添加的jar包添加到系统webINF/lib路径下 来自为知笔记(Wiz) spring加载过程中jar包加载不了,解决方法

北京Python筛选过程中应注意什么

计算机初级爱好者普遍喜欢Python,因为Python干净利索,简单直接.它编写代码的速度非常的快,而且非常注重代码的可读性,非常适合多人参与的项目.很多人选择了培训,那么北京Python培训筛选过程中应注意什么? 第一点:虽然不靠谱的机构很多,但不能说没有.还是那句话,主要还是得看自己,不努力,在哪都学不会.无论是学哪门语言,基础知识非常重要.因此,找有丰富编程经验的老师带着你会少走很多弯路,你的进步速度也会快很多. 第二点:很多培训机构,无论是英语.IT.金融等行业,都是为就业提高自己的职业

爬虫过程中如何有效的应对IP限制?

大数据时代,营销推广的主要依据就是大数据:根据大数据去抓取用户习惯,去抓取竞争对手的信息,却或许同类产品的相关资料等等.数据采集推动着数据分析,数据分析推动发展.但是在这个过程中会出现很多问题.拿最简单最基础的爬虫采集数据为例,过程中就会面临,IP被封,爬取受限.违法操作等多种问题,所以在爬去数据之前,一定要了解好预爬网站是否涉及违法操作,找到合适的代理IP访问网站等一系列问题. 我们都知道如果一个固定的IP在短暂的时间内,快速大量的访问一个网站,那自然会引起注意,管理员可以通过一些手段把这个I

安装python caffe过程中遇到的一些问题以及对应的解决方案

关于系统环境: Ubuntu 16.04 LTS cuda 8.0 cudnn 6.5 Anaconda3 编译pycaffe之前需要配置文件Makefile.config 1 ## Refer to http://caffe.berkeleyvision.org/installation.html 2 # Contributions simplifying and improving our build system are welcome! 3 4 # cuDNN acceleration

python安装过程中一些小知识点

1.做些软链接和virtualenv的基本使用: ln -s /data/linkdood/im/vrv/python36/bin/python3.6 /usr/bin/python3 ln -s /data/linkdood/im/vrv/python36/bin/pip3.6 /usr/bin/pip ln -s /data/linkdood/im/vrv/python36/bin/virtualenv /usr/bin/virtualenv virtualenv /data/my_env1

设计网路爬虫过程中需要注意的解析问题

现在爬虫工作者越来越多,那么今天就讲讲就从解析数据和模拟器好好说说爬虫. 原本的称呼就是应该是叫解析网页,但是目前移动数据已经成为日常生活中不可或缺的数据走向,所以解析数据这个词来形容 会更加精准,解析数据.解析数据就是说当我们访问一个网址的时候,服务器就该网站把内容反馈给了我,我应该如何的把我 真正需要的数据提取出来.当服务器返回给我们的是html的时候,我需要提取到具体哪个 DIV 下面的内容;当服务器返回给我 的是 XML 时,我也需要提取某个标签下面的内容. 我们采用的最原始的方式就是使

Python学习 过程中零散知识点的总结

自学资料比较零碎,本文是对在Python学习过程中积累的零零散散的知识点的总结 ====================================================================== 1.  关于编码的简单介绍 unicode  --   万国码 utf-8     --  数字.字符用8位,欧洲字符16位来存,中文3个字节24位 unicode  -->  utf-8   编码 encode utf-8    -->  unicode  解码 decod