小红书滑块验证码

小红书滑块验证码问题

  • 背景
  • 情况
  • 代码编写
    • 下载图片
    • 计算滑动距离
    • 生成滑动距离列表
    • 执行

背景

最近在维护公司之前的小红书采集代码,其中详情页采集使用的是web直接采集,由于请求频率蛮高的就有了弹滑块验证的问题,之前靠人手滑,但昨天网站仿佛抽风了一样无限弹滑块验证,于是着手开发自动滑块功能。

情况

滑块验证大概就长这样
小红书滑块验证码_第1张图片
在出现这种页面的时候你拿cookie无论怎么访问,都返回不了正确结果,因此需要将这个划开。

代码编写

下载图片

第一步肯定是先把图片下载下来,主要是两张图片,背景图和滑块图,就是红框和蓝框部分
小红书滑块验证码_第2张图片

计算滑动距离

然后是要知道滑动距离,大概就是下图图示的长度
小红书滑块验证码_第3张图片
那问题来了,怎么拿呢,用的方法是基于opencv中的Imgproc,也就是图像处理。那处理缺口图片的方法就用在小学二年级都学过的 灰度化+二值化,这里用的是阈值二值化。话不多说直接上代码

    public static Integer getXiaohongshuDistance(String backgroundPath, String slidePath) {
        OpenCV.loadShared();
        //处理背景图片
        Mat background = Imgcodecs.imread(backgroundPath);
        background = resize(background, 400, 200);
        Imgcodecs.imwrite("e:/tmp/xiaohongshu_bg_400x200.png", background);
        Mat backgroundGrey = new Mat();
        //灰度化
        Imgproc.cvtColor(background, backgroundGrey, Imgproc.COLOR_RGB2GRAY);
        Mat backgroundBit = new Mat();
        //阈值二值化
        Imgproc.threshold(backgroundGrey, backgroundBit, 175, 255, Imgproc.THRESH_BINARY_INV);
        Imgcodecs.imwrite("e:/tmp/xiaohongshu_bg_bit.png", backgroundBit);
        //处理滑块图片
        Mat slide = Imgcodecs.imread(slidePath, IMREAD_UNCHANGED);
        slide = resize(slide, 60, 200);
        Imgcodecs.imwrite("e:/tmp/xiaohongshu_bg_60x200.png", slide);
        Rect opaqueArea = getOpaqueArea(slide);
        Mat crop = crop(slide, opaqueArea);
        //将所有不透明的全部变白
        fillWhiteBackgroundColor(crop);
        Mat slideGrey = new Mat();
        Imgproc.cvtColor(crop, slideGrey, Imgproc.COLOR_RGB2GRAY);
        Mat slideBit = new Mat();
        Imgproc.threshold(slideGrey, slideBit, 254, 255, Imgproc.THRESH_BINARY);
        Imgcodecs.imwrite("e:/tmp/xiaohongshu_slide_bit.png", slideBit);
        //匹配模板
        Mat matchResult = new Mat();
        Imgproc.matchTemplate(backgroundBit, slideBit, matchResult, Imgproc.TM_CCOEFF_NORMED);
        Rect rect = new Rect(0, opaqueArea.y - 3, matchResult.width(), 7);
        matchResult = crop(matchResult, rect);
        Core.MinMaxLocResult minMaxLocResult = Core.minMaxLoc(matchResult);
        Double x = minMaxLocResult.maxLoc.x;
        return x.intValue() - opaqueArea.x;
    }
    public static Mat resize(Mat img, Integer width, Integer height) {
        Mat result = new Mat();
        Imgproc.resize(img, result, new Size(width, height));
        return result;
    }

    public static Rect getOpaqueArea(Mat slide) {
        Set<Integer> xProjection = new HashSet<>();
        Set<Integer> yProjection = new HashSet<>();
        for (int i = 0; i < slide.height(); i++) {
            for (int j = 0; j < slide.width(); j++) {
                Double alpha = slide.get(i, j)[3];
                if (alpha >= 255.0) {
                    xProjection.add(j);
                    yProjection.add(i);
                }
            }
        }
        Integer minX = Collections.min(xProjection);
        Integer maxX = Collections.max(xProjection);
        Integer minY = Collections.min(yProjection);
        Integer maxY = Collections.max(yProjection);
        Rect rect = new Rect(minX, minY, maxX - minX + 1, maxY - minY + 1);
        return rect;
    }

    public static Mat crop(Mat slide, Rect rect) {
        Mat crop = new Mat(slide, rect);
        Mat result = new Mat();
        crop.copyTo(result);
        return result;
    }

    public static void fillWhiteBackgroundColor(Mat slide) {
        for (int i = 0; i < slide.height(); i++) {
            for (int j = 0; j < slide.width(); j++) {
                Double alpha = slide.get(i, j)[3];
                if (alpha < 255.0) {
                    double[] whitePoint = {255.0, 255.0, 255.0, 255.0};
                    slide.put(i, j, whitePoint);
                }
            }
        }
    }

