爬虫之验证码处理
验证码处理
一、 字符验证码
通过某个程序,计算机产生一个字符串,一般四位,包含数字、字母、中文
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() # 点击提交按钮
本文来自博客园,作者:Kenny_LZK,转载请注明原文链接:https://www.cnblogs.com/liuzhongkun/p/16194016.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)