Press "Enter" to skip to content

使用selenium模拟网页操作,执行重复任务

背景

战网安全令独立服关闭,导致之前没有存储密钥的安全令统统不好用了,经过一番研究发现可以通过反复绑定安全令到战网账号的方式获取到安全令的密钥,但是这个方式需要去战网账号上面将安全令进行解绑,暴雪没有提供解绑接口,一番研究发现战网的登录机制还是比较复杂的。时间比较紧张,考虑用selenium模拟网页操作的方式来解绑安全令。

方案

1.安装docker版本的模拟器

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
docker run -d -p 4444:4444 selenium/standalone-chrome
docker run -d -p 4444:4444 selenium/standalone-chrome
docker run -d -p 4444:4444 selenium/standalone-chrome

2.测试模拟器

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import time
from selenium import webdriver
import requests
import json
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
options = webdriver.ChromeOptions()
options.add_argument('--headless') # example
driver = webdriver.Remote("http://127.0.0.1:4444/wd/hub", options=options)
print("尝试打开官网")
driver.get('https://account.battle.net/')
print(driver.page_source.encode("utf-8"))
driver.quit()
#!/usr/bin/python # -*- coding: UTF-8 -*- import time from selenium import webdriver import requests import json from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.common.by import By from selenium.common.exceptions import NoSuchElementException options = webdriver.ChromeOptions() options.add_argument('--headless') # example driver = webdriver.Remote("http://127.0.0.1:4444/wd/hub", options=options) print("尝试打开官网") driver.get('https://account.battle.net/') print(driver.page_source.encode("utf-8")) driver.quit()
#!/usr/bin/python
# -*- coding: UTF-8 -*-


import time
from selenium import webdriver
import requests
import json
from selenium.webdriver.support.wait import WebDriverWait 
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException


options = webdriver.ChromeOptions()
options.add_argument('--headless')  # example
driver = webdriver.Remote("http://127.0.0.1:4444/wd/hub", options=options)

print("尝试打开官网")
driver.get('https://account.battle.net/')
print(driver.page_source.encode("utf-8"))

driver.quit()

