selenium + Chrome 滑动验证码破解三之京东 实现某东登录

之前爬取模拟登录B站和模拟春秋官网的注册验证码,问题都不大.所以今天想挑战下某东,对于稍微有些爬虫基础的来说脚本到登录界面应该都没什么问题,这部分就直接上代码了哈

1.最先开始到输入账号密码,点击登录出现验证码的代码

 def __init__(self):
        self.url = 'https://passport.jd.com/new/login.aspx'
        self.chromedriverPath = "C:\Program Files (x86)\Google\Chrome\Application\chromedriver.exe"
        self.driver = webdriver.Chrome(executable_path=self.chromedriverPath)
        self.wait = WebDriverWait(self.driver, 20)  # 设置等待时间20秒
        self.username = "18516544488"
        self.password = "密码呀密码"

    def agreement_inputPhone(self):
        self.driver.get(self.url)
        self.driver.maximize_window()
        sleep(1)
        #点击账户登录
        self.driver.find_elements_by_xpath('//div[@class="login-tab login-tab-r"]//a')[0].click()
        sleep(3)
        self.inputuser = self.wait.until(EC.presence_of_element_located((By.NAME, "loginname")))
        self.inputuser.send_keys(self.username)
        self.inputpwd = self.wait.until(EC.presence_of_element_located((By.NAME, "nloginpwd")))
        self.inputpwd.send_keys(self.password)
        sleep(1)
        # 点击登录按钮,出现验证码图片
        login_button = self.driver.find_elements_by_xpath(r'//a[@id="loginsubmit"]')[0]
        login_button.click()
        sleep(3)  # 这里必须睡眠,不然可能会获取不到缺口图和缺口

                                          

点击登录,出现验证码,发现每次图片都是重新请求过来,而且不能像之前一样通过JS修改CSS样式获取到完整图了

                             

所以也不能像之前一样采用下面的方式截图了

 gapimg = self.wait.until(EC.presence_of_element_located((By.CLASS_NAME,'geetest_canvas_bg')))
        sleep(2)
        gapimg.screenshot(r'./captcha1.png') #将class为geetest_canvas_bg的区域截屏保存
        # 通过js代码修改标签样式 显示图片2

不然截出来的图会这样

                                                                        

缺口大图和缺口图在一起

2.寻找突破口

也没多想,获取图片URL,先把缺口大图和缺口图下过来再说:抓取缺口图链接并保存图片

url = self.driver.find_element_by_xpath(r'//div/div[@class="JDJRV-bigimg"]/img').get_attribute("src")[0]
with open("backimg.png","wb",error="ignore") as f:
    f.write(requests.get(url).content)

直接报错,URL不合法.

查看url,发现格式为

data:image/png;base64,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

这个格式的URL说实话我是第一次见,不懂就百度,原来是利用base64编码把图片数据翻译成标准ASCII字符,等同于<img src="images/image.png"/> ,具体参考https://www.cnblogs.com/zwshi/p/9251244.html

百度到可以保存图片的方法代码:

    def get_image(self):
        backimgUrl = self.driver.find_element_by_xpath(r'//div/div[@class="JDJRV-bigimg"]/img').get_attribute("src")  # 背景缺口图链接
        gapUrl = self.driver.find_element_by_xpath(r'//div/div[@class="JDJRV-smallimg"]/img').get_attribute("src")  # 块状缺口链接

        request.urlretrieve(backimgUrl, "backing.png")
        request.urlretrieve(gapUrl, "gap.png")

 

那么和之前模拟其他网站一样,最关键的点是获取移动的距离,如何获取?

思路:若是能够寻找缺口在大缺口图中的位置,不就行了嘛,仔细查看缺口图和缺口大图,发现在彩色图片下是不好寻找的,太多干扰,所以决定将图片灰度化,再查找缺口在大缺口中的位置,返回是一个矩阵,是每个点的匹配结果,而我们要的只是X轴上的偏移量即可,这里需要用的cv2和numpy模块,自行安装

代码:

    def get_diff_location(self):
        # 获取图片并灰度化
        block = cv2.imread("gap.png", 0)
        template = cv2.imread("backing.png", 0)
        # 二值化后的图片名称
        blockName = "block.jpg"
        templateName = "template.jpg"
        # 将二值化后的图片进行保存
        cv2.imwrite(blockName, block)
        cv2.imwrite(templateName, template)
        block = cv2.imread(blockName)
        block = cv2.cvtColor(block, cv2.COLOR_RGB2GRAY)
        block = abs(255 - block)
        cv2.imwrite(blockName, block)
        block = cv2.imread(blockName)
        template = cv2.imread(templateName)
        # 获取偏移量
        result = cv2.matchTemplate(block, template,
                                   cv2.TM_CCOEFF_NORMED)  # 查找block在template中的位置,返回result是一个矩阵,是每个点的匹配结果
        x, y = np.unravel_index(result.argmax(), result.shape)
        return y

原图和灰度化后的图片如图:

                                           

