来点接地气的,继续部门的老 web 自动化框架解读,这次里面一些偏方

windows 环境在启动自动化前的清理工作

因为自动化是根据不同的 hosts 来确定对应版本环境,所以要进行清理上次运行的代理、dns 缓存,于是有以下代码片段(修改注册表)

import os
cmd_switchProxy="chcp 65001 & reg add \"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\" /v ProxyEnable /t REG_DWORD /d 0 /f & "+ \
                            "reg add \"HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\" /v ProxyServer /d \"\" /f & "+ \
                            "ipconfig /flushdns"
with os.popen(cmd_switchProxy,"r") as f: print('清理系统代理及dns缓存:',f.read())

windows 环境关闭掉 chrome 的 trigger 页面

chromedriver 打开 chrome 默认会打开一个 trigger 页面,让人心烦,同样也是修改注册表来关闭它

import os
cmd_closeTrigger="chcp 65001 &  reg delete HKEY_CURRENT_USER\Software\Google\Chrome\TriggeredReset /f"
with os.popen(cmd_closeTrigger,"r") as f:print('关闭chrome的Trigger页面:',f.read())

模拟 ajax

webdriver 可以执行 js 和异步 js,但貌似无法执行 ajax,于是有如下模拟 ajax 的方法。
方法比较简单:利用 requests 库 +webdriver 获取 cookie 字符串 + 特殊请求头而已,这样可以模拟 ajax 执行一些请求了

import requests
def getAjaxedRequsetSession(driver:webdriver,proxy_ip:str,proxy_port:int) -> requests.Session:
        cookies=driver.get_cookies()
        cookieString="; ".join([c["name"]+"="+c["value"] for c in Cookie])
        requestSession=requests.Session()
        requestSession.trust_env=False
        requestSession.headers.update(
            {
                'User-Agent': "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4478.0 Safari/537.36",
                'Cookie': cookieString,
                'x-requested-with':'XMLHttpRequest',
                'Sec-Fetch-Site': 'same-origin'
             }
        )
        if proxy_ip and proxy_port:
            requestSession.proxies={
                'http':'http://%s:%d'%(proxy_ip,proxy_port),
                'https':'https://%s:%d'%(proxy_ip,proxy_port)
            }
        return requestSession

精确的转到新打开的窗口

一些人转到新打开的窗口都是通过 title 来定位,但如果 title 重复、没有 title 咋办?
方法也比较简单:先获取当前窗口的句柄列表,执行某个操作后,再获取之后的的窗口句柄列表,找到多出来的那个就好了。
打开新窗口一般都是点击操作,而且一般是打开一个窗口(其他情况是反人类的)
以下方法 通过点击元素跳转到新的窗口,并返回原窗口的句柄,便于操作完成返回原先窗口

def switchToNewWindowByClick(driver:webdriver,element:WebElement):
        """
        通过点击元素跳转到新的窗口
        :param element:
        :return:
        """
        currentHandle = driver.current_window_handle
        preAllHandles = set(driver.window_handles)
        element.click()
        time.sleep(5)
        nxtAllHandles = set(driver.window_handles)
        newHandles=nxtAllHandles.difference(preAllHandles)
        if len(newHandles)<1:
            return False,currentHandle
        newWindow=newHandles.pop()
        try:
            driver.switch_to.window(newWindow)
            return True,currentHandle
        except NoSuchWindowException as nswe:
            print(nswe.msg)
            traceback.print_exc()
            return False,currentHandle

chrome 和 firefox 的忽略证书错误

测试环境的 ssl 证书错误比较烦人,一般要忽略证书错误来做,方法也能百度到,如下:
firefox 的

GeckodriverDriverPath="Geckodriver的路径"
firefoxProfiles=[
    ("network.http.phishy-userpass-length", 255),
    ("webdriver_assume_untrusted_issuer",False),
]
firefox_profile = webdriver.FirefoxProfile()
for profile in firefoxProfiles:
    firefox_profile.set_preference(profile[0],profile[1])
driver = webdriver.Firefox(executable_path=GeckodriverDriverPath,firefox_profile=firefox_profile)

chrome 的

ChromeDriverPath="chromedriver的路径"
chromeOptions=['--demo-type','--ignore-certificate-errors']
for option in chromeOptions:
    chrome_options.add_argument(option)
driver = webdriver.Chrome(executable_path=ChromeDriverPath,options=chrome_options)

xpath 的文本中包含单引号 (') 和双引号 (") 问题

假如有这样一个元素:<div> "双",'单' </div>。那 xpath 应该怎样写?
【1】是//div[contains(text(),"A")]或者//div[contains(text(),'B')]格式吗
A、B 中内容有单双引号,与包裹他们的双、单引号是冲突的,xpath 中可不认转义符\"或\',所以不可行
【2】投机取巧//div[contains(text(),'"双",') and contains(text(),"'单'") ]
这种方法只能特殊情况特殊处理,不具备普遍性,不适合用于自动化框架中
【3】利用 concat 函数是正确方法,如://div[contains(text(),concat('"双"',",'单'")) ]
生成的字符串方式会很多,通用方法是将单/双引号做分割符,然后再拼接。于是有了如下方法:

def fixXpathText(text:str):
    """
    处理用xpath文本使用text()定位时,里面包含单引号、双引号情况
    生成xpath中的字符串
    :param text:
    :return:
    """
    x=text.split('"')
    if len(x)==1:
        return "\""+text+"\""
    y=[]
    for s in x[:-1]:
        y.append(s)
        y.append('"')
    y.append(x[-1])
    ts="concat("
    te=")"
    for t in y[:-1]:
        if "\""==t:
            ts+="'\"',"
            continue
        ts+="\""+t+"\","
    if "\""==y[-1]:
        ts+="'\"',"
    else:
        ts+="\""+y[-1]+"\""
    return ts+te

效果如下(虽不精炼,但够用)


↙↙↙阅读原文可查看相关链接,并与作者交流