3.完整脚本

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import time
from selenium import webdriver
import requests
import json
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException
ACCOUNT = ''
PASSWORD = ''
TOKEN = 'Bearer xx'
def rollback(serial, restoreCode, deviceSecret):
options = webdriver.ChromeOptions()
options.add_argument('--headless') # example
driver = webdriver.Remote("http://127.0.0.1:4444/wd/hub", options=options)
try:
# 登录战网
print("尝试打开官网")
driver.get('https://account.battle.net/')
element = WebDriverWait(driver, 30).until(lambda x: x.find_element(By.ID, "accountName"))
# is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)).until_not(lambda x: x.find_element(By.ID, "someId").is_displayed())
print("官网已经打开....")
accountName = driver.find_element_by_id('accountName')
accountName.send_keys(ACCOUNT)
password = driver.find_element_by_id('password')
password.send_keys(PASSWORD)
driver.find_element_by_id("submit").click()
print("输入账号密码..")
time.sleep(5)
if check_exists_by_xpath(driver, '//button[@formaction="/login/en/authenticator/choose"]'):
uasc = driver.find_element_by_xpath('//button[@formaction="/login/en/authenticator/choose"]')
uasc.click()
element = WebDriverWait(driver, 30).until(lambda x: x.find_element(By.ID, "authValue"))
authValue = driver.find_element_by_id('authValue')
a = getauth(serial, restoreCode, deviceSecret)
authValue.send_keys(a['code']) #输入安全令密钥
print("输入安全令...")
driver.find_element_by_id("submit").click()
element = WebDriverWait(driver, 30).until(lambda x: x.find_element(By.ID, "app"))
print("尝试解除安全令...")
#解除安全令
driver.get('https://account.battle.net/security/authenticator/detach')
element = WebDriverWait(driver, 30).until(lambda x: x.find_element(By.ID, "attach-detach-confirm"))
print("点击解除安全令按钮...")
driver.find_element_by_id("attach-detach-confirm").click()
element = WebDriverWait(driver, 30).until(lambda x: x.find_element_by_xpath('//input[@maxlength="8"]'))
print("输入安全令密钥...")
authValue = driver.find_element_by_xpath('//input[@maxlength="8"]')
# print(getAllAttr(driver, authValue))
# print(authValue.get_attribute('innerHTML'))
a = getauth(serial, restoreCode, deviceSecret)
authValue.send_keys(a['code']) #输入安全令密钥
print("解除安全令.按钮查找中.......")
btn1 = driver.find_elements_by_class_name("btn-primary")[0]
time.sleep(5)
# print(getAllAttr(driver, btn1))
print("解除安全令.按钮点击.......")
btn1.click()
time.sleep(10)
check = hasAuth()
if("restoreCode" in check):
print("解除安全令.失败了.......")
else:
print("解除安全令.成功.......")
except Exception as err:
print(f"Unexpected {err}")
time.sleep(10)
driver.quit()
def getAllAttr(driver, element):
attr = driver.execute_script('var items = {}; for (index = 0; index < arguments[0].attributes.length; ++index) { items[arguments[0].attributes[index].name] = arguments[0].attributes[index].value }; return items;', element)
attr['innerHTML'] = element.get_attribute('innerHTML')
return attr
def check_exists_by_xpath(driver, xpath):
try:
driver.find_element_by_xpath(xpath)
except NoSuchElementException:
return False
return True
def getauth(serial, restoreCode, deviceSecret):
url = "http://192.168.1.99:8080/api/directGetCode"
payload = {
'restore': restoreCode,
'serial': serial,
'secret': deviceSecret
}
headers = {
'user-agent': 'BlizzardSocial/1.21.2 (com.blizzard.social; build:6686; iOS 16.5.1) Alamofire/5.6.4',
}
response = requests.request("POST", url, headers=headers, data=payload)
return response.json()
def calc(serial, restoreCode):
url = "https://authenticator-rest-api.bnet-identity.blizzard.net/v1/authenticator/device"
payload = json.dumps({
"serial": serial,
"restoreCode": restoreCode
})
headers = {
'authorization': TOKEN,
'user-agent': 'BlizzardSocial/1.21.2 (com.blizzard.social; build:6686; iOS 16.5.1) Alamofire/5.6.4',
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=payload)
return response.json()
def hasAuth():
url = "https://authenticator-rest-api.bnet-identity.blizzard.net/v1/authenticator"
headers = {
'authorization': TOKEN,
'user-agent': 'BlizzardSocial/1.21.2 (com.blizzard.social; build:6686; iOS 16.5.1) Alamofire/5.6.4',
'Content-Type': 'application/json'
}
response = requests.request("GET", url, headers=headers)
return response.json()
def getSecret(serial, restoreCode):
print("try to handel: " + serial + " : " + restoreCode)
check = hasAuth()
if("restoreCode" in check):
print("account has auth with: " + check['serial'] + ", try to unlock..")
doGetSecret(check['serial'], check['restoreCode'])
return doGetSecret(serial, restoreCode)
def doGetSecret(serial, restoreCode):
c = calc(serial, restoreCode)
if("deviceSecret" in c):
a = getauth(serial, restoreCode, c['deviceSecret'])
rollback(serial, restoreCode, c['deviceSecret'])
return c
else:
raise Exception("doGetSecret failed: " + str(c))
if __name__ == '__main__':
sec = getSecret("US230521228545", "W8M2HRA0EK")
print(sec)
#!/usr/bin/python # -*- coding: UTF-8 -*- import time from selenium import webdriver import requests import json from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.common.by import By from selenium.common.exceptions import NoSuchElementException ACCOUNT = '' PASSWORD = '' TOKEN = 'Bearer xx' def rollback(serial, restoreCode, deviceSecret): options = webdriver.ChromeOptions() options.add_argument('--headless') # example driver = webdriver.Remote("http://127.0.0.1:4444/wd/hub", options=options) try: # 登录战网 print("尝试打开官网") driver.get('https://account.battle.net/') element = WebDriverWait(driver, 30).until(lambda x: x.find_element(By.ID, "accountName")) # is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)).until_not(lambda x: x.find_element(By.ID, "someId").is_displayed()) print("官网已经打开....") accountName = driver.find_element_by_id('accountName') accountName.send_keys(ACCOUNT) password = driver.find_element_by_id('password') password.send_keys(PASSWORD) driver.find_element_by_id("submit").click() print("输入账号密码..") time.sleep(5) if check_exists_by_xpath(driver, '//button[@formaction="/login/en/authenticator/choose"]'): uasc = driver.find_element_by_xpath('//button[@formaction="/login/en/authenticator/choose"]') uasc.click() element = WebDriverWait(driver, 30).until(lambda x: x.find_element(By.ID, "authValue")) authValue = driver.find_element_by_id('authValue') a = getauth(serial, restoreCode, deviceSecret) authValue.send_keys(a['code']) #输入安全令密钥 print("输入安全令...") driver.find_element_by_id("submit").click() element = WebDriverWait(driver, 30).until(lambda x: x.find_element(By.ID, "app")) print("尝试解除安全令...") #解除安全令 driver.get('https://account.battle.net/security/authenticator/detach') element = WebDriverWait(driver, 30).until(lambda x: x.find_element(By.ID, "attach-detach-confirm")) print("点击解除安全令按钮...") driver.find_element_by_id("attach-detach-confirm").click() element = WebDriverWait(driver, 30).until(lambda x: x.find_element_by_xpath('//input[@maxlength="8"]')) print("输入安全令密钥...") authValue = driver.find_element_by_xpath('//input[@maxlength="8"]') # print(getAllAttr(driver, authValue)) # print(authValue.get_attribute('innerHTML')) a = getauth(serial, restoreCode, deviceSecret) authValue.send_keys(a['code']) #输入安全令密钥 print("解除安全令.按钮查找中.......") btn1 = driver.find_elements_by_class_name("btn-primary")[0] time.sleep(5) # print(getAllAttr(driver, btn1)) print("解除安全令.按钮点击.......") btn1.click() time.sleep(10) check = hasAuth() if("restoreCode" in check): print("解除安全令.失败了.......") else: print("解除安全令.成功.......") except Exception as err: print(f"Unexpected {err}") time.sleep(10) driver.quit() def getAllAttr(driver, element): attr = driver.execute_script('var items = {}; for (index = 0; index < arguments[0].attributes.length; ++index) { items[arguments[0].attributes[index].name] = arguments[0].attributes[index].value }; return items;', element) attr['innerHTML'] = element.get_attribute('innerHTML') return attr def check_exists_by_xpath(driver, xpath): try: driver.find_element_by_xpath(xpath) except NoSuchElementException: return False return True def getauth(serial, restoreCode, deviceSecret): url = "http://192.168.1.99:8080/api/directGetCode" payload = { 'restore': restoreCode, 'serial': serial, 'secret': deviceSecret } headers = { 'user-agent': 'BlizzardSocial/1.21.2 (com.blizzard.social; build:6686; iOS 16.5.1) Alamofire/5.6.4', } response = requests.request("POST", url, headers=headers, data=payload) return response.json() def calc(serial, restoreCode): url = "https://authenticator-rest-api.bnet-identity.blizzard.net/v1/authenticator/device" payload = json.dumps({ "serial": serial, "restoreCode": restoreCode }) headers = { 'authorization': TOKEN, 'user-agent': 'BlizzardSocial/1.21.2 (com.blizzard.social; build:6686; iOS 16.5.1) Alamofire/5.6.4', 'Content-Type': 'application/json' } response = requests.request("POST", url, headers=headers, data=payload) return response.json() def hasAuth(): url = "https://authenticator-rest-api.bnet-identity.blizzard.net/v1/authenticator" headers = { 'authorization': TOKEN, 'user-agent': 'BlizzardSocial/1.21.2 (com.blizzard.social; build:6686; iOS 16.5.1) Alamofire/5.6.4', 'Content-Type': 'application/json' } response = requests.request("GET", url, headers=headers) return response.json() def getSecret(serial, restoreCode): print("try to handel: " + serial + " : " + restoreCode) check = hasAuth() if("restoreCode" in check): print("account has auth with: " + check['serial'] + ", try to unlock..") doGetSecret(check['serial'], check['restoreCode']) return doGetSecret(serial, restoreCode) def doGetSecret(serial, restoreCode): c = calc(serial, restoreCode) if("deviceSecret" in c): a = getauth(serial, restoreCode, c['deviceSecret']) rollback(serial, restoreCode, c['deviceSecret']) return c else: raise Exception("doGetSecret failed: " + str(c)) if __name__ == '__main__': sec = getSecret("US230521228545", "W8M2HRA0EK") print(sec)
#!/usr/bin/python
# -*- coding: UTF-8 -*-


