python B站 滑动验证码破解(极验)

代码基本参考:http://cuijiahua.com/blog/2017/11/spider_2_geetest.html

流程:

首先通过网页分析得到网页图片区域

用selenium进入页面,采取图片

简单分析一下,得出缺口位置(应该也没人恶意爬取B站=,=,所以比较简单)

将图片按规律进行拼接(这个得自己看几张,不难,代码中也有体现,但最好自己分析下)

模拟人进行移动,为了避免被认为是机器,采用先快后慢的速度进行移动滑块

完成

 
# -*-coding:utf-8 -*-
import random
import time

from selenium.webdriver import ActionChains
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.common.by import By
from urllib.request import urlretrieve
from selenium import webdriver
from bs4 import BeautifulSoup
import PIL.Image as image
import re

class Crack():
    def __init__(self,username,passwd):
        self.url = 'https://passport.bilibili.com/login'
        self.browser = webdriver.Chrome('chromedriver')
        self.wait = WebDriverWait(self.browser, 100)
        self.BORDER = 6
        self.passwd = passwd
        self.username = username
    def open(self):
        """
        打开浏览器,并输入查询内容
        """
        self.browser.get(self.url)
        keyword = self.wait.until(EC.presence_of_element_located((By.ID, 'login-username')))
        keyword.send_keys('')
        keyword = self.wait.until(EC.presence_of_element_located((By.ID, 'login-passwd')))
        keyword.send_keys('')
        # bowton.click()

    def get_images(self, bg_filename = 'bg.jpg', fullbg_filename = 'fullbg.jpg'):
        """
        获取验证码图片
        :return: 图片的location信息
        """
        bg = []
        fullgb = []
        while bg == [] and fullgb == []:
            bf = BeautifulSoup(self.browser.page_source, 'lxml')
            bg = bf.find_all('div', class_ = 'gt_cut_bg_slice')
            fullgb = bf.find_all('div', class_ = 'gt_cut_fullbg_slice')
        bg_url = re.findall('url\(\"(.*)\"\);', bg[0].get('style'))[0].replace('webp', 'jpg')
        fullgb_url = re.findall('url\(\"(.*)\"\);', fullgb[0].get('style'))[0].replace('webp', 'jpg')
        bg_location_list = []
        fullbg_location_list = []
        for each_bg in bg:
            location = {}
            location['x'] = int(re.findall('background-position: (.*)px (.*)px;',each_bg.get('style'))[0][0])
            location['y'] = int(re.findall('background-position: (.*)px (.*)px;',each_bg.get('style'))[0][1])
            bg_location_list.append(location)
        for each_fullgb in fullgb:
            location = {}
            location['x'] = int(re.findall('background-position: (.*)px (.*)px;',each_fullgb.get('style'))[0][0])
            location['y'] = int(re.findall('background-position: (.*)px (.*)px;',each_fullgb.get('style'))[0][1])
            fullbg_location_list.append(location)

        urlretrieve(url = bg_url, filename = bg_filename)
        print('缺口图片下载完成')
        urlretrieve(url = fullgb_url, filename = fullbg_filename)
        print('背景图片下载完成')
        return bg_location_list, fullbg_location_list

    def get_merge_image(self, filename, location_list):
        """
        根据位置对图片进行合并还原
        :filename:图片
        :location_list:图片位置
        """
        im = image.open(filename)
        new_im = image.new('RGB', (260,116))
        im_list_upper=[]
        im_list_down=[]

        for location in location_list:
            if location['y'] == -58:
                im_list_upper.append(im.crop((abs(location['x']),58,abs(location['x']) + 10, 166)))
            if location['y'] == 0:
                im_list_down.append(im.crop((abs(location['x']),0,abs(location['x']) + 10, 58)))

        new_im = image.new('RGB', (260,116))

        x_offset = 0
        for im in im_list_upper:
            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.save(filename)

        return new_im

    def get_merge_image(self, filename, location_list):
        """
        根据位置对图片进行合并还原
        :filename:图片
        :location_list:图片位置
        """
        im = image.open(filename)
        new_im = image.new('RGB', (260,116))
        im_list_upper=[]
        im_list_down=[]

        for location in location_list:
            if location['y']==-58:
                im_list_upper.append(im.crop((abs(location['x']),58,abs(location['x'])+10,166)))
            if location['y']==0:
                im_list_down.append(im.crop((abs(location['x']),0,abs(location['x'])+10,58)))

        new_im = image.new('RGB', (260,116))

        x_offset = 0
        for im in im_list_upper:
            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.save(filename)

        return new_im

    def is_pixel_equal(self, img1, img2, x, y):
        """
        判断两个像素是否相同
        :param image1: 图片1
        :param image2: 图片2
        :param x: 位置x
        :param y: 位置y
        :return: 像素是否相同
        """
        # 取两个图片的像素点
        pix1 = img1.load()[x, y]
        pix2 = img2.load()[x, y]
        threshold = 60
        if (abs(pix1[0] - pix2[0] < threshold) and abs(pix1[1] - pix2[1] < threshold) and abs(pix1[2] - pix2[2] < threshold)):
            return True
        else:
            return False

    def get_gap(self, img1, img2):
        """
        获取缺口偏移量
        :param img1: 不带缺口图片
        :param img2: 带缺口图片
        :return:
        """
        left = 43
        for i in range(left, img1.size[0]):
            for j in range(img1.size[1]):
                if not self.is_pixel_equal(img1, img2, i, j):
                    left = i
                    return left
        return left

    def get_track(self, distance):
        """
        根据偏移量获取移动轨迹
        :param distance: 偏移量
        :return: 移动轨迹
        """
        # 移动轨迹
        track = []
        # 当前位移
        current = 0
        # 减速阈值
        mid = distance * 4 / 5
        # 计算间隔
        t = 0.2
        # 初速度
        v = 0

        while current < distance:
            if current < mid:
                # 加速度为正2
                a = 2
            else:
                # 加速度为负3
                a = -3
            # 初速度v0
            v0 = v
            # 当前速度v = v0 + at
            v = v0 + a * t
            # 移动距离x = v0t + 1/2 * a * t^2
            move = v0 * t + 1 / 2 * a * t * t
            # 当前位移
            current += move
            # 加入轨迹
            track.append(round(move))
        return track

    def get_slider(self):
        """
        获取滑块
        :return: 滑块对象
        """
        while True:
            try:
                slider = self.browser.find_element_by_xpath("//div[@class='gt_slider_knob gt_show']")
                break
            except:
                time.sleep(0.5)
        return slider

    def move_to_gap(self, slider, track):
        """
        拖动滑块到缺口处
        :param slider: 滑块
        :param track: 轨迹
        :return:
        """
        ActionChains(self.browser).click_and_hold(slider).perform()
        while track:
            x = random.choice(track)
            ActionChains(self.browser).move_by_offset(xoffset=x, yoffset=0).perform()
            track.remove(x)
        time.sleep(0.5)
        ActionChains(self.browser).release().perform()

    def crack(self):
        # 打开浏览器
        self.open()

        # 保存的图片名字
        bg_filename = 'bg.jpg'
        fullbg_filename = 'fullbg.jpg'

        # 获取图片
        bg_location_list, fullbg_location_list = self.get_images(bg_filename, fullbg_filename)

        # 根据位置对图片进行合并还原
        bg_img = self.get_merge_image(bg_filename, bg_location_list)
        fullbg_img = self.get_merge_image(fullbg_filename, fullbg_location_list)

        # 获取缺口位置
        gap = self.get_gap(fullbg_img, bg_img)
        print('缺口位置', gap)

        track = self.get_track(gap-self.BORDER)
        print('滑动滑块')
        print(track)

        # 点按呼出缺口
        slider = self.get_slider()
        # 拖动滑块到缺口处
        self.move_to_gap(slider, track)

