滑动拼图验证码思路

1. 最简单的的调用打码平台,收费

2. 还有一种利用华为云物体检测接口,可以自己上传图片去训练模型,模型训练好后,部署上线,就可以直接用了,收费,目测一次0.27元,也没细算,有提供接口服务,重要可以自己提供数据训练模型

3. 获取到一张完整的图片和带缺口的图片,利用PIL模块,对比两张图的像素点,计算出距离,然后selenium滑动,免费,比如:

以下代码为虎嗅 登陆

代码为18年7月的,已失效,只提供思路

# coding=utf-8
from selenium import webdriver
from selenium.webdriver.common.proxy import Proxy
from selenium.webdriver.common.proxy import ProxyType
from selenium.webdriver.support.wait import WebDriverWait
# from selenium.webdriver.support.ui import WebDriverWait
# from selenium.common.exceptions import NoAlertPresentException,TimeoutException,NoSuchElementException
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains
from PIL import ImageGrab, Image
import time, random, re, json, requests, os, sys
sys.path.append('C:/Users/Administrator/Desktop/tian_yan_cha/img_huxiu')


class Hu_Xiu:
    def __init__(self):
        chromedriver = 'C:/Users/Administrator/AppData/Local/Programs/Python/Python35/Scripts/chromedriver'
        chromeOptions = webdriver.ChromeOptions()
        chromeOptions.add_argument("--proxy-server = http://115.198.32.22:6666")
        self.driver = webdriver.Chrome(chromedriver, chrome_options=chromeOptions)    # chromedriver, chrome_options=chromeOptions
        self.driver.maximize_window()
        # self.driver = webdriver.Firefox()

        self.header = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36'
        }

    def visit_web(self):
        # 访问网站, 出现拼图按钮, 鼠标移动到拼图按钮上并执行, 刷新 极验 拼图
        print('----------- 访问网站 ----------')
        self.driver.get("https://www.huxiu.com/")
        time.sleep(random.uniform(10, 15))
        # 等待页面的上元素刷新出来    '''判断某个元素中是否可见并且是enable的,代表可点击'''   注册 按钮
        WebDriverWait(self.driver, 10, 0.5).until(EC.element_to_be_clickable((By.XPATH, '//*[@class="js-register"]')))
        reg_element = self.driver.find_element_by_xpath('//*[@class="js-register"]')
        reg_element.click()
        WebDriverWait(self.driver, 10, 0.5).until(EC.element_to_be_clickable((By.XPATH, '//div[@class="gt_slider_knob gt_show"]')))

    def visit_web_show(self):
        # 鼠标移动到拖动按钮,显示出拖动图片
        element = self.driver.find_element_by_xpath('//div[@class="gt_slider_knob gt_show"]')
        ActionChains(self.driver).move_to_element(element).perform()
        time.sleep(random.uniform(3, 5))

        # 刷新一下极验图片
        element = self.driver.find_element_by_xpath('//a[@class="gt_refresh_button"]')
        element.click()
        time.sleep(random.uniform(3, 5))
        # print(self.driver.page_source)

    def get_merge_img(self, image_name, location_list):
        # ---------》  重新合并 还原 图片  《----------
        print('---------》  重新合并 还原 图片  《----------')
        print(location_list)
        # 打开图片
        img = Image.open(image_name)
        # 创建新的图片,大小为260*116
        new_im = Image.new('RGB', (260, img.height))

        im_list_up = []
        im_list_down = []
        for location in location_list:
            # y值== -58 的图片属于上半部分,高度58
            if location['y'] == -58:
                im_list_up.append(img.crop((abs(location['x']), 58, abs(location['x']) + 10, img.height)))
            # 下边图片
            if location['y'] == 0:
                im_list_down.append(img.crop((abs(location['x']), 0, abs(location['x']) + 10, 58)))
        # 黏贴图片
        x_offset = 0
        for im in im_list_up:
            new_im.paste(im, (x_offset, 0))
            x_offset += im.size[0]
        x_offset = 0
        for im in im_list_down:
            new_im.paste(im, (x_offset, 58))
            x_offset += im.size[0]
        # new_im.show()
        return new_im

    def get_img(self, xpath_rules):
        # 保存所有图片
        print('---------- 保存所有图片 ----------')
        images_list = self.driver.find_elements_by_xpath(xpath_rules)
        location_list = []
        for image_list in images_list:
            location = {}
            # 在html里面解析出小图片的url地址,还有长高的数值
            location['x'] = int(re.findall("background-image: url\(\"(.*?)\"\); background-position: (.*?)px (.*?)px;",image_list.get_attribute('style'))[0][1])
            location['y'] = int(re.findall("background-image: url\(\"(.*?)\"\); background-position: (.*?)px (.*?)px;",image_list.get_attribute('style'))[0][2])
            location_list.append(location)
            imageurl = re.findall("background-image: url\(\"(.*?)\"\); background-position: (.*?)px (.*?)px;",image_list.get_attribute('style'))[0][0]
        # 替换图片的后缀,获得图片的URL
        image_url = imageurl.replace('webp', 'jpg')
        print(image_url)
        # 获取图片名字
        image_name = './img_huxiu/' + image_url.split('/')[-1]
        # 下载图片  保存
        res = requests.get(url=image_url, headers=self.header)
        with open(image_name, 'wb') as f:
            f.write(res.content)
        # ---------》  重新合并 还原 图片  《----------
        ima = self.get_merge_img(image_name, location_list)
        return ima, image_name

    def is_similar_color(self, img1, img2, i, j):
        # 对比颜色 RGB值
        # 获取指定位置的RGB值
        # print('------------ 对比颜色 RGB值 -------------')
        pixel01 = img1.getpixel((i, j))     # 像素值 pixel01 --  (31, 160, 80)
        pixel02 = img2.getpixel((i, j))     # 像素值 pixel02 --  (31, 160, 80)
        # print('pixel01 -- ', pixel01)
        # print('pixel02 -- ', pixel02)

        for k in range(0, len(pixel01)):
            # 如果相差超过50则就认为找到了缺口的位置
            if abs(pixel01[k] - pixel02[k]) >= 30:
                return False
        return True

    def get_offset_destance(self, img1, img2):
        # 计算缺口距离
        # 两张原始图的大小都是相同的260*116
        # 那就通过两个for循环依次对比每个像素点的RGB值
        # 如果相差超过50则就认为找到了缺口的位置
        print('--------------- 计算缺口距离 --------------')
        for i in range(0, img1.width):
            for j in range(0, img1.height):
                # 判断颜色是否相近
                if self.is_similar_color(img1, img2, i, j) == False:
                    img3 = img1.crop((i, j, i + 50, j + 40))
                    img3.save('./img_huxiu/gap.jpg')
                    print('=== 缺口距离 i == ', str(i))
                    return i

    def get_track(self, loc):
        # 根据缺口的位置模拟x轴移动的轨迹
        print('--------------- 根据缺口的位置模拟x轴移动的轨迹 --------------')
        # 前 4/5  加速运动, 后 1/5 减速运动
        '''
        速度公式: v = v0 + at   v0 初速度  a 加速度值   t 时间    v 末速度
        位移公式: s = v0*t + 1/2 * a*t*t    
        '''
        v = 0    # 初速度
        t = 0.2  # 时间
        s0 = 0    # 当前的位移
        mid = 4/5*loc
        print('mid= %s' % mid)
        move_list = []    #其中一个元素 代表 在0.2s时间内的位移距离
        while s0 < loc:
            if s0 < mid:
                # 加速运动
                a = 2
            else:
                # 减速运动
                a = -4
            v0 = v
            # 0.2s 内的位移距离
            s = v0*t + 0.5*a*t*t
            # 当前位置
            s0 += s
            # print(s)
            # print('s0= ', s0)
            move_list.append(round(s))
            # 速度已经达到v,该速度作为下次的初速度
            v = v0 + a * t
        print('s0= ', s0)
        print('move_list= %s' % move_list)
        # move_list= [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2]
        return move_list

    def sliding(self, m_list):
        # 点击 滑动按钮 进行 滑动 验证
        print('--------------- 点击 滑动按钮 进行 滑动 验证 --------------')
        # 获取元素坐标
        elment = self.driver.find_element_by_xpath('//div[@class="gt_slider_knob gt_show"]')
        location = elment.location
        print('刷新按钮图片大小 = ', elment.size)    # {'height': 44, 'width': 44}
        # 获取圆球高度
        y = location['y']
        print('y = ', y)
        # 点击元素不放
        ActionChains(self.driver).click_and_hold(on_element=elment).perform()
        time.sleep(random.uniform(0.2, 0.7))
        # 拖动元素
        track_string = ''
        for track in m_list:
            # xoffset=track+22:这里的移动位置的值是相对于滑动圆球左上角的相对值,而轨迹变量里的是圆球的中心点,所以要加上圆球长度的一半。
            # yoffset=y-445:这里也是一样的。不过要注意的是不同的浏览器渲染出来的结果是不一样的,要保证最终的计算后的值是22,也就是圆球高度的一半
            track_string = track_string + '(%s, %s)' % (track, y - 423)
            ActionChains(self.driver).move_to_element_with_offset(to_element=elment, xoffset=track + 22 , yoffset=y - 445).perform()
            time.sleep(random.uniform(0.03, 0.15))
        print('track_string = ', track_string)
        # xoffset=21,本质就是向后退一格。这里退了5格是因为圆球的位置和滑动条的左边缘有5格的距离
        p = 0
        while p < 5:
            ActionChains(self.driver).move_to_element_with_offset(to_element=elment, xoffset=21, yoffset=y - 445).perform()
            time.sleep(random.uniform(0.1, 0.7))
            p += 1

        # 释放鼠标
        ActionChains(self.driver).release(on_element=elment).perform()
        time.sleep(random.uniform(3, 5))

    def register(self):
        # 验证通过, 输入账号密码
        element = self.driver.find_element_by_xpath('//input[@id="sms_username"]')
        element.clear()
        element.send_keys("手机号")

        ele_captcha = self.driver.find_element_by_xpath('//span[@class="js-btn-captcha btn-captcha"]')
        ele_captcha.click()




    def all_steps(self):
        # 鼠标移动到拼图按钮上并执行, 刷新 极验 拼图
        self.visit_web_show()
        # 保存所有图片
        img1, img1_name = self.get_img('//div[@class="gt_cut_bg_slice"]')
        img2, img2_name = self.get_img('//div[@class="gt_cut_fullbg_slice"]')
        # print('img1 = %s ; img2 = %s' % (img1, img2))
        # 计算距离  x 轴移动距离
        loc = self.get_offset_destance(img1, img2)  # 84

        os.remove(img1_name)
        os.remove(img2_name)
        # 生成 x 轴 移动轨迹
        # loc = 84
        move_list = self.get_track(loc)
        # 点击 滑动按钮 进行 滑动 验证
        self.sliding(move_list)

    def judge_succ(self):
        # 判断是否验证成功
        try:
            WebDriverWait(self.driver, 10, 0.5).until(
                EC.element_to_be_clickable((By.XPATH, '//div[@class="gt_ajax_tip gt_success"]')))
            print('>>>>>>>>>>>>>>>>>>   验证成功    <<<<<<<<<<<<<<<<<<<<<')
            return True
        except:
            print(">>>>>>>>>>>>>>>>>>   验证失败,点击刷新后重试   <<<<<<<<<<<<<<<<<<<<<")
            time.sleep(random.uniform(5, 10))
            return False






    def run(self):
        # 访问网站, 出现拼图按钮,
        self.visit_web()
        # 步骤
        self.all_steps()
        # 判断 验证是否 成功, 不成功的话, 在从头 试一遍
        if self.judge_succ() == True:
            print('>>>>>>>>>>>>>>>>>>   验证成功    <<<<<<<<<<<<<<<<<<<<<')
            # 成功后输入手机号,发送验证码
            self.register()
        else:
            # 失败后递归执行拖动
            self.all_steps()


if __name__ == '__main__':
    # 虎嗅网 爬虫

    hx = Hu_Xiu()
    hx.run()

 

posted @ 2019-10-23 17:21  殇夜00  阅读(19)  评论(0)    收藏  举报