3.获取到移动距离,那么和之前一样了,设置移动轨迹,再移动滑块就行了,具体代码和之前差不多,代码如下

    def get_move_track(self, gap):
        track = []  # 移动轨迹
        current = 0  # 当前位移
        # 减速阈值
        mid = gap * 4 / 5  # 前4/5段加速 后1/5段减速
        t = 0.2  # 计算间隔
        v = 0  # 初速度
        while current < gap:
            if current < mid:
                a = random.uniform(2, 5)  # 加速度随机
            else:
                a = -(random.uniform(12.5, 13.5))  # 加速度随机,负数
            v0 = v  # 初速度v0
            v = v0 + a * t  # 当前速度
            move = v0 * t + 1 / 2 * a * t * t  # 移动距离
            current += move  # 当前位移
            track.append(round(move))  # 加入轨迹
        return track

    def move_slider(self, track):
        slider = self.driver.find_elements_by_xpath(r'//div[@class="JDJRV-slide-inner JDJRV-slide-btn"]')[0]
        ActionChains(self.driver).click_and_hold(slider).perform()
        for x in track:  # 只有水平方向有运动 按轨迹移动
            ActionChains(self.driver).move_by_offset(xoffset=x, yoffset=0).perform()
        sleep(1)
        ActionChains(self.driver).release().perform()  # 松开鼠标

 

全部代码,一开始用在注册那边正确率和识别率很高,后面用在登录识别率降低了,后面再优化

# -*- coding: UTF-8 -*-
'''
@Author :Jason
'''
import random

from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from urllib import request
from time import sleep
import cv2
import numpy as np
from selenium.webdriver import ActionChains
from lxml.html import etree
from PIL import Image

import re, requests
from urllib.request import urlretrieve
from bs4 import BeautifulSoup
# 设置无界面模式
from selenium.webdriver.chrome.options import Options


class JD_Login(object):
    def __init__(self):
        self.left = 40 #定义一个左边的起点 缺口一般离图片左侧有一定的距离 有一个滑块
        self.url = 'https://passport.jd.com/new/login.aspx'
        self.chromedriverPath = "C:\Program Files (x86)\Google\Chrome\Application\chromedriver.exe"
        self.driver = webdriver.Chrome(executable_path=self.chromedriverPath)
        self.wait = WebDriverWait(self.driver, 20)  # 设置等待时间20秒
        self.username = "账号"
        self.password = "账号密码"

    def agreement_inputPhone(self):
        self.driver.get(self.url)
        self.driver.maximize_window()
        sleep(1)
        #点击账户登录
        self.driver.find_elements_by_xpath('//div[@class="login-tab login-tab-r"]//a')[0].click()
        sleep(3)
        self.inputuser = self.wait.until(EC.presence_of_element_located((By.NAME, "loginname")))
        self.inputuser.send_keys(self.username)
        self.inputpwd = self.wait.until(EC.presence_of_element_located((By.NAME, "nloginpwd")))
        self.inputpwd.send_keys(self.password)
        sleep(1)
        # 点击登录按钮,出现验证码图片
        login_button = self.driver.find_elements_by_xpath(r'//a[@id="loginsubmit"]')[0]
        login_button.click()
        sleep(3)  # 这里必须睡眠,不然会获取不到缺口图和缺口

    def get_image(self):
        backimgUrl = self.driver.find_element_by_xpath(r'//div/div[@class="JDJRV-bigimg"]/img').get_attribute("src")  # 背景缺口图链接
        gapUrl = self.driver.find_element_by_xpath(r'//div/div[@class="JDJRV-smallimg"]/img').get_attribute("src")  # 块状缺口链接

        request.urlretrieve(backimgUrl, "backing.png")
        request.urlretrieve(gapUrl, "gap.png")

    def get_diff_location(self):
        # 获取图片并灰度化
        block = cv2.imread("gap.png", 0)
        template = cv2.imread("backing.png", 0)
        # 二值化后的图片名称
        blockName = "block.jpg"
        templateName = "template.jpg"
        # 将二值化后的图片进行保存
        cv2.imwrite(blockName, block)
        cv2.imwrite(templateName, template)
        block = cv2.imread(blockName)
        block = cv2.cvtColor(block, cv2.COLOR_RGB2GRAY)
        block = abs(255 - block)
        cv2.imwrite(blockName, block)
        block = cv2.imread(blockName)
        template = cv2.imread(templateName)
        # 获取偏移量
        result = cv2.matchTemplate(block, template,cv2.TM_CCOEFF_NORMED)  # 查找block在template中的位置,返回result是一个矩阵,是每个点的匹配结果
        x, y = np.unravel_index(result.argmax(), result.shape)
        print("x方向的偏移", int(y * 0.4 + 18), 'x:', x, 'y:', y)
        return y

    def get_move_track(self, gap):
        print("需要移动的距离",gap)
        track = []  # 移动轨迹
        current = 0  # 当前位移
        # 减速阈值
        mid = gap * 4 / 5  # 前4/5段加速 后1/5段减速
        t = 0.2  # 计算间隔
        v = 0  # 初速度
        while current < gap:
            if current < mid:
                a = 3  # 加速度为+3
            else:
                a = -3  # 加速度为-3
            v0 = v  # 初速度v0
            v = v0 + a * t  # 当前速度
            move = v0 * t + 1 / 2 * a * t * t  # 移动距离
            current += move  # 当前位移
            track.append(round(move))  # 加入轨迹
        return track

    def move_slider(self, track):
        slider = self.driver.find_elements_by_xpath(r'//div[@class="JDJRV-slide-inner JDJRV-slide-btn"]')[0]
        ActionChains(self.driver).click_and_hold(slider).perform()
        for x in track:  # 只有水平方向有运动 按轨迹移动
            ActionChains(self.driver).move_by_offset(xoffset=x, yoffset=0).perform()
        sleep(1)
        ActionChains(self.driver).release().perform()  # 松开鼠标

    def main(self):
        self.agreement_inputPhone()
        self.get_image()
        gap = self.get_diff_location()
        track = self.get_move_track(gap)
        # print("移动轨迹",track)
        self.move_slider(track)


if __name__ == "__main__":
    jd = JD_Login()
    jd.main()
          

 

  • 7
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值