import time
from selenium import webdriver
import requests
import json
from selenium.webdriver.support.wait import WebDriverWait 
from selenium.webdriver.common.by import By
from selenium.common.exceptions import NoSuchElementException


ACCOUNT = ''
PASSWORD = ''
TOKEN = 'Bearer xx'


def rollback(serial, restoreCode, deviceSecret):
    options = webdriver.ChromeOptions()
    options.add_argument('--headless')  # example
    driver = webdriver.Remote("http://127.0.0.1:4444/wd/hub", options=options)

    try:
        # 登录战网
        print("尝试打开官网")
        driver.get('https://account.battle.net/')
        element = WebDriverWait(driver, 30).until(lambda x: x.find_element(By.ID, "accountName")) 
        # is_disappeared = WebDriverWait(driver, 30, 1, (ElementNotVisibleException)).until_not(lambda x: x.find_element(By.ID, "someId").is_displayed())
        print("官网已经打开....")
        accountName = driver.find_element_by_id('accountName')
        accountName.send_keys(ACCOUNT)
        password = driver.find_element_by_id('password')
        password.send_keys(PASSWORD)
        driver.find_element_by_id("submit").click()

        print("输入账号密码..")
        time.sleep(5)
        if check_exists_by_xpath(driver, '//button[@formaction="/login/en/authenticator/choose"]'):
            uasc = driver.find_element_by_xpath('//button[@formaction="/login/en/authenticator/choose"]')
            uasc.click()

        element = WebDriverWait(driver, 30).until(lambda x: x.find_element(By.ID, "authValue")) 
        authValue = driver.find_element_by_id('authValue')
        a = getauth(serial, restoreCode, deviceSecret)
        authValue.send_keys(a['code']) #输入安全令密钥
        print("输入安全令...")
        driver.find_element_by_id("submit").click()


        element = WebDriverWait(driver, 30).until(lambda x: x.find_element(By.ID, "app")) 
        print("尝试解除安全令...")
        #解除安全令
        driver.get('https://account.battle.net/security/authenticator/detach')
        element = WebDriverWait(driver, 30).until(lambda x: x.find_element(By.ID, "attach-detach-confirm")) 
        print("点击解除安全令按钮...")
        driver.find_element_by_id("attach-detach-confirm").click()
        element = WebDriverWait(driver, 30).until(lambda x: x.find_element_by_xpath('//input[@maxlength="8"]')) 

        print("输入安全令密钥...")
        authValue = driver.find_element_by_xpath('//input[@maxlength="8"]')
        # print(getAllAttr(driver, authValue))
        # print(authValue.get_attribute('innerHTML'))
        a = getauth(serial, restoreCode, deviceSecret)
        authValue.send_keys(a['code']) #输入安全令密钥

        print("解除安全令.按钮查找中.......")
        btn1 = driver.find_elements_by_class_name("btn-primary")[0]
        time.sleep(5)
        # print(getAllAttr(driver, btn1))
        print("解除安全令.按钮点击.......")
        btn1.click()
        time.sleep(10)
        check = hasAuth()
        if("restoreCode" in check):
             print("解除安全令.失败了.......")
        else:
            print("解除安全令.成功.......")
    except Exception as err:
        print(f"Unexpected {err}")
    time.sleep(10)
    driver.quit()