if __name__ == '__main__':
    crack = Crack('username','passwd')
    crack.crack()
    print('验证成功')


  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
编写滑动验证码的主要思路是: 1. 在页面中随机生成一个背景图片和一个滑块图片,并将滑块图片的初始位置随机设置。 2. 在浏览器端实现鼠标拖拽事件,将滑块图片拖动到指定位置。 3. 通过比较滑块图片的初始位置和最终位置,判断用户是否通过验证。 下面是一个简单的滑动验证码Python实现: ```python from PIL import Image, ImageDraw, ImageFont import random # 生成背景图片 def generate_background(): width, height = 300, 150 img = Image.new('RGB', (width, height), (255, 255, 255)) draw = ImageDraw.Draw(img) for x in range(width): for y in range(height): draw.point((x, y), fill=(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))) return img # 生成滑块图片 def generate_slider(): width, height = 60, 60 img = Image.new('RGB', (width, height), (255, 255, 255)) draw = ImageDraw.Draw(img) font = ImageFont.truetype('arial.ttf', 30) text = '>>' draw.text((10, 10), text, font=font, fill=(0, 0, 0)) return img # 将滑块图片添加到背景图片上并随机设置初始位置 def add_slider_to_background(background, slider): x = random.randint(slider.width, background.width - slider.width) y = random.randint(0, background.height - slider.height) background.paste(slider, (x, y)) return x, y # 计算滑动距离 def calculate_offset(x1, x2): return abs(x2 - x1) # 验证拖动距离是否正确 def verify(offset, threshold=10): return offset < threshold # 生成滑动验证码 def generate_captcha(): background = generate_background() slider = generate_slider() x, y = add_slider_to_background(background, slider) background.show() slider.show() return background, slider, x, y # 测试滑动验证码 def test_captcha(): background, slider, x, y = generate_captcha() offset = calculate_offset(x, x + slider.width) print('请拖动滑块完成验证') while True: x2 = input('请输入滑块最终位置的横坐标:') if x2.isdigit(): x2 = int(x2) offset2 = calculate_offset(x, x2) if verify(offset2): print('验证通过') break else: print('验证失败,请重试') else: print('输入不合法,请输入数字') ``` 在上述代码中,`generate_background()`函数用于生成背景图片,`generate_slider()`函数用于生成滑块图片,`add_slider_to_background()`函数将滑块图片添加到背景图片上并随机设置初始位置。`calculate_offset()`函数用于计算滑动距离,`verify()`函数用于验证拖动距离是否正确。`generate_captcha()`函数用于生成滑动验证码,`test_captcha()`函数用于测试滑动验证码
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值