爬虫之验证码处理

验证码处理

一、 字符验证码

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

1、 难点

  1. 噪点
  2. 干扰线
  3. 重叠
  4. 颜色
  5. 变形

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

2、 图像处理

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

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

基础知识

  1. RGB通道

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

  2. 像素

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

  3. 图像分辨率

    图像分辨率通常是指每英寸中像素的个数,用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、 难点

  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、 问题

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

2、 解决方案

  1. 人工识别

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

  2. 机器学习

    • 数学学得好
    • 数据分析、机器学习算法、神经网络
    • 海量的数据集
  3. 第三方工具

    这里我们选择超级鹰

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() # 点击提交按钮
posted @   Kenny_LZK  阅读(222)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示