def getAllAttr(driver, element):
    attr = driver.execute_script('var items = {}; for (index = 0; index < arguments[0].attributes.length; ++index) { items[arguments[0].attributes[index].name] = arguments[0].attributes[index].value }; return items;', element)
    attr['innerHTML'] = element.get_attribute('innerHTML')
    return attr

def check_exists_by_xpath(driver, xpath):
    try:
        driver.find_element_by_xpath(xpath)
    except NoSuchElementException:
        return False
    return True

def getauth(serial, restoreCode, deviceSecret):
    url = "http://192.168.1.99:8080/api/directGetCode"

    payload = {
            'restore': restoreCode,
            'serial': serial,
            'secret': deviceSecret
            }

    headers = {
        'user-agent': 'BlizzardSocial/1.21.2 (com.blizzard.social; build:6686; iOS 16.5.1) Alamofire/5.6.4',
    }

    response = requests.request("POST", url, headers=headers, data=payload)

    return response.json()

def calc(serial, restoreCode):
    url = "https://authenticator-rest-api.bnet-identity.blizzard.net/v1/authenticator/device"

    payload = json.dumps({
        "serial": serial,
        "restoreCode": restoreCode
    })
    headers = {
        'authorization': TOKEN,
        'user-agent': 'BlizzardSocial/1.21.2 (com.blizzard.social; build:6686; iOS 16.5.1) Alamofire/5.6.4',
        'Content-Type': 'application/json'
    }
    response = requests.request("POST", url, headers=headers, data=payload)
    return response.json()

