# 浏览器自动化（一）

By [plsdm7](https://paragraph.com/@dogsuisui) · 2025-03-12

---

最近实在亏的太多心情很浮躁，而且近期一直有朋友问我怎么刷量的。就想着大概写点东西总结一下，先开个坑写一下怎么用浏览器自动化刷号。相关的教程网上很多，对程序员而言这个也不算什么难事儿，我主要会结合真实的例子讲一下怎么把流程跑通。

**工具选择**

我假定大家都对撸空投有基本认识，手动或者写脚本操作过。

基于撸空投场景，最常见的几个问题

1、浏览器插件（主要是钱包插件）操作

2、twitter、discord、gmail等等账号认证授权

3、各种验证码

上面的问题基于纯代码也是能搞定的。但有时候操作流程又臭又长，要拼接处理很多参数，部分项目还会有一些加密混淆需要去分析。所以很容易想到使用浏览器自动化代替人来执行。

关于自动化工具最常见的就是selenium（我研究生论文就是用它搞得），但是后来我问了下AI，他推荐了DrissionPage。

[https://www.drissionpage.cn/](https://www.drissionpage.cn/)

我试了一下功能比较强大语法也很简洁（给大佬递茶），对浏览器插件的支持也很不错。唯一问题就是AI对他的支持不是很好，遇到可能需要自己去查API没法直接问AI。

后面就基于Mac + DrissionPage + web3py + pyautogui实现。

本次以小狐狸钱包为例，实现导入切换删除钱包功能，如果用其他钱包应该也差不多。

    from DrissionPage import WebPage, ChromiumOptions
    import os, time, random, pyautogui
    from DrissionPage.common import Actions
    import sys,requests
    from DrissionPage.common import Keys
    import threading
    import json
    
    class wallet:
        def __init__(self, address, private, mem=''):
            self.address = address
            self.private = private
            self.mem = mem
    
    #不带浏览器信息，采用导入metamask的方式，适用于只需要钱包信息的场景
    
    def get_chromium_options( arguments: list) -> ChromiumOptions:
        options = ChromiumOptions()
        # options.set_argument('--auto-open-devtools-for-tabs', 'true') # we don't need this anymore
        # options.set_paths(browser_path=browser_path)
        for argument in arguments:
            options.set_argument(argument)
        return options
    
    
    
    def createPage():
        global page  # 声明为全局变量
        arguments = [
            "-no-first-run",
            "-force-color-profile=srgb",
            "-metrics-recording-only",
            # "-password-store=basic",
            "-use-mock-keychain",
            "-export-tagged-pdf",
            "-no-default-browser-check",
            "-disable-background-mode",
            "-enable-features=NetworkService,NetworkServiceInProcess,LoadCryptoTokenExtension,PermuteTLSExtensions",
            # "-disable-features=FlashDeprecationWarning,EnablePasswordsAccountStorage",
            "-deny-permission-prompts",
            "--start-maximized",
            # "-disable-gpu",
            # "-accept-lang=en-US",
            # "--no-sandbox",
        ]
        co = get_chromium_options(arguments)
        # co.add_extension('/Users/jhw/Desktop/12.1.3_0')
        # 创建页面对象
        page = WebPage(chromium_options=co)
    
        time.sleep(1)
    
        page.set.window.full()
    
    
    def openMetamask():
        # 因为插件会保存，所以跳过导入助记词，直接用私钥导入
        # 可以先跑一遍导入，后续直接用搜索就行了
        
        #打开插件
        page.get('chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/home.html')
    
        success = page.wait.ele_displayed('x://*[@id="password"]', timeout=5)
        if success:
            #输入密码
            input = page.ele('x://*[@id="password"]')
            input.input('11111111')
            
            btn = page.ele('x://*[@id="app-content"]/div/div[2]/div/div/button')
            btn.click()
            time.sleep(5)
        
    
    def importWallet(wal:wallet):
        btn = page.ele('x://*[@id="app-content"]/div/div[2]/div/div[2]/button')
        btn.click()
        time.sleep(1)
    
        page.wait.ele_displayed('@data-testid=multichain-account-menu-popover-action-button', timeout=20)
        addBtn = page.ele('@data-testid=multichain-account-menu-popover-action-button')
        addBtn.click()
    
        importX = 'x:/html/body/div[3]/div[3]/div/section/div/div[2]/button'
        page.wait.ele_displayed(importX, timeout=20)
        importBtn = page.ele(importX)
        importBtn.click()
    
        page.wait.ele_displayed('x://*[@id="private-key-box"]', timeout=20)
        inputBox = page.ele('x://*[@id="private-key-box"]')
        inputBox.input(wal.private)
    
        confirmX = 'x:/html/body/div[3]/div[3]/div/section/div/div/div[2]/button[2]'
        page.wait.ele_displayed(confirmX, timeout=20)
        confirmBtn = page.ele(confirmX)
        confirmBtn.click()    
    
        success = page.wait.ele_displayed('@text=启用', timeout=2)
        page.actions.key_down(Keys.ESCAPE)
        if success:
            open = page.ele('@text=启用')
            open.click()
    
    
    def deleteWallet():
        tab = None
        for t in page.get_tabs():
            if t.title == 'MetaMask':
                tab = t
                break
        
        if tab == None:
            print('找不到tab退出')
            return
    
        tab.set.activate()
    
        success = False
        retry_count = 0
        bX = 'x://*[@id="app-content"]/div/div[2]/div/div[2]/button'
        while not success and retry_count < 3:
            success = tab.wait.ele_displayed(bX, timeout=5)
            retry_count += 1
    
        if success:
            btn = tab.ele(bX)
            btn.click()
        else:
            print('找不到按钮，退出')
            return
    
        time.sleep(1)
        account2MoreOptX = '@aria-label=Account 2 期权'
        success = tab.wait.ele_displayed(account2MoreOptX, timeout=3)
        if success == False:
            print("找不到第二个账号，直接退出")
            tab.actions.key_down(Keys.ESCAPE)
            return
        account2MoreBtn = tab.ele(account2MoreOptX)
        account2MoreBtn.click()
    
        delX = '@data-testid=account-list-menu-remove'
        tab.wait.ele_displayed(delX, timeout=5)
        delBtn = tab.ele(delX)
        delBtn.click()
    
        confirmDelX = 'tx=删除'
        tab.wait.ele_displayed(confirmDelX, timeout=10)
        confirmDelBtn = tab.ele(confirmDelX)
        confirmDelBtn.click()
        time.sleep(1)
    
    
    page:WebPage = None
    
    if __name__ == '__main__':
        walletList = [
            wallet(address='0x1234567890abcdef1234567890abcdef12345678', private='private_key_1'),
            wallet(address='0xabcdef1234567890abcdef1234567890abcdef12', private='private_key_2'),
            wallet(address='0x7890abcdef1234567890abcdef1234567890abcd', private='private_key_3'),
        ]
    
        createPage()
    
        openMetamask()
    
        deleteWallet()
    
        for i, src_wal in enumerate(walletList):
            print("当前主地址：" + src_wal.address)
            
            importWallet(src_wal)
    
            time.sleep(2)
    
            deleteWallet()
    
            time.sleep(2)
    
        page.quit()
    

首先运行脚本打开浏览器，要自己下载小狐狸钱包，创建一个钱包完成配置。

也可以导入钱包

    # co.add_extension('/Users/jhw/Desktop/12.1.3_0')
    

小狐狸经常更新，导入其实也不是很稳定。

完成设置后，关掉无头浏览器，再次启动脚本就可以开始运行了。

代码非常简单，就是打开小狐狸，导入钱包，删除钱包。

浏览器参数可以灵活调整，主要在于减少被验证码识别为bot的概率，我们暂时不涉及，每个参数的含义可以问AI。

打开小狐狸

        page.get('chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/home.html')
    

这里我们需要把浏览器插件作为一个tab访问，所以直接打开插件地址（这个地址可以在小狐狸里点展开视图就可以看到）。

以输入密码为例，打开浏览器调试工具，定位元素到输入框找到对应的html元素，右键点击复制xpath。这样就得到了这个元素的xpath

//\*\[@id="password"\]

![](https://storage.googleapis.com/papyrus_images/8f01ed6af38f67560ba8fb3de90a50132c6fdd80183578e48588b610eaec025a.png)

利用xpath就可以定位到对应元素，执行操作

    def openMetamask():
        # 因为插件会保存，所以跳过导入助记词，直接用私钥导入
        # 可以先跑一遍导入，后续直接用搜索就行了
        
        #打开插件
        page.get('chrome-extension://nkbihfbeogaeaoehlefnkodbefgpgknn/home.html')
    
        success = page.wait.ele_displayed('x://*[@id="password"]', timeout=5)
        if success:
            #输入密码
            input = page.ele('x://*[@id="password"]')
            input.input('11111111')
            
            btn = page.ele('x://*[@id="app-content"]/div/div[2]/div/div/button')
            btn.click()
            time.sleep(5)
    

DrissionPage支持各种方式的元素定位，具体可以参考官方文档。

了解了定位和操作元素后，后面就是同样的操作流程，定位元素，操作元素，等待响应

具体可以看视频

[https://drive.google.com/file/d/1nwOq4oa6W5oza\_htPfVVVSKLgx0XcMGL/view?usp=drive\_link](https://drive.google.com/file/d/1nwOq4oa6W5oza_htPfVVVSKLgx0XcMGL/view?usp=drive_link)

注意由于小狐狸更新可能对应的元素xpath会有变化，所以如果某一步操作不生效，需要重新查看一下xpath更新一下代码，有时候小狐狸也会突然弹出一些广告啊、新功能啥的弹框遇到了就手动点掉就好了

这样操作小狐狸钱包的流程就完成了，剩下的就是开始交互项目了。

下一篇应该会写一下怎么绕过cloudflare验证。

---

*Originally published on [plsdm7](https://paragraph.com/@dogsuisui/pbLDTgUh7xi9ck7yAxAu)*
