易盾滑动拼图验证码识别
依赖环境
toml
[tool.poetry.dependencies]
python = "^3.8"
Pillow = "^9.1.0"
selenium = "^4.1.3"
requests = "^2.27.1"
代码
python
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
import requests
from detect import get_x_offset
import time
import io
def get_image(url):
headers = {
'authority': 'necaptcha.nosdn.127.net',
'accept': 'image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8',
'accept-language': 'en-US,en;q=0.9',
'referer': 'https://dun.163.com/',
'sec-ch-ua': '" Not A;Brand";v="99", "Chromium";v="100", "Microsoft Edge";v="100"',
'sec-ch-ua-mobile': '?0',
'sec-ch-ua-platform': '"Windows"',
'sec-fetch-dest': 'image',
'sec-fetch-mode': 'no-cors',
'sec-fetch-site': 'cross-site',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Safari/537.36 Edg/100.0.1185.50'
}
return requests.get(url, headers=headers).content
def get_offset(driver):
a1 = driver.find_element(by=By.CSS_SELECTOR, value="img.yidun_bg-img").get_attribute('src')
a2 = driver.find_element(by=By.CSS_SELECTOR, value="img.yidun_jigsaw").get_attribute('src')
offset = get_x_offset(io.BytesIO(get_image(a1)), io.BytesIO(get_image(a2)))
return offset
def run_it(driver, el_yidun_slider):
action = ActionChains(driver)
offset = get_offset(driver)
# print("find offset", offset)
action.drag_and_drop_by_offset(el_yidun_slider, offset+10, 0)
action.perform()
driver = webdriver.Chrome()
driver.get("https://dun.163.com/trial/jigsaw")
el_yidun_slider = driver.find_element(by=By.CSS_SELECTOR, value="div.yidun_slider")
old_x = el_yidun_slider.location['x']
run_it(driver, el_yidun_slider)
for _ in range(5):
time.sleep(1)
if el_yidun_slider.location['x'] == old_x:
run_it(driver, el_yidun_slider)
else:
break
python
from PIL import Image
import os
import traceback
COLOR_TRANSPARENT = (0,0,0,0)
COLOR_GREY = (109,109,105,255)
COLOR_BLACK = (0,0,0,255)
COLOR_WHITE = (255,255,255,255)
COLOR_RED = (255,0,0,255)
COLOR_GREEN = (0,255,0,255)
COLOR_YELLOW = (255,255,0,255)
class ImageHelper:
def __init__(self, im):
self.im = im
self.h = self.im.height
self.w = self.im.width
self.im_data = self.im.getdata()
self.pos_data = self._get_posdata()
def _get_posdata(self):
i = 0
posdata = dict()
for y in range(self.h):
for x in range(self.w):
posdata[(x,y)] = self.im_data[i]
i += 1
return posdata
def is_near_color(self, pos, color):
x, y = pos
for x,y in [(x,y-1), (x,y+1), (x-1,y), (x+1,y)]:
if (x,y) in self.pos_data and self.pos_data[(x,y)] == color:
return True, (x,y)
return False, None
def show_pos_data(posdata):
im = Image.new('RGBA', (60, 158), COLOR_GREY)
for pos,color in posdata.items():
im.putpixel(pos, color)
im.show()
def get_image(posdata):
im = Image.new('RGBA', (60, 158), COLOR_TRANSPARENT)
for pos,color in posdata.items():
im.putpixel(pos, color)
return im
def get_part_outline_posdata(im):
imh = ImageHelper(im)
pos_data = dict()
for pos, color in imh.pos_data.items():
if imh.pos_data[pos] != COLOR_TRANSPARENT:
pos_data[pos] = COLOR_BLACK
return pos_data
def get_bordered_image(im, border_color=COLOR_WHITE, line_color=COLOR_GREEN):
imh = ImageHelper(im)
for pos, color in imh.pos_data.items():
is_near, _ = imh.is_near_color(pos, border_color)
if is_near and (imh.pos_data[pos] == COLOR_BLACK):
im.putpixel(pos, line_color)
return im
def get_line_posdata(im):
pos_data = get_part_outline_posdata(im)
im = get_image(pos_data)
im = get_bordered_image(im, border_color=COLOR_TRANSPARENT, line_color=COLOR_RED)
im = get_bordered_image(im, border_color=COLOR_RED, line_color=COLOR_GREEN)
im = get_bordered_image(im, border_color=COLOR_GREEN, line_color=COLOR_YELLOW)
im = get_bordered_image(im, border_color=COLOR_YELLOW, line_color=COLOR_WHITE)
# im.save("part-1.png")
posdata = dict()
for pos,color in ImageHelper(im).pos_data.items():
if color == COLOR_WHITE:
posdata[pos] = color
return posdata
def grey_img(im, t=195):
table = list(map(lambda i:0 if i < t else 1, range(256)))
return im.convert("RGBA").convert("L").point(table, '1').convert("RGBA")
def get_score(img_grey_helper, img_part_line_posdata, offset_x):
score = 0
for pos,color in img_part_line_posdata:
x, y = pos
new_pos = (x+offset_x,y+1)
if new_pos in img_grey_helper.pos_data:
if (img_grey_helper.pos_data[new_pos] == color):
score += 1
return score
def get_x_offset(image_name, image_part_name):
img_grey_helper = ImageHelper(grey_img(Image.open(image_name)))
img_part_line_posdata = get_line_posdata(Image.open(image_part_name)).items()
max_score_offset = (0,0)
for offset in range(320):
score = get_score(img_grey_helper, img_part_line_posdata, offset)
if score > max_score_offset[1]:
max_score_offset = (offset, score)
return max_score_offset[0]
def merge_save(im1_name, im2_name, c, offset):
im1 = Image.open(im1_name).convert("RGBA")
im2 = Image.open(im2_name)
im3 = im1.copy()
im3.paste(im2, (offset, 1), im2)
im3.save(c)
def merge(a, b, c):
x_offset = get_x_offset(a, b)
print("Merged", a, "and", b, ",Offset", x_offset, ",Output", c)
merge_save(a, b, c, x_offset)
def main():
merge("download.jpg", "download.png", "out.png")
if __name__ == '__main__':
main()