def hasAuth():
    url = "https://authenticator-rest-api.bnet-identity.blizzard.net/v1/authenticator"

    headers = {
        'authorization': TOKEN,
        'user-agent': 'BlizzardSocial/1.21.2 (com.blizzard.social; build:6686; iOS 16.5.1) Alamofire/5.6.4',
        'Content-Type': 'application/json'
    }
    response = requests.request("GET", url, headers=headers)
    return response.json()

def getSecret(serial, restoreCode):
    print("try to handel: " + serial + " : " + restoreCode)
    check = hasAuth()
    if("restoreCode" in check):
        print("account has auth with: " + check['serial'] + ", try to unlock..")
        doGetSecret(check['serial'], check['restoreCode'])
    return doGetSecret(serial, restoreCode)

def doGetSecret(serial, restoreCode):
    c = calc(serial, restoreCode)
    if("deviceSecret" in c):
        a = getauth(serial, restoreCode, c['deviceSecret'])
        rollback(serial, restoreCode, c['deviceSecret'])
        return c
    else:
        raise Exception("doGetSecret failed: " + str(c))


if __name__ == '__main__':
    sec = getSecret("US230521228545", "W8M2HRA0EK")
    print(sec)

参考资料:

https://stackoverflow.com/questions/45323271/how-to-run-selenium-with-chrome-in-docker

4 Comments

    • admin
      admin 2023年9月10日

      重新研究暴雪api呗,自己研究。

  1. 喜乐
    喜乐 2023年12月3日

    老哥用密钥如何获取安全令验证码啊,这个代码在哪里能得到

    • 喜乐
      喜乐 2023年12月4日

      已经会了,感谢你

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注