当前位置:AIGC资讯 > 数据采集 > 正文

爬虫之验证码处理

文章目录

验证码处理 一、 字符验证码 1、 难点 2、 图像处理 3、 实例代码 二、 滑块验证码 1、 难点 2、 实现示例 三、 点触验证码 1、 问题 2、 解决方案 3、 使用案例

验证码处理

一、 字符验证码

通过某个程序,计算机产生一个字符串,一般四位,包含数字、字母、中文

1、 难点

噪点 干扰线 重叠 颜色 变形

经过这么一些的操作之后,程序会生成一张图片,而我们要做的就是输入和图片里面的文本信息一致,才算通过校验

2、 图像处理

在数字世界中,有色彩模式这一算法,来表示各种颜色

比较常见的有RGB模式,HSL模式等

基础知识

RGB通道

一张彩色图片,包含着R、G、B三个通道,每一个通道用(0, 255)来表示,越接近0,表示该通道的色彩越淡

像素

像素是图形单元的 简称,它是位图最小的完整单位。像素具有两种属性:一种是相对于位图图像中的其他像素来说,一个像素具有一个特定的位置;另一种是具有可以用位来度量的颜色深度

图像分辨率

图像分辨率通常是指每英寸中像素的个数,用ppi表示。图像分辨率取决于显示图像的大小。分辨率与图像清晰程度成正比。分辨率越高,图像越清晰,当然产生的图形图像文件越大,在图形处理是所需计算机的内存较多,同时CPU处理的时间也就越长

使用PIllow库进行图像处理

3、 实例代码

识别古诗文网的图形验证码:https://so.gushiwen.cn/RandCode.ashx

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: A.L.Kun
# @file : demo01.py
# @time : 2022/4/25 15:22
from requests import get
from PIL import Image
import pytesser3  # 注意,要安装tesseract
from io import BytesIO  # 内存IO,将数据写入到内存中去,不会产生文件

img_url = "https://so.gushiwen.cn/RandCode.ashx"  # 生成验证码的网址
img_data = BytesIO(get(img_url).content)  # 里面传入二进制数据
img = Image.open(img_data)  # 打开验证码图片
img = img.convert("L")  # 将图片转换为灰度图
# 获取阈值阈值可以通过数组来获取,这里默认方便,默认阈值为175
avg = 175
# 二值化处理
w, h = img.size  # 获取图片的大小
pixes = img.load()  # 获取照片的像素
for x in range(w):
    for y in range(h):
        if pixes[x, y] < avg:
            pixes[x, y] = 0
        else:
            pixes[x, y] = 255
# 去除噪点
for x in range(1, w - 1):
    for y in range(1, h - 1):
        count = 0  # 统计周边摆设像素的个数
        if pixes[x, y - 1] > 245:  # 如果上面的像素的颜色为白色
            count += 1
        if pixes[x, y + 1] > 245:  # 如果下面的像素的颜色白色
            count += 1
        if pixes[x - 1, y] > 245:  # 如果左边的像素的颜色为白色
            count += 1
        if pixes[x + 1, y] > 245:  # 如果右边的像素的颜色为白色
            count += 1
        if pixes[x - 1, y - 1] > 245:  # 如果左上面的像素的颜色为白色
            count += 1
        if pixes[x - 1, y + 1] > 245:  # 如果左下面的像素的颜色为白色
            count += 1
        if pixes[x - 1, y + 1] > 245:  # 如果右下边的像素的颜色为白色
            count += 1
        if pixes[x + 1, y - 1] > 245:  # 如果右上边的像素的颜色为白色
            count += 1
        if count > 5:
            pixes[x, y] = 255  # 如果有超过5个像素点的颜色为白色,说明很可能是一个噪点

print(pytesser3.image_to_string(img))  # 使用该方法,将图片内容转换为文字
img.show()

注意,如果使用scrapy请求图片数据,通过response.body来获取图片的二进制数据

二、 滑块验证码

通过滑动图片进行拼图进行验证,可以通过selenium来实现

1、 难点

精灵图:

CSS Sprites,CSS精灵图

所谓精灵图就是把好多小的图片合并一张大图,以该大图的元素为背景图片的元素尺寸比较小,可以通过控制背景图片的位置,让元素上显示合适的图像

2、 实现示例

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: A.L.Kun
# @file : demo02.py
# @time : 2022/4/25 19:59
import re
from io import BytesIO
from PIL import Image
from requests_html import HTMLSession
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait  # 显性等待
from selenium.webdriver.support import expected_conditions as ec  # 提供预期判断的方法,指定等待元素
from selenium.webdriver.common.action_chains import ActionChains  # 导入动作链


def get_img(web, div_class):
    """获取素材图,以及每个div的前景图坐标"""
    imgs = web.find_elements(By.CLASS_NAME, div_class)  # 得到存放图片的标签
    img_url = None
    pos_lis = []
    for img in imgs:
        # 拿到每一个图片元素
        # print(img.get_attribute("style"))
        img_info = {}  # 每一张图片具体的坐标
        img_ = re.findall(r'background-image: url\("(.*?)"\); background-position: (.*?)px (.*?)px;',
                          img.get_attribute("style"))  # 匹配到图片和每个图片的偏移量
        # 元组拆包
        img_url, img_info['x'], img_info['y'] = img_[0]
        # 将坐标转换为整型
        img_info["x"], img_info['y'] = int(img_info["x"]), int(img_info['y'])
        pos_lis.append(img_info)

    # 抓取素材图片
    # print(img_url, pos_lis)
    session = HTMLSession()
    resp = session.get(url=img_url).content
    img = BytesIO(resp)
    img = Image.open(img)
    # img.show()
    return img, pos_lis  # 返回图片和每个精灵图的偏移量