如果看代码还有点不清楚的话,看处理结果图片应该就知道了
小红书滑块验证码_第4张图片
在这里插入图片描述

生成滑动距离列表

得到了滑动距离之后怎么做,直接一步到位直接匀速直线运动全划了?那怕不是直接告诉网站“我是个机器人”。肯定要表现得像个人一样,这里直接用抄来的一段加速度模型。

public static List<Integer> generateTrackers(Integer distance) {
        List<Integer> forwardTrackers = new ArrayList<>();
        Double overDistance = distance + 20.0;
        Double v = 0.0;
        Double t = 0.5;
        Double current = 0.0;
        Double mid = overDistance * 3 / 5;
        while (current < overDistance) {
            Double a;
            if (current < mid) {
                a = 2.0;
            } else {
                a = -3.0;
            }
            Double s = v * t + 0.5 * a * t * t;
            v = v + a * t;
            current += s;
            Integer sInt = (int) Math.round(s);
            forwardTrackers.add(sInt);
        }
        Integer sum = 0;
        for (Integer s : forwardTrackers) {
            sum += s;
        }
        sum = sum - distance - 16;
        List<Integer> backTrackers = new ArrayList<>();
        backTrackers.add(-3);
        backTrackers.add(-3);
        backTrackers.add(-2);
        backTrackers.add(-2);
        backTrackers.add(-2);
        backTrackers.add(-2);
        backTrackers.add(-2);
        for (int i = 0; i < sum; i++) {
            backTrackers.add(-1);
        }
        forwardTrackers.addAll(backTrackers);
        return forwardTrackers;
    }

这段代码的目的是生成一个单次滑动距离的滑动列表。

执行

拖动按钮滑动

public static void performSlide(ChromeDriver driver, WebElement dragButton, List<Integer> trackers) {
        PointerInput defaultMouse = new PointerInput(MOUSE, "default mouse");
        Interaction move = defaultMouse.createPointerMove(Duration.ofMillis(100l), PointerInput.Origin.fromElement(dragButton), 0, 0);
        Sequence sequence = new Sequence(move.getSource(), 0);
        sequence.addAction(move);
        Interaction buttonDown = defaultMouse.createPointerDown(PointerInput.MouseButton.LEFT.asArg());
        sequence.addAction(buttonDown);
        for (int i = 0; i < trackers.size(); i++) {
            Interaction moveByOffset = defaultMouse.createPointerMove(Duration.ofMillis(20l+new Random().nextInt(5)), PointerInput.Origin.pointer(), trackers.get(i), new Random().nextInt(2));
            sequence.addAction(moveByOffset);
        }
        Interaction buttonUp = defaultMouse.createPointerUp(PointerInput.MouseButton.LEFT.asArg());
        sequence.addAction(buttonUp);
        List<Sequence> sequences = new ArrayList<>();
        sequences.add(sequence);
        driver.perform(sequences);
    }

你可能感兴趣的:(opencv,爬虫)