├── wqrfnium ├── __init__.py ├── wqrfnium.ini ├── test.py └── wqrfnium.py └── README.md /wqrfnium/__init__.py: -------------------------------------------------------------------------------- 1 | #wqrf -------------------------------------------------------------------------------- /wqrfnium/wqrfnium.ini: -------------------------------------------------------------------------------- 1 | [Excel] 2 | elements_xls_path = /Users/zijiawang/elements.xls 3 | 4 | -------------------------------------------------------------------------------- /wqrfnium/test.py: -------------------------------------------------------------------------------- 1 | from selenium import webdriver 2 | from wqrfnium import * 3 | 4 | begin_wqrf('./MyElements2.xls') 5 | 6 | driver = webdriver.Chrome() 7 | driver.get("http://www.baidu.com/") 8 | time.sleep(2) 9 | getelement(driver,"seachinput").send_keys('xiaozhu') 10 | 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wqrf_selenium 简称 wqrfnium 2 | 封装的selenium和po模式,可自动维护元素和减少前端ui修改带来的麻烦工作量 3 | --------- 4 | ## 最新更新: 5 | 增加api获取/更新 自动维护元素 的方式,可用 from wqrfnium.wqrfnium_api import * 替换 from wqrfnium.wqrfnium import * 6 | 具体使用方式请细读 使用方法-api 7 | ## 您需要注意的部分: 8 | wqrfnium会自动生成一个excel表,并打印表位置,您需要把您selenium脚本中经常容易因前端变化导致定位失败的元素放入此表中 9 | 每行一个元素,列含义: 10 | 元素标识-icon:用户自行输入(如:seach_input/my_username),之后脚本中getelement方法中需要传入driver和icon 11 | 默认定位方式-tmp_find_method:用户需自行输入初始(如id/name等),之后脚本会自行维护无需再度关心 12 | 默认定位值-tmp_find_value:用户需自行输入初始(如username/password/kw/login等),之后脚本会自行维护无需再度关心 13 | 下标-index:用户自行输入初始(如0/1/2/3....),之后脚本会自行维护无需再度关心 14 | 原始html标签内容-html_element-:系统自动生成,无需关心 15 | ## 原理: 16 | selenium定位时默认先利用excel表中的默认定位方式和默认值定位,若定位失败,则启动自动定位算法,找到最符合要求的元素返回,并把新元素的tagname定位方式和内容写入excel表中以便下次调用。 17 | ## 优点: 18 | 1.使用简单,只需要变化定位语句即可。 19 | 2.源码简单,方便进行二次开发。 20 | 3.其中的所有分数权重参数可自行根据公司项目风格更改,来达到99%以上的成功率。 21 | 4.博主更新快,框架优化和前景非常nice。 22 | 5.可稍加变化应用到appium中 23 | ## 下载方法: 24 | 1.可以用pip install wqrfnium 25 | 2.可以download本项目 26 | ## 使用方法-exlce: 27 | 1.示范代码: 28 | 29 | from selenium import webdriver 30 | from wqrfnium.wqrfnium import * 31 | begin_wqrf('./MyElements.xls') 32 | driver = webdriver.Chrome() 33 | driver.get("http://www.baidu.com/") 34 | time.sleep(2) 35 | getelement(driver,"seachinput").send_keys('xiaozhu') 36 | 2.首次运行一下,会自动生成存放elements.xls文件,会打印出此文件地址(begin_wqrf()为初始化语句,可传入自定义的excel表路径,若不写则会在默认位置生成) 37 | 3.手动进入elements.xls,把要定位的页面元素手动输入定位方式和定位值,粘贴到excel表中,每行一个元素 38 | 第一列:元素的标识,用于之后代码中直接调用该元素,如示范代码中的“seachinput” 39 | 第二列:元素的默认定位方式,如id 40 | 第三列:元素的默认定位值,如 kw 41 | 第四列:元素的下标,一般都写0,算法获取元素是获取符合要求的所有元素 42 | 第五列:元素的html源码标签,无需注意,由系统自动生成。 43 | 4.在代码中调用getelement方法,传入driver和元素标识即可,后续前端页面的各种更改,这个定位代码都会成功找到 44 | ## 使用方法-api: 45 | 1.示范代码: 46 | 47 | from selenium import webdriver 48 | from wqrfnium_api import * 49 | get_api_url = "http://xxx.xxx.xxx/aaa/get_element_test/***/" 50 | update_api_url = "http://xxx.xxx.xxx/bbb/update_element_test/***/" 51 | begin_wqrf(get_api_url,update_api_url) 52 | driver = webdriver.Chrome() 53 | driver.get("http://www.baidu.com/") 54 | time.sleep(2) 55 | getelement(driver,"searchinput").send_keys('xiaozhu') 56 | 2.首次运行会把配置写入配置文件,之后无需再加入begin_wqrf()方法 57 | 3.您的获取/更新元素接口需要满足如下要求 58 | 获取元素的api: 59 | 1.url 中必须有***来占位,这个***就是后来会替换成元素的icon 60 | 2.必为get 61 | 3.返回值根路径必须含有元素的五种属性即:{“icon”:"",“tmp_find_method”:"",“tmp_find_value”:"",“index”:"",“html_element”:"",} 62 | 更新元素的api: 63 | 1.url 中必须有***来占位,这个***就是后来会替换成元素的icon 64 | 2.必为post 65 | 3.请求体根路径必须含有元素的五种属性即:{“tmp_find_method”:"",“tmp_find_value”:"",“index”:"",“html_element”:"",} 66 | 具体使用帮助可参考博文:https://blog.csdn.net/qq_22795513/article/details/103182097 67 | 68 | ## 依赖包: 69 | 1.selenium 70 | 2.python-Levenshtein 71 | 3.python2/3 72 | 4.xlrd 73 | 5.xlutils 74 | 6.configparser 75 | 7.requests 76 | ## 联系作者: 77 | qq:1074321997 78 | 79 | ## 历史更新: 80 | 修复部分用户自动生成的xls文件打不开问题 81 | 增加可自动移excle表位置代码:begin_wqrf('./MyElements.xls') 82 | 新增首次无需手动粘贴html_element字段,系统会自动生成。 83 | 同时支持py2,py3 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /wqrfnium/wqrfnium.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import os,sys 3 | import re,time 4 | import Levenshtein 5 | import xlrd,xlwt 6 | from xlutils.copy import copy 7 | import os,platform 8 | import configparser 9 | try: 10 | reload(sys) 11 | sys.setdefaultencoding('utf-8') 12 | except: 13 | pass 14 | #---------------------------------- 15 | 16 | # diy your elements_xls_path 17 | 18 | def create_xls(elements_xls_path): 19 | if not os.path.exists(elements_xls_path): 20 | book = xlwt.Workbook(encoding='utf-8',style_compression=0) 21 | book.add_sheet('Sheet1',cell_overwrite_ok=True) 22 | book.save(elements_xls_path) 23 | 24 | def get_elements(icon): 25 | try: 26 | Data = xlrd.open_workbook(elements_xls_path) 27 | except Exception: 28 | print('Please put the element into the elements.xls first!') 29 | print('First column:icon,Second column:tmp_find_method,Third column:tmp_find_value,Fourth column:index,Fifth column:html_element') 30 | print('For example:seachinput,id,kw,0,') 31 | exit(0) 32 | table = Data.sheet_by_name("Sheet1") 33 | nrows = table.nrows 34 | for i in range(nrows): 35 | element_tmp = table.cell(i,0).value 36 | if element_tmp == icon: 37 | try: 38 | html_element = table.cell(i,4).value 39 | except: 40 | html_element = '' 41 | return [table.cell(i,1).value,table.cell(i,2).value,int(table.cell(i,3).value),html_element,i] 42 | print('not fonund the element: [ %s ],please fixed it by yourself...'%icon) 43 | exit(0) 44 | 45 | def update_elements(id,html,tmp,tmp_value,index): 46 | Data = xlrd.open_workbook(elements_xls_path) 47 | ww = copy(Data) 48 | ww.get_sheet(0).write(id, 1,tmp) 49 | ww.get_sheet(0).write(id, 2,tmp_value) 50 | ww.get_sheet(0).write(id, 3,index) 51 | ww.get_sheet(0).write(id, 4,html) 52 | os.remove(elements_xls_path) 53 | ww.save(elements_xls_path) 54 | 55 | def input_html_element(id,html): 56 | Data = xlrd.open_workbook(elements_xls_path) 57 | ww = copy(Data) 58 | ww.get_sheet(0).write(id, 4, html) 59 | os.remove(elements_xls_path) 60 | ww.save(elements_xls_path) 61 | 62 | def likescore(oldstr,newstr): 63 | score = Levenshtein.ratio(str(oldstr), str(newstr)) 64 | return score 65 | 66 | def search_new(driver,old_html): 67 | try:old_id = re.findall(r'id="(.*?)"',old_html)[0] 68 | except:old_id = None 69 | try:old_name = re.findall(r'name="(.*?)"',old_html)[0] 70 | except:old_name=None 71 | try:old_class = re.findall(r'class="(.*?)"',old_html)[0] 72 | except:old_class=None 73 | try:old_text = re.findall(r'>(.*?)<',old_html)[0] 74 | except:old_text='' 75 | try:old_value = re.findall(r'value="(.*?)"',old_html)[0] 76 | except:old_value='' 77 | try:old_onclick = re.findall(r'onclick="(.*?)"',old_html)[0] 78 | except:old_onclick=None 79 | try:old_style = re.findall(r'style="(.*?)"',old_html)[0] 80 | except:old_style='' 81 | try:old_placeholder = re.findall(r'placeholder="(.*?)"', old_html)[0] 82 | except:old_placeholder=None 83 | try:old_href = re.findall(r'href="(.*?)"',old_html)[0] 84 | except:old_href=None 85 | try:old_type = re.findall(r'type="(.*?)"',old_html)[0] 86 | except:old_type = None 87 | #--------------------------------------------------------get all par 88 | bq = re.findall(r'<(.+?) ',old_html)[0] 89 | new_elements = driver.find_elements_by_tag_name(bq) 90 | end_element = new_elements[0] 91 | end_index = 0 92 | tmp_score = 0 93 | for i in range(len(new_elements)): 94 | score = 0 95 | new_id = new_elements[i].get_attribute("id") 96 | new_name = new_elements[i].get_attribute("name") 97 | new_class = new_elements[i].get_attribute("class") 98 | new_text = new_elements[i].text 99 | new_value = new_elements[i].get_attribute("value") 100 | new_onclick = new_elements[i].get_attribute("onclick") 101 | new_style = new_elements[i].get_attribute("style") 102 | new_placeholder = new_elements[i].get_attribute("placeholder") 103 | new_href = new_elements[i].get_attribute("href") 104 | try:new_type = re.findall(r'type="(.*?)"',new_elements[i].get_attribute("outerHTML"))[0] 105 | except:new_type = None 106 | score += likescore(old_id, new_id) 107 | score += likescore(old_name, new_name) 108 | score += likescore(old_class, new_class) 109 | score += likescore(old_text, new_text) 110 | score += likescore(old_value, new_value) 111 | score += likescore(old_onclick, new_onclick) 112 | score += likescore(str(old_style).replace(' ',''), str(new_style).replace(' ','')) 113 | score += likescore(old_placeholder, new_placeholder) 114 | score += likescore(old_href, new_href) 115 | score += likescore(old_type,new_type) 116 | if score > tmp_score: 117 | end_element = new_elements[i] 118 | end_index = i 119 | tmp_score = score 120 | new_html = end_element.get_attribute("outerHTML") 121 | new_tmp = 'tag name' #use id,name 122 | new_tmp_value = bq 123 | new_index = end_index 124 | return [end_element,new_html,new_tmp,new_tmp_value,new_index] 125 | 126 | def getelement(driver,icon): 127 | time1 = time.time() 128 | element = get_elements(icon) 129 | if element == 'error': 130 | raise Exception 131 | print('find: %s ...'%icon) 132 | old_html = element[3] 133 | try: 134 | el = driver.find_elements(element[0],element[1])[element[2]] 135 | print('success in %s s'%str(time.time()-time1)[:5]) 136 | if old_html == '': 137 | html_element = el.get_attribute("outerHTML") 138 | input_html_element(element[-1],html_element) 139 | return el 140 | except Exception: 141 | print('find_faild,begin fix....') 142 | newel_detail = search_new(driver,old_html) 143 | newel = newel_detail[0] 144 | new_html = newel_detail[1] 145 | new_tmp = newel_detail[2] 146 | new_tmp_value = newel_detail[3] 147 | new_index = newel_detail[4] 148 | update_elements(element[4],html=new_html,tmp=new_tmp,tmp_value=new_tmp_value,index=new_index) 149 | print('find success in %s s'%str(time.time()-time1)[:5]) 150 | return newel 151 | 152 | try: 153 | cfp = configparser.ConfigParser() 154 | cfp.read('wqrfnium.ini') 155 | elements_xls_path = cfp.get('Excel','elements_xls_path') 156 | except: # create wqrfnium.ini 157 | cfp = configparser.ConfigParser() 158 | cfp["Excel"] = {"elements_xls_path":""} 159 | with open('wqrfnium.ini','w') as fp: 160 | cfp.write(fp) 161 | elements_xls_path = cfp.get('Excel','elements_xls_path') 162 | 163 | def begin_wqrf(path): 164 | global elements_xls_path 165 | if 'xls' not in path.split('.')[-1]: 166 | if path[-1] == '/': 167 | path += 'elements.xls' 168 | else: 169 | path += '/elements.xls' 170 | if elements_xls_path != path: 171 | print("----------------------------------") 172 | print("You are changeing the elements_xls_path,the new path is %s now!"%path) 173 | print("You'd better handle the old elements_xls : %s by yourself."%elements_xls_path) 174 | create_xls(path) 175 | cfp.set("Excel","elements_xls_path",path) 176 | with open("wqrfnium.ini","w+") as f: 177 | cfp.write(f) 178 | elements_xls_path = path 179 | 180 | 181 | if elements_xls_path == '': #no path 182 | # begin to set the elements 183 | if 'arwin' in platform.system() or 'inux' in platform.system() : 184 | elements_xls_path =os.environ['HOME']+"/elements.xls" 185 | else: 186 | elements_xls_path = "C:\\elements.xls" 187 | print('You are first use wqrfnium,it is creating elements.xls,you must edit elements.xls and play wqrfnium after!') 188 | print('Your elements.xls tmp path is %s' % elements_xls_path) 189 | print("First colum is element's icon,second is element's tmp_find_method,third is element's tmp_find_value,forth is element's index,the last is element's html_element") 190 | print("You can also read the README to get help or wirte email to 1074321997@qq.com") 191 | print('You can use code [begin_wqrf("your diy new elements_xls_path ")] to diy your elements_xls_path!') 192 | create_xls(elements_xls_path) 193 | cfp.set("Excel", "elements_xls_path", elements_xls_path) 194 | with open("wqrfnium.ini", "w+") as f: 195 | cfp.write(f) 196 | 197 | 198 | elif elements_xls_path == os.environ['HOME']+"/elements.xls" or elements_xls_path == "C:\\elements.xls": # default path 199 | print('Your elements.xls tmp path is default : %s'%elements_xls_path) 200 | 201 | else : #diy path 202 | print('Your elements.xls tmp path is diy by yourself : %s' % elements_xls_path) --------------------------------------------------------------------------------