def get_merge_image(img_, pos_lis):
    """剪裁并拼接图片"""
    pos_lis_upper = []  # 存储上部坐标信息
    pos_lis_lower = []  # 存储下部坐标信息
    """
    我们是通过pos_lis,把每一张图片剪裁下来,每一张图片,宽10高58
    得到第一张图片之后,我们让第二张图片在 x+10,y + 58 的位置,进行拼接
    """
    # 遍历每一个坐标点
    for pos in pos_lis:
        # 如果是上部坐标
        img_c = img_.crop(((abs(pos["x"])),
                           (abs(pos["y"])),
                           (abs(pos["x"]) + 10),
                           (abs(pos["y"]) + 58)
                           ))  # 对图片进行裁剪
        # img_c.show()
        # break
        if pos["y"] == -58:
            pos_lis_upper.append(img_c)
        else:
            pos_lis_lower.append(img_c)
    # 拼接图片,粘贴图片
    new_img = Image.new("RGB", (260, 116))  # 创建一个画布
    x_offset = 0  # 图片从原点进行编辑
    for i in range(len(pos_lis_upper)):
        new_img.paste(pos_lis_upper[i], (x_offset, 0))  # 上边的依次从左到右进行粘贴
        new_img.paste(pos_lis_lower[i], (x_offset, 58))  # 下边的依次从左到右进行粘贴
        x_offset += 10  # 图片的宽度为10
    new_img.show()
    return new_img


def is_similar(img_full, img_new, x, y):
    """判断两张图片是否相等,x,y 是指定点的坐标,使用对比像素"""
    full_pix = img_full.getpixel((x, y))  # 获取完整图片的像素
    new_pix = img_new.getpixel((x, y))  # 获取切口图片的像素
    # 遍历颜色通道
    for i in range(0, 3):
        if abs(full_pix[i] - new_pix[i]) >= 50:
            # 如果其中一个的颜色通道的值相差超过50
            return False
    return True


def get_diff_location(img_full, img_new):
    """比较图片区域"""
    for x in range(1, 259):
        for y in range(1, 115):
            if not is_similar(img_full, img_new, x, y):
                # 两个像素点的颜色不一致
                return x


if __name__ == '__main__':
    web = webdriver.Chrome()
    web.maximize_window()  # 窗口最大化
    web.get("http://www.cnbaowen.net/api/geetest/")
    # 等待滑块加载完再 开始获取图片
    get_knob_btn = WebDriverWait(web, 10).until(ec.element_to_be_clickable((By.CLASS_NAME, "gt_slider_knob")))  # 同时获取到该元素
    # 获取完整图片
    # 获取图片以及坐标
    img, pos_lis = get_img(web, "gt_cut_fullbg_slice")
    # 拼接图片
    img_full = get_merge_image(img, pos_lis)

    # 获取缺口图片
    # 获取图片及坐标
    img, pos_lis = get_img(web, "gt_cut_bg_slice")
    # 拼接图片
    img_new = get_merge_image(img, pos_lis)

    x = get_diff_location(img_full, img_new)  # 获取缺口左上角的坐标
    # 创建动作链
    action_chain = ActionChains(web)
    action_chain.click_and_hold(get_knob_btn)  # 点击并按住
    action_chain.pause(0.2)  # 停顿0.2秒
    action_chain.move_by_offset(x - 10, 0)  # 快到缺口的时候,停顿0.6秒
    action_chain.pause(0.6)
    action_chain.move_by_offset(10, 0)  # 移动到缺口处
    action_chain.release(get_knob_btn)  # 到达位置后松开
    action_chain.perform()  # 执行动作

三、 点触验证码

给定一组文字,给定一张照片,按照文字顺序点击图片的文字,来完成验证码

1、 问题

颜色不一致 字体镂空 背景图片干扰大

2、 解决方案

人工识别

通过sleep,手动点击(成功率较高)

机器学习

数学学得好 数据分析、机器学习算法、神经网络 海量的数据集

第三方工具

这里我们选择超级鹰

3、 使用案例

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
# @author: A.L.Kun
# @file : demo03.py
# @time : 2022/4/26 9:00
import time

from PIL import Image
from chaojiying import Chaojiying_Client
from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as ec
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.action_chains import ActionChains

# 获取超级鹰的账户信息
username = input("请输入用户名:")
pwd = input("请输入密码:")
id = input("软件id:")
# 传入用户名,密码,软件id
chaojiying = Chaojiying_Client(username, pwd, id)  # 实例化超级鹰账户对象
url = "https://zc.yy.com/reg/udb/reg4udb.do?mode=udb&type=Mobile&appid=1&foreignMb=1&action=3&busiurl=https%3A%2F%2Faq.yy.com&fromadv=lgn&reqDomainList="
driver = Chrome()  # 创建一个驱动
driver.get(url)  # 访问url
driver.maximize_window()  # 使窗口最大化
element = WebDriverWait(driver, 20).until(ec.element_to_be_clickable((By.CLASS_NAME, "pw_main")))  # 显性等待
element.screenshot("pw_main.png")  # 对element元素进行截图
img = open("pw_main.png", "rb").read()  # 读取图片的字节数据
ret = chaojiying.PostPic(img, 9004)  # 获取图片识别结果
pos = [(int(j), int(k)) for j, k in
       [i.split(",") for i in ret.get("pic_str").split("|")]]  # 将数据转换为一个一个的坐标,同时使用get方法防止报错
for i in pos:
    # 根据返回的坐标,去执行点击事件
    ActionChains(driver).move_to_element_with_offset(element, i[0], i[1]).click().perform()  # 点击对应点的坐标
    
driver.find_element(By.CLASS_NAME, "pw_submit").click()  # 点击提交按钮

更新时间 2023-11-08