声明

本文章中所有内容仅供学习交流使用,不用于其他任何目的,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!
本文章未经许可禁止转载,禁止任何修改后二次传播,擅自使用本文讲解的技术而导致的任何意外,作者均不负责。
若有侵权,请在评论区联系作者立即删除!

前言

本文章主要是我结合自己的工作经验,讲解一下基于 DrissionPage 如何实现京东网页端登录流程,涉及到一些 DrissionPage 的简单使用,滑块算法和图像识别算法的说明和使用。如有更专业高效的方案欢迎在评论区探讨,有不正之处还望指正。

一、DrissionPage 启动浏览器

参考官方文档 DrissionPage 使用文档,先将浏览器启动起来。

1.1 安装 DrissionPage

 pip install DrissionPage 

我使用的版本

1.2 自动化打开浏览器

使用最简单的配置,成功打开一个新的浏览器。

import time
from DrissionPage import Chromium, ChromiumOptions
def new_browser():
    co = ChromiumOptions()
    # 指定其他端口,新开一个浏览器,不影响当前已打开的浏览器的使用
    co.set_paths(local_port=10086)
    # 设置启动时最大化
    co.set_argument("--start-maximized")
    # 设置浏览器可视窗口大小
    co.set_argument("--window-size=1920,1080")
    # 以该配置创建页面对象
    browser = Chromium(addr_or_opts=co)
    time.sleep(10)
    # 关闭浏览器
    browser.quit()
if __name__ == "__main__":
    new_browser()

1.3 登录主流程实现

写好登录流程的主要逻辑,如下所示:

import time
from DrissionPage import Chromium, ChromiumOptions

class JDCrawler:
    def new_browser(self):
        co = ChromiumOptions()
    	# 指定其他端口,新开一个浏览器,不影响当前已打开的浏览器的使用
        co.set_paths(local_port=10086)
        # 设置启动时最大化
        co.set_argument("--start-maximized")
        # 设置浏览器可视窗口大小
        co.set_argument("--window-size=1920,1080")
        # 以该配置创建页面对象
        browser = Chromium(addr_or_opts=co)
        return browser

    def login(self, page):
        print("============= 账号密码登录页面加载完成 =============")
        # TODO: 实现具体登录逻辑


if __name__ == "__main__":
    print("============= 程序开始 =============")
    jd_crawler = JDCrawler()

    try:
        browser = jd_crawler.new_browser()
        page = browser.latest_tab

        print("============= 准备进入登录页 =============")
        page.get(JD_LOGIN_URL)
        time.sleep(3)
        jd_crawler.login(page)
    except Exception as e:
        print("执行报错:", e)

    finally:
        print("============= 准备关闭浏览器 =============")
        page.close()
        browser.quit()

    print("============= 程序退出 =============")

二、分析登录流程

我们打开进入 京东登录页,输入账号密码会发现整体登录流程可以拆解为这几个步骤:

  1. 输入账号
  2. 输入密码
  3. 点击登录
  4. 滑块校验
  5. 校验通过,登录完成

2.1 输入账号

我这里根据 css 选择器 规则 #loginname 定位到账号输入框元素。

通过 DP 提取元素并输入账户名:page.ele("#loginname", timeout=DEFAULT_TIMEOUT).clear().input(JD_ACCOUNT),其中 clear() 是为了清除输入框内容。

2.2 输入密码

css 选择器 规则 #nloginpwd 定位到密码输入框元素。

通过 DP 输入密码:page.ele("#nloginpwd", timeout=DEFAULT_TIMEOUT).clear().input(JD_PASS)

2.3 点击提交

page.ele("#loginsubmit", timeout=DEFAULT_TIMEOUT).click() 实现点击操作,提交账号。

2.4 提交账户登录

点击登录后因为风控规则可能会出现滑块,需要检查一下是出现了滑块,再进行后续操作。

检查代码:

 slider_ele = page.ele('xpath://div[@class="JDJRV-suspend-slide"]',timeout=DEFAULT_TIMEOUT)
 if slider_ele and slider_ele.states.is_displayed:
     # TODO: 处理滑块

三、滑块校验

3.1 获取背景图片

利用 xpath 规则://div[@class="JDJRV-bigimg"]/img获取背景图片,DP 提取元素:page.ele('xpath://div[@class="JDJRV-bigimg"]/img')

3.2 获取缺口图片

page.ele('xpath://div[@class="JDJRV-smallimg"]/img')

3.3 定位滑块按钮位置

page.ele('xpath://div[@class="JDJRV-slide-inner JDJRV-slide-btn"]')

3.4 图像识别,计算出移动距离

3.4.1 转换文件格式

网页的图片是base64格式,需要将图片转成 OpenCV 可以处理的图像格式。

 @staticmethod
 def img2cv(img_data):
     """openCV 读取 base64 图片"""
     data = img_data.replace("data:image/png;base64,", "").replace(
         "data:image/jpg;base64,", ""
     )
     img_data = base64.b64decode(data)
     # 将图片的二进制数据转换为图像的原始字节数据;np.uint8 指定将数据解析为 8 位无符号整数
     img_np = np.frombuffer(img_data, np.uint8)
     # 图像的原始字节数据解码为 OpenCV 可以处理的图像格式
     img = cv2.imdecode(img_np, cv2.IMREAD_COLOR)
     return img

