浏览器自动化(一)

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

工具选择

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

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

1、浏览器插件(主要是钱包插件)操作

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

3、各种验证码

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

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

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"]

post image

利用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

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

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

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