官网

中文文档:DrissionPage官网

一、为什么使用自动化?

1. 个人考虑

必须要承认一点,在各大厂商爬虫和反爬的策略不断升级迭代,逆向的成本逐步增加,逆向时间长,复杂度高等。自动化则可以间接规避掉环境,签名等加密参数的破解,逆向经常被各种网站埋藏的蜜罐参数,可以请求,但确一不小心就中招了,让网站一眼就能知道你不是一个人!

2. 时间成本

3. 法律风险

4. 不足

显而易见的慢,和消耗大量资源,所以对紧急任务做一个快速上线是个不错的选择。

二、为什么选择 DrissionPage

1. 优雅

语法简介,代码量少,对新手友好

既能控制浏览器也能收法数据包(和requests那般丝滑)

2. 特征

1) selenium 有webdrive特征,driisionpage 没有webdrive特征

2) driisionpage基于cdp协议(其实selenium 4.0版本以后也支持了)

什么是cpd请参考
Chrome DevTools 协议
浏览器自动化必须知道CDP协议_chrome cdp-CSDN博客

三、开始

1. 下载

当前学习的版本 DrissionPage 4.1.0.12

pip install DrissionPage==4.1.0.12

2. 使用

from DrissionPage import Chromium, ChromiumPage

browser = Chromium()
tab = browser.latest_tab
tab.get("http://baidu.com")

四、滑动

滑动验证码的一些使用案例,配合ddddocr即可完成一些简单的滑动

page.actions.hold 按住鼠标左键不放

page.actions.move 移动

page.actions.release 松开鼠标

下面是一些核心代码。包括了如何使用ddddocr计算坐标,计算滑动轨迹,和自动化移动的实现

import time
import ddddocr
import random
from DrissionPage import ChromiumPage
import os
import shutil


class SlideCaptchaSolver:
    def __init__(self):
        self.page = ChromiumPage()

    @staticmethod
    def get_distance_by_ddddocr():
        """使用ddddocr计算缺口距离"""
        det = ddddocr.DdddOcr(det=False, ocr=False, show_ad=False)
        with open('./img/target.png', 'rb') as f:
            target_bytes = f.read()
        with open('./img/background.png', 'rb') as f:
            background_bytes = f.read()
        res = det.slide_match(target_bytes, background_bytes)
        x_distance = res["target"][0]
        return x_distance

    @staticmethod
    def get_tracks(distance):
        """滑块的运动轨迹"""
        value = round(random.uniform(0.55, 0.75), 2)
        v, t, sum = 0, 0.3, 0
        plus = []
        mid = distance * value
        while sum < distance:
            if sum < mid:
                a = round(random.uniform(2.5, 3.5), 1)
            else:
                a = -round(random.uniform(2.0, 3.0), 1)
            s = v * t + 0.5 * a * (t ** 2)
            v = v + a * t
            sum += s
            plus.append(round(s))

        reduce = [-6, -4, -6, -4]
        return {'plus': plus, 'reduce': reduce}

    def move_to_gap(self, slide_ele, tracks):
        self.page.actions.hold(f"{slide_ele}")  # 此方法用于按住鼠标左键不放,按住前可先移动到元素上
        # 使鼠标相对当前位置移动若干距离
        for track in tracks['plus']:
            self.page.actions.move(
                offset_x=track,
                offset_y=round(random.uniform(1.0, 3.0), 0),
                duration=.1
            )
        time.sleep(0.5)
        self.page.actions.release(f"{slide_ele}")  # 此方法用于释放鼠标左键,释放前可先移动到元素上。

ddddocr 是可以直接传入bytes ,可以不用下载图片

target_bytes = self.page.ele('.shumei_captcha_loaded_img_fg').src()
background_bytes = self.page.ele('.shumei_captcha_loaded_img_bg').src()

det = ddddocr.DdddOcr(det=False, ocr=False, show_ad=False)
res = det.slide_match(target_bytes, background_bytes)

五、获取show_root

showroot.jpg

# !/usr/bin/env python
# -*- coding: UTF-8 -*-
# @project    :爬虫 
# @author     :zhy
# @date       :2024-12-31 12:36
from DrissionPage import Chromium


browser = Chromium()
tab = browser.latest_tab
tab.get('https://ygp.gdzwfw.gov.cn/#/44/new/jygg/v3/A?noticeId=0a69a67b775c4690b1c49d02700c5048&projectCode=X44020008320002o32nn&bizCode=3C14&siteCode=440200&publishDate=20241231121903&source=%E9%9F%B6%E5%85%B3%E5%B8%82%E5%85%AC%E5%85%B1%E8%B5%84%E6%BA%90%E4%BA%A4%E6%98%93%E5%B9%B3%E5%8F%B0&titleDetails=%E5%B7%A5%E7%A8%8B%E5%BB%BA%E8%AE%BE&classify=A02&nodeId=1864880096317337601')

# html = tab.ele('xpath://div[@class="richtext"]/div').html
# print(html)
# out: <div data-v-0b057b9b=""></div>

html = tab.ele('xpath://div[@class="richtext"]/div').shadow_root
print(html)
# out: <ShadowRoot in <ChromiumElement div data-v-0b057b9b=''>>

print(html.ele('xpath://p[12]').text)

六、多线程

# !/usr/bin/env python
# -*- coding: UTF-8 -*-
# @project    :爬虫 
# @author     :zhy
# @date       :2024-12-31 12:36
from threading import Thread

from DrissionPage import ChromiumPage
from DataRecorder import Recorder


def collect(tab, recorder, title):
    """用于采集的方法
    :param tab: ChromiumTab 对象
    :param recorder: Recorder 记录器对象
    :param title: 类别标题
    :return: None
    """
    num = 1  # 当前采集页数
    while True:
        # 遍历所有标题元素
        for i in tab.eles('.title project-namespace-path'):
            # 获取某页所有库名称,记录到记录器
            recorder.add_data((title, i.text, num))

        # 如果有下一页,点击翻页
        btn = tab('@rel=next', timeout=2)
        if btn:
            btn.click(by_js=True)
            tab.wait.load_start()
            num += 1

        # 否则,采集完毕
        else:
            break


def main():
    # 新建页面对象
    page = ChromiumPage()

    url = 'https://www.xiaohongshu.com/explore'

    # 第一个标签页访问网址
    page.get('https://www.xiaohongshu.com/explore')
    # 获取第一个标签页对象
    tab1 = page.get_tab()

    # 新建一个标签页并访问另一个网址
    tab2 = page.new_tab('https://gitee.com/explore/machine-learning')
    # 获取第二个标签页对象
    tab2 = page.get_tab(tab2)

    # 新建记录器对象
    recorder = Recorder('data.csv')

    # 多线程同时处理多个页面
    Thread(target=collect, args=(tab1, recorder, 'python')).start()
    Thread(target=collect, args=(tab2, recorder, 'java')).start()


if __name__ == '__main__':
    main()

七、对接指纹浏览器 - 比特浏览器