3.4.2 滑动距离识别

完成了对图片的处理之后,我们继续利用 OpenCV 来识别背景图片和缺口图片之间的距离。

def img2xy(bg_img, tp_img):
    """
    识别图片缺口位置
    Args:
        bg_img : 背景图片
        tp_img : 缺口图片
    Returns:
        返回缺口的左上角、右下角坐标
    """
    # 边缘检测:利用边缘检测算法 cv2.Canny 识别图片的边缘
    bg_edge = cv2.Canny(bg_img, 100, 200)
    # 100 和 200 是 Canny 算法的阈值参数,用于控制边缘检测的灵敏度
    tp_edge = cv2.Canny(tp_img, 100, 200)
    # 转换图片格式:将边缘检测结果从灰度图转换为 RGB 格式
    bg_pic = cv2.cvtColor(bg_edge, cv2.COLOR_GRAY2RGB)
    tp_pic = cv2.cvtColor(tp_edge, cv2.COLOR_GRAY2RGB)
    # 缺口匹配
    res = cv2.matchTemplate(bg_pic, tp_pic, cv2.TM_CCOEFF_NORMED)
    # 寻找最优匹配
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
    # 左上角点的坐标
    tl = max_loc
    # th 和 tw 分别是缺口图片的高度和宽度
    th, tw = tp_pic.shape[:2]
    # 右下角点的坐标
    br = (tl[0] + tw, tl[1] + th)
    # 返回缺口区域的左上角坐标 (x1, y1) 和右下角坐标 (x2, y2)
    return max_loc, br

上面是图片缺口识别的算法,最终返回左上角坐标和右下角坐标,在登录流程中,我们只需要左上角坐标 max_locx 坐标,这个就是我们要的滑动距离 distance
需要注意的是网页会存在对图片进行不同比例的渲染,这时候移动的距离也要等比例延长或者缩短,才能移动到目标位置,这个比例系数 coefficient 我们可以通过观察图片的 Rendered Size(渲染尺寸)Intrinsic Size(固有尺寸) 的比值得出,比如这里 Rendered Size / Intrinsic Size = 0.95,因此我们真实的滑动距离为 distance * 0.95

3.5 生成滑动轨迹

3.5.1 公式原理

  • 位移公式:s = v₀t + ½at²
  • 速度公式:v = v₀ + at
  • s 表示距离;v₀ 表示初速度;v 表示末速度;t 表示时间;a 表示加速度
    利用上面两个公式,模拟人在滑动滑块时先加速后减速的行为,s 的值则是上面步骤得出的 distance,加速度 a 则根据我们调整不同的数值,观察滑动的情况来优化。

3.5.2 轨迹生成示例代码

数据需要根据实际滑动的情况来调整,直到效果跟人滑动出的效果差不多,则证明这个轨迹坐标是有效的。

def handle_distance(distance):
    """生成轨迹"""

    v = 0  # 初始速度为 0
    t = 2  # 每 2 秒生成一个坐标
    forward_tracks = []  # 保存坐标值 x

    current = 0  # 当前已滑动的距离
    # 加速和减速的距离转折点
    mid = "给一个距离"
    while current < distance:
        if current < mid:
            a = "给一个加速数值"
        else:
            a = "给一个减速数值"

        s = v * t + 0.5 * a * (t**2)    # 位移公式
        v = v + a * t   # 速度公式
        current += s
        forward_tracks.append(round(s))

3.6 DrissionPage 实现拖拽

利用 DP 的动作链实现鼠标拖拽。

 def slider(
     self, page, bg_ele_xpath, patch_ele_xpath, button_ele_xpath, coefficient
 ):
     """
     移动滑块
     Args:
         page (string): page对象
         bg_ele_xpath (string): 背景图片选择器表达式
         patch_ele_xpath (string): 缺口图片选择器表达式
         button_ele_xpath (string): 拖动按钮选择器表达式
         coefficient (float): 图片缩放比例系数
     """

     print("============= 识别图片 =============")
     # 识别缺口距离
     picture_distance = self.recognize_position(page, bg_ele_xpath, patch_ele_xpath)
     distance = picture_distance * coefficient  # 调整滑动距离
     tracks = self.handle_distance(distance)  # 生成轨迹坐标

     button = page.ele(button_ele_xpath)  # 获取滑块按钮
     ac = Actions(page)  # 动作链对象
     print("============= 开始移动滑块 =============")
     ac.move_to(button)  # 移动鼠标到滑块按钮位置
     ac.hold(button)  # 按住滑块
     # 移动滑块
     for pos in tracks:
         y = random.uniform(-1, 2)  # 模拟鼠标上下晃动
         ac.move(pos, y)  # 移到坐标位置
     # 模拟停顿
     time.sleep(random.uniform(0.5, 0.8))
     ac.release(button)  # 松开按钮

四、验证效果

滑块校验通过。

 

获取资源前请仔细阅读一下声明:

重要提示

如有解压密码: 看下载页、看下载页、看下载页。
源码工具资源类具有可复制性: 建议具有一定思考和动手能力的用户购买。
请谨慎考虑: 小白用户和缺乏思考动手能力者不建议赞助。
虚拟商品购买须知: 虚拟类商品,一经打赏赞助,不支持退款。请谅解,谢谢合作!
声明: 本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。