用selenium做自动化,有时候会遇到需要模拟鼠标 *** 作才能进行的情况,比如单击、双击、点击鼠标右键、拖拽等等。而selenium给我们提供了一个类来处理这类事件——ActionChains
seleniumwebdrivercommonaction_chainsActionChains(driver)
这个类基本能够满足我们所有对鼠标 *** 作的需求。
1ActionChains基本用法
首先需要了解ActionChains的执行原理,当你调用ActionChains的方法时,不会立即执行,而是会将所有的 *** 作按顺序存放在一个队列里,当你调用perform()方法时,队列中的时间会依次执行。
这种情况下我们可以有两种调用方法:
链式写法
menu = driverfind_element_by_css_selector("nav")hidden_submenu = driverfind_element_by_css_selector("nav #submenu1")
ActionChains(driver)move_to_element(menu)click(hidden_submenu)perform()
1
2
3
4
分步写法
menu = driverfind_element_by_css_selector("nav")hidden_submenu = driverfind_element_by_css_selector("nav #submenu1")
actions = ActionChains(driver)
actionsmove_to_element(menu)
actionsclick(hidden_submenu)
actionsperform()
1
2
3
4
5
6
7
两种写法本质是一样的,ActionChains都会按照顺序执行所有的 *** 作。
2ActionChains方法列表
click(on_element=None) ——单击鼠标左键
click_and_hold(on_element=None) ——点击鼠标左键,不松开
context_click(on_element=None) ——点击鼠标右键
double_click(on_element=None) ——双击鼠标左键
drag_and_drop(source, target) ——拖拽到某个元素然后松开
drag_and_drop_by_offset(source, xoffset, yoffset) ——拖拽到某个坐标然后松开
key_down(value, element=None) ——按下某个键盘上的键
key_up(value, element=None) ——松开某个键
move_by_offset(xoffset, yoffset) ——鼠标从当前位置移动到某个坐标
move_to_element(to_element) ——鼠标移动到某个元素
move_to_element_with_offset(to_element, xoffset, yoffset) ——移动到距某个元素(左上角坐标)多少距离的位置
perform() ——执行链中的所有动作
release(on_element=None) ——在某个元素位置松开鼠标左键
send_keys(keys_to_send) ——发送某个键到当前焦点的元素
send_keys_to_element(element, keys_to_send) ——发送某个键到指定元素
接下来用示例来详细说明和演示每一个方法的用法:
3代码示例
1 点击 *** 作
代码:
# -- coding: utf-8 --from selenium import webdriverfrom seleniumwebdrivercommonaction_chains import ActionChainsfrom time import sleepdriver = webdriverFirefox()
driverimplicitly_wait(10)
drivermaximize_window()
driverget('')
click_btn = driverfind_element_by_xpath('//input[@value="click me"]') # 单击按钮doubleclick_btn = driverfind_element_by_xpath('//input[@value="dbl click me"]') # 双击按钮rightclick_btn = driverfind_element_by_xpath('//input[@value="right click me"]') # 右键单击按钮ActionChains(driver)click(click_btn)double_click(doubleclick_btn)context_click(rightclick_btn)perform() # 链式用法print driverfind_element_by_name('t2')get_attribute('value')
sleep(2)
driverquit()1234567891011121314151617181920212223
结果:
[CLICK][DOUBLE_CLICK][RIGHT_CLICK]12鼠标移动
示例代码:
# -- coding: utf-8 --from selenium import webdriverfrom seleniumwebdrivercommonaction_chains import ActionChainsfrom time import sleepdriver = webdriverFirefox()
driverimplicitly_wait(10)
drivermaximize_window()
driverget('')
write = driverfind_element_by_xpath('//input[@value="Write on hover"]') # 鼠标移动到此元素,在下面的input框中会显示“Mouse moved”blank = driverfind_element_by_xpath('//input[@value="Blank on hover"]') # 鼠标移动到此元素,会清空下面input框中的内容result = driverfind_element_by_name('t1')
action = ActionChains(driver)
actionmove_to_element(write)perform() # 移动到write,显示“Mouse moved”print resultget_attribute('value')# actionmove_to_element(blank)perform()actionmove_by_offset(10, 50)perform() # 移动到距离当前位置(10,50)的点,与上句效果相同,移动到blank上,清空print resultget_attribute('value')
actionmove_to_element_with_offset(blank, 10, -40)perform() # 移动到距离blank元素(10,-40)的点,可移动到write上print resultget_attribute('value')
sleep(2)
driverquit()1234567891011121314151617181920212223242526272829
结果
Mouse movedMouse moved123
一般很少用位置关系来移动鼠标,如果需要,可参考下面的链接来测量元素位置
3拖拽
代码:
# -- coding: utf-8 --from selenium import webdriverfrom seleniumwebdrivercommonaction_chains import ActionChainsfrom time import sleepdriver = webdriverFirefox()
driverimplicitly_wait(10)
drivermaximize_window()
driverget('lshtm')
dragger = driverfind_element_by_id('dragger') # 被拖拽元素item1 = driverfind_element_by_xpath('//div[text()="Item 1"]') # 目标元素1item2 = driverfind_element_by_xpath('//div[text()="Item 2"]') # 目标2item3 = driverfind_element_by_xpath('//div[text()="Item 3"]') # 目标3item4 = driverfind_element_by_xpath('//div[text()="Item 4"]') # 目标4action = ActionChains(driver)
actiondrag_and_drop(dragger, item1)perform() # 1移动dragger到目标1sleep(2)
actionclick_and_hold(dragger)release(item2)perform() # 2效果与上句相同,也能起到移动效果sleep(2)
actionclick_and_hold(dragger)move_to_element(item3)release()perform() # 3效果与上两句相同,也能起到移动的效果sleep(2)# actiondrag_and_drop_by_offset(dragger, 400, 150)perform() # 4移动到指定坐标actionclick_and_hold(dragger)move_by_offset(400, 150)release()perform() # 5与上一句相同,移动到指定坐标sleep(2)
driverquit()12345678910111213141516171819202122232425262728
结果:
dropped dropped dropped dropped1一般用坐标定位很少,用上例中的方法1足够了,如果看源码,会发现方法2其实就是方法1中的drag_and_drop()的实现。注意:拖拽使用时注意加等待时间,有时会因为速度太快而失败。
4按键
模拟按键有多种方法,能用win32api来实现,能用SendKeys来实现,也可以用selenium的WebElement对象的send_keys()方法来实现,这里ActionChains类也提供了几个模拟按键的方法。
代码1:
# -- coding: utf-8 --from selenium import webdriverfrom seleniumwebdrivercommonaction_chains import ActionChainsfrom time import sleepdriver = webdriverFirefox()
driverimplicitly_wait(10)
drivermaximize_window()
driverget('presshtm')
key_up_radio = driverfind_element_by_id('r1') # 监测按键升起key_down_radio = driverfind_element_by_id('r2') # 监测按键按下key_press_radio = driverfind_element_by_id('r3') # 监测按键按下升起enter = driverfind_elements_by_xpath('//form[@name="f1"]/input')[1] # 输入框result = driverfind_elements_by_xpath('//form[@name="f1"]/input')[0] # 监测结果# 监测key_downkey_down_radioclick()
ActionChains(driver)key_down(KeysCONTROL, enter)key_up(KeysCONTROL)perform()print resultget_attribute('value')# 监测key_upkey_up_radioclick()
enterclick()
ActionChains(driver)key_down(KeysSHIFT)key_up(KeysSHIFT)perform()print resultget_attribute('value')# 监测key_presskey_press_radioclick()
enterclick()
ActionChains(driver)send_keys('a')perform()print resultget_attribute('value')
driverquit()1234567891011121314151617181920212223242526272829303132333435
结果:
key downed charCode=[0] keyCode=[17] CTRLkey upped charCode=[0] keyCode=[16] NONEkey pressed charCode=[97] keyCode=[0] NONE123示例2:
代码:
# -- coding: utf-8 --from selenium import webdriverfrom seleniumwebdrivercommonaction_chains import ActionChainsfrom seleniumwebdrivercommonkeys import Keysfrom time import sleepdriver = webdriverFirefox()
driverimplicitly_wait(10)
drivermaximize_window()
driverget('helhtm')
input1 = driverfind_elements_by_tag_name('input')[3]
input2 = driverfind_elements_by_tag_name('input')[4]
action = ActionChains(driver)
input1click()
actionsend_keys('Test Keys')perform()
actionkey_down(KeysCONTROL)send_keys('a')key_up(KeysCONTROL)perform() # ctrl+aactionkey_down(KeysCONTROL)send_keys('c')key_up(KeysCONTROL)perform() # ctrl+cactionkey_down(KeysCONTROL, input2)send_keys('v')key_up(KeysCONTROL)perform() # ctrl+vprint input1get_attribute('value')print input2get_attribute('value')
driverquit()12345678910111213141516171819202122232425262728
结果:
Test KeysTest Keys12
复制粘贴用WebElement< input >send_keys()也能实现,大家可以试一下,也可以用更底层的方法,同时也是osd框的处理办法之一的win32api,有兴趣也可以试试SendKeys、keybd_event
一、什么是Selenium
selenium 是一套完整的web应用程序测试系统,包含了测试的录制(selenium IDE),编写及运行(Selenium Remote Control)和测试的并行处理(Selenium Grid)。Selenium的核心Selenium Core基于JsUnit,完全由JavaScript编写,因此可以用于任何支持JavaScript的浏览器上。
selenium可以模拟真实浏览器,自动化测试工具,支持多种浏览器,爬虫中主要用来解决JavaScript渲染问题。
二、selenium基本使用
用python写爬虫的时候,主要用的是selenium的Webdriver,我们可以通过下面的方式先看看SeleniumWebdriver支持哪些浏览器
执行结果如下,从结果中我们也可以看出基本山支持了常见的所有浏览器:
这里要说一下比较重要的PhantomJS,PhantomJS是一个而基于WebKit的服务端JavaScript API,支持Web而不需要浏览器支持,其快速、原生支持各种Web标准:Dom处理,CSS选择器,JSON等等。PhantomJS可以用用于页面自动化、网络监测、网页截屏,以及无界面测试
声明浏览器对象
上面我们知道了selenium支持很多的浏览器,但是如果想要声明并调用浏览器则需要:
from selenium import webdriver
browser = webdriverChrome()
browser = webdriverFirefox()
这里只写了两个例子,当然了其他的支持的浏览器都可以通过这种方式调用
访问页面
from selenium import webdriver
browser = webdriverChrome()
browserget("httiducom")print(browserpage_source)
browserclose()
上述代码运行后,会自动打开Chrome浏览器,并登陆百度打印百度首页的源代码,然后关闭浏览器
查找元素
单个元素查找
from selenium import webdriver
browser = webdriverChrome()
browserget("baocom")
input_first = browserfind_element_by_id("q")
input_second = browserfind_element_by_css_selector("#q")
input_third = browserfind_element_by_xpath('//[@id="q"]')print(input_first)print(input_second)print(input_third)
browserclose()
这里我们通过三种不同的方式去获取响应的元素,第一种是通过id的方式,第二个中是CSS选择器,第三种是xpath选择器,结果都是相同的。
结果如下:
这里列举一下常用的查找元素方法:
find_element_by_name
find_element_by_id
find_element_by_xpath
find_element_by_link_text
find_element_by_partial_link_text
find_element_by_tag_name
find_element_by_class_name
find_element_by_css_selector
下面这种方式是比较通用的一种方式:这里需要记住By模块所以需要导入
from seleniumwebdrivercommonby import By
from selenium import webdriverfrom seleniumwebdrivercommonby import By
browser = webdriverChrome()
browserget("obaocom")
input_first = browserfind_element(ByID,"q")print(input_first)
browserclose()
当然这种方法和上述的方式是通用的,browserfind_element(ByID,"q")这里ByID中的ID可以替换为其他几个
多个元素查找
其实多个元素和单个元素的区别,举个例子:find_elements,单个元素是find_element,其他使用上没什么区别,通过其中的一个例子演示:
from selenium import webdriver
browser = webdriverChrome()
browserget("obaocom")
lis = browserfind_elements_by_css_selector('service-bd li')print(lis)
browserclose()
这样获得就是一个列表
当然上面的方式也是可以通过导入from seleniumwebdrivercommonby import By 这种方式实现
lis = browserfind_elements(ByCSS_SELECTOR,'service-bd li')
同样的在单个元素中查找的方法在多个元素查找中同样存在:
find_elements_by_name
find_elements_by_id
find_elements_by_xpath
find_elements_by_link_text
find_elements_by_partial_link_text
find_elements_by_tag_name
find_elements_by_class_name
find_elements_by_css_selector
元素交互 *** 作
对于获取的元素调用交互方法
from selenium import webdriverimport time
browser = webdriverChrome()
browserget("baocom")
input_str = browserfind_element_by_id('q')
input_strsend_keys("ipad")
timesleep(1)
input_strclear()
input_strsend_keys("MakBook pro")
button = browserfind_element_by_class_name('btn-search')
buttonclick()
运行的结果可以看出程序会自动打开Chrome浏览器并打开淘宝输入ipad,然后删除,重新输入MakBook pro,并点击搜索
交互动作
将动作附加到动作链中串行执行
from selenium import webdriverfrom seleniumwebdriver import ActionChains
browser = webdriverChrome()
url = "ry/tryphpfilename=jqueryui-api-droppable"browserget(url)
browserswitch_toframe('iframeResult')
source = browserfind_element_by_css_selector('#draggable')
target = browserfind_element_by_css_selector('#droppable')
actions = ActionChains(browser)
actionsdrag_and_drop(source, target)
actionsperform()
执行JavaScript
这是一个非常有用的方法,这里就可以直接调用js方法来实现一些 *** 作,
下面的例子是通过登录知乎然后通过js翻到页面底部,并d框提示
from selenium import webdriver
browser = webdriverChrome()
browserget("ucom/explore")
browserexecute_script('windowscrollTo(0, documentbodyscrollHeight)')
browserexecute_script('alert("To Bottom")')
获取元素属性
get_attribute('class')
from selenium import webdriver
browser = webdriverChrome()
url = 'hihucom/explore'browserget(url)
logo = browserfind_element_by_id('zh-top-link-logo')print(logo)print(logoget_attribute('class'))
获取文本值
text
from selenium import webdriver
browser = webdriverChrome()
url = 'com/explore'browserget(url)
input = browserfind_element_by_class_name('zu-top-add-question')print(inputtext)
获取ID,位置,标签名
id
location
tag_name
size
from selenium import webdriver
browser = webdriverChrome()
url = 'com/explore'browserget(url)
input = browserfind_element_by_class_name('zu-top-add-question')print(inputid)print(inputlocation)print(inputtag_name)print(inputsize)
Frame
在很多网页中都是有Frame标签,所以我们爬取数据的时候就涉及到切入到frame中以及切出来的问题,通过下面的例子演示
这里常用的是switch_tofrom()和switch_toparent_frame()
import timefrom selenium import webdriverfrom seleniumcommonexceptions import NoSuchElementException
browser = webdriverChrome()
url = 'oobcom/try/tryphpfilename=jqueryui-api-droppable'browserget(url)
browserswitch_toframe('iframeResult')
source = browserfind_element_by_css_selector('#draggable')print(source)try:
logo = browserfind_element_by_class_name('logo')except NoSuchElementException: print('NO LOGO')
browserswitch_toparent_frame()
logo = browserfind_element_by_class_name('logo')print(logo)print(logotext)
等待
当使用了隐式等待执行测试的时候,如果 WebDriver没有在 DOM中找到元素,将继续等待,超出设定时间后则抛出找不到元素的异常, 换句话说,当查找元素或元素并没有立即出现的时候,隐式等待将等待一段时间再查找 DOM,默认的时间是0
隐式等待
到了一定的时间发现元素还没有加载,则继续等待我们指定的时间,如果超过了我们指定的时间还没有加载就会抛出异常,如果没有需要等待的时候就已经加载完毕就会立即执行
from selenium import webdriver
browser = webdriverChrome()
browserimplicitly_wait(10)
browserget('com/explore')
input = browserfind_element_by_class_name('zu-top-add-question')print(input)
显示等待
指定一个等待条件,并且指定一个最长等待时间,会在这个时间内进行判断是否满足等待条件,如果成立就会立即返回,如果不成立,就会一直等待,直到等待你指定的最长等待时间,如果还是不满足,就会抛出异常,如果满足了就会正常返回
from selenium import webdriverfrom seleniumwebdrivercommonby import Byfrom seleniumwebdriversupportui import WebDriverWaitfrom seleniumwebdriversupport import expected_conditions as EC
browser = webdriverChrome()
browserget('taobaocom/')
wait = WebDriverWait(browser, 10)
input = waituntil(ECpresence_of_element_located((ByID, 'q')))
button = waituntil(ECelement_to_be_clickable((ByCSS_SELECTOR, 'btn-search')))print(input, button)
上述的例子中的条件:ECpresence_of_element_located()是确认元素是否已经出现了
ECelement_to_be_clickable()是确认元素是否是可点击的
常用的判断条件:
title_is 标题是某内容
title_contains 标题包含某内容
presence_of_element_located 元素加载出,传入定位元组,如(ByID, 'p')
visibility_of_element_located 元素可见,传入定位元组
visibility_of 可见,传入元素对象
presence_of_all_elements_located 所有元素加载出
text_to_be_present_in_element 某个元素文本包含某文字
text_to_be_present_in_element_value 某个元素值包含某文字
frame_to_be_available_and_switch_to_it frame加载并切换
invisibility_of_element_located 元素不可见
element_to_be_clickable 元素可点击
staleness_of 判断一个元素是否仍在DOM,可判断页面是否已经刷新
element_to_be_selected 元素可选择,传元素对象
element_located_to_be_selected 元素可选择,传入定位元组
element_selection_state_to_be 传入元素对象以及状态,相等返回True,否则返回False
element_located_selection_state_to_be 传入定位元组以及状态,相等返回True,否则返回False
alert_is_present 是否出现Alert
浏览器的前进和后退
back()
forward()
import timefrom selenium import webdriver
browser = webdriverChrome()
browserget('wwbaiducom/')
browserget('aobaocom/')
browserget('wwpythonorg/')
browserback()
timesleep(1)
browserforward()
browserclose()
cookie *** 作
get_cookies()
delete_all_cookes()
add_cookie()
from selenium import webdriver
browser = webdriverChrome()
browserget('om/explore')print(browserget_cookies())
browseradd_cookie({'name': 'name', 'domain': '>
选项卡管理
通过执行js命令实现新开选项卡windowopen()
不同的选项卡是存在列表里browserwindow_handles
通过browserwindow_handles[0]就可以 *** 作第一个选项卡
import timefrom selenium import webdriver
browser = webdriverChrome()
browserget('baiducom')
browserexecute_script('windowopen()')print(browserwindow_handles)
browserswitch_to_window(browserwindow_handles[1])
browserget('baocom')
timesleep(1)
browserswitch_to_window(browserwindow_handles[0])
browserget('honorg')
异常处理
这里只进行简单的演示,查找一个不存在的元素
from selenium import webdriverfrom seleniumcommonexceptions import TimeoutException, NoSuchElementException
browser = webdriverChrome()try:
browserget('ducom')except TimeoutException: print('Time Out')try:
browserfind_element_by_id('hello')except NoSuchElementException: print('No Element')finally:
browserclose()
所有的努力都值得期许,每一份梦想都应该灌溉!
三种d框: alert:用来提示
confirm:用来确认
prompt:输入内容
常用的属性和方法:accept() 接受
dismiss()取消
text显示的文本
send_keys输入内容
源码:
from seleniumimport webdriver
from timeimport sleep
import os
class TestCase(object):
def __init__(self):
selfdriver = webdriverChrome()
path = ospathdirname(ospathabspath(__file__))#ospathabspath当前文件路径
file_path ='file:///' + path +'/test_alerthtml'
selfdriverget(file_path)
def test_alert(self):
selfdriverfind_element_by_id('alert')click()
#切换到alert
a =selfdriverswitch_toalert
print(atext)
sleep(2)
aaccept()
def test_confirm(self):
selfdriverfind_element_by_id('confirm')click()
#切换到confirm
c =selfdriverswitch_toalert
print(ctext)
sleep(2)
#caccept()
cdismiss()
def test_prompt(self):
selfdriverfind_element_by_id('prompt')click()
#切换到confirm
p =selfdriverswitch_toalert
print(ptext)
sleep(2)
psend_keys('18')
paccept()
if __name__ =='__main__':
case = TestCase()
#casetest_alert()
#casetest_confirm()
casetest_prompt()
test_alerthtml:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<a href="javascript:alert('提示框')" id="alert">Alert</a><br>
<a href="javascript:confirm('真的要删除吗')" id="confirm">Confirm</a><br>
<a href="javascript:var age = prompt('请输入年龄');documentwrite(age)" id="prompt">Prompt</a><br>
</body>
</html>
不是你表达不清,也许只是你根本不想仔细看一睛VC下面目录的源码,事实上就是有的。后附其中的qsortc,以证明所言不虚。
VC的库是提供源码的,这东西也不值钱。
X:\Program Files\Microsoft Visual Studio\VCXX\CRT\SRC
注意有些可能本身是用汇编写的。
/
qsortc - quicksort algorithm; qsort() library function for sorting arrays
Copyright (c) 1985-1997, Microsoft Corporation All rights reserved
Purpose:
To implement the qsort() routine for sorting arrays
/
#include <cruntimeh>
#include <stdlibh>
#include <searchh>
/ prototypes for local routines /
static void __cdecl shortsort(char lo, char hi, unsigned width,
int (__cdecl comp)(const void , const void ));
static void __cdecl swap(char p, char q, unsigned int width);
/ this parameter defines the cutoff between using quick sort and
insertion sort for arrays; arrays with lengths shorter or equal to the
below value use insertion sort /
#define CUTOFF 8 / testing shows that this is good value /
/
qsort(base, num, wid, comp) - quicksort function for sorting arrays
Purpose:
quicksort the array of elements
side effects: sorts in place
Entry:
char base = pointer to base of array
unsigned num = number of elements in the array
unsigned width = width in bytes of each array element
int (comp)() = pointer to function returning analog of strcmp for
strings, but supplied by user for comparing the array elements
it accepts 2 pointers to elements and returns neg if 1<2, 0 if
1=2, pos if 1>2
Exit:
returns void
Exceptions:
/
/ sort the array between lo and hi (inclusive) /
void __cdecl qsort (
void base,
unsigned num,
unsigned width,
int (__cdecl comp)(const void , const void )
)
{
char lo, hi; / ends of sub-array currently sorting /
char mid; / points to middle of subarray /
char loguy, higuy; / traveling pointers for partition step /
unsigned size; / size of the sub-array /
char lostk[30], histk[30];
int stkptr; / stack for saving sub-array to be processed /
/ Note: the number of stack entries required is no more than
1 + log2(size), so 30 is sufficient for any array /
if (num < 2 || width == 0)
return; / nothing to do /
stkptr = 0; / initialize stack /
lo = base;
hi = (char )base + width (num-1); / initialize limits /
/ this entry point is for pseudo-recursion calling: setting
lo and hi and jumping to here is like recursion, but stkptr is
prserved, locals aren't, so we preserve stuff on the stack /
recurse:
size = (hi - lo) / width + 1; / number of el's to sort /
/ below a certain size, it is faster to use a O(n^2) sorting method /
if (size <= CUTOFF) {
shortsort(lo, hi, width, comp);
}
else {
/ First we pick a partititioning element The efficiency of the
algorithm demands that we find one that is approximately the
median of the values, but also that we select one fast Using
the first one produces bad performace if the array is already
sorted, so we use the middle one, which would require a very
wierdly arranged array for worst case performance Testing shows
that a median-of-three algorithm does not, in general, increase
performance /
mid = lo + (size / 2) width; / find middle element /
swap(mid, lo, width); / swap it to beginning of array /
/ We now wish to partition the array into three pieces, one
consisiting of elements <= partition element, one of elements
equal to the parition element, and one of element >= to it This
is done below; comments indicate conditions established at every
step /
loguy = lo;
higuy = hi + width;
/ Note that higuy decreases and loguy increases on every iteration,
so loop must terminate /
for (;;) {
/ lo <= loguy < hi, lo < higuy <= hi + 1,
A[i] <= A[lo] for lo <= i <= loguy,
A[i] >= A[lo] for higuy <= i <= hi /
do {
loguy += width;
} while (loguy <= hi && comp(loguy, lo) <= 0);
/ lo < loguy <= hi+1, A[i] <= A[lo] for lo <= i < loguy,
either loguy > hi or A[loguy] > A[lo] /
do {
higuy -= width;
} while (higuy > lo && comp(higuy, lo) >= 0);
/ lo-1 <= higuy <= hi, A[i] >= A[lo] for higuy < i <= hi,
either higuy <= lo or A[higuy] < A[lo] /
if (higuy < loguy)
break;
/ if loguy > hi or higuy <= lo, then we would have exited, so
A[loguy] > A[lo], A[higuy] < A[lo],
loguy < hi, highy > lo /
swap(loguy, higuy, width);
/ A[loguy] < A[lo], A[higuy] > A[lo]; so condition at top
of loop is re-established /
}
/ A[i] >= A[lo] for higuy < i <= hi,
A[i] <= A[lo] for lo <= i < loguy,
higuy < loguy, lo <= higuy <= hi
implying:
A[i] >= A[lo] for loguy <= i <= hi,
A[i] <= A[lo] for lo <= i <= higuy,
A[i] = A[lo] for higuy < i < loguy /
swap(lo, higuy, width); / put partition element in place /
/ OK, now we have the following:
A[i] >= A[higuy] for loguy <= i <= hi,
A[i] <= A[higuy] for lo <= i < higuy
A[i] = A[lo] for higuy <= i < loguy /
/ We've finished the partition, now we want to sort the subarrays
[lo, higuy-1] and [loguy, hi]
We do the smaller one first to minimize stack usage
We only sort arrays of length 2 or more/
if ( higuy - 1 - lo >= hi - loguy ) {
if (lo + width < higuy) {
lostk[stkptr] = lo;
histk[stkptr] = higuy - width;
++stkptr;
} / save big recursion for later /
if (loguy < hi) {
lo = loguy;
goto recurse; / do small recursion /
}
}
else {
if (loguy < hi) {
lostk[stkptr] = loguy;
histk[stkptr] = hi;
++stkptr; / save big recursion for later /
}
if (lo + width < higuy) {
hi = higuy - width;
goto recurse; / do small recursion /
}
}
}
/ We have sorted the array, except for any pending sorts on the stack
Check if there are any, and do them /
--stkptr;
if (stkptr >= 0) {
lo = lostk[stkptr];
hi = histk[stkptr];
goto recurse; / pop subarray from stack /
}
else
return; / all subarrays done /
}
/
shortsort(hi, lo, width, comp) - insertion sort for sorting short arrays
Purpose:
sorts the sub-array of elements between lo and hi (inclusive)
side effects: sorts in place
assumes that lo < hi
Entry:
char lo = pointer to low element to sort
char hi = pointer to high element to sort
unsigned width = width in bytes of each array element
int (comp)() = pointer to function returning analog of strcmp for
strings, but supplied by user for comparing the array elements
it accepts 2 pointers to elements and returns neg if 1<2, 0 if
1=2, pos if 1>2
Exit:
returns void
Exceptions:
/
static void __cdecl shortsort (
char lo,
char hi,
unsigned width,
int (__cdecl comp)(const void , const void )
)
{
char p, max;
/ Note: in assertions below, i and j are alway inside original bound of
array to sort /
while (hi > lo) {
/ A[i] <= A[j] for i <= j, j > hi /
max = lo;
for (p = lo+width; p <= hi; p += width) {
/ A[i] <= A[max] for lo <= i < p /
if (comp(p, max) > 0) {
max = p;
}
/ A[i] <= A[max] for lo <= i <= p /
}
/ A[i] <= A[max] for lo <= i <= hi /
swap(max, hi, width);
/ A[i] <= A[hi] for i <= hi, so A[i] <= A[j] for i <= j, j >= hi /
hi -= width;
/ A[i] <= A[j] for i <= j, j > hi, loop top condition established /
}
/ A[i] <= A[j] for i <= j, j > lo, which implies A[i] <= A[j] for i < j,
so array is sorted /
}
/
swap(a, b, width) - swap two elements
Purpose:
swaps the two array elements of size width
Entry:
char a, b = pointer to two elements to swap
unsigned width = width in bytes of each array element
Exit:
returns void
Exceptions:
/
static void __cdecl swap (
char a,
char b,
unsigned width
)
{
char tmp;
if ( a != b )
/ Do the swap one character at a time to avoid potential alignment
problems /
while ( width-- ) {
tmp = a;
a++ = b;
b++ = tmp;
}
}
上一篇 我们介绍了 spring-tx 中的底层抽象,本篇我们一起来看看围绕这些抽象概念 spring-tx 是如何打造出声明式事务的吧。笼统的说, spring-tx-526RELEASE 的实现主要分为两个部分:
这两部分彼此独立又相互成就,并且每个部分都有着大量的源码支撑,本篇我们先来分析 spring-tx 中的AOP部分吧。
EnableTransactionManagement 注解想必大家都很熟悉了,它是启用 Spring 中注释驱动的事务管理功能的关键。
EnableTransactionManagement 注解的主要作用是向容器中导入 TransactionManagementConfigurationSelector ,至于注解中定义的几个属性在 Spring AOP源码解析 中有过详细分析,这里就不再赘述了。
由于我们并没有使用 AspectJ ,因此导入容器的自然是 ProxyTransactionManagementConfiguration 这个配置类。
这个配置类的核心是向容器中导入一个类型为 BeanFactoryTransactionAttributeSourceAdvisor 的Bean。这是一个 PointcutAdvisor ,它的 Pointcut 是 TransactionAttributeSourcePointcut , Advice 是 TransactionInterceptor 。
TransactionAttributeSourcePointcut 利用 TransactionAttributeSource 解析 @Transactional 注解的能力来选取标注了 @Transactional 注解的方法,而 TransactionInterceptor 则根据应用提出的需求(来自对 @Transactional 注解的解析)将方法增强为事务方法,因此 BeanFactoryTransactionAttributeSourceAdvisor 可以识别出那些标注了 @Transactional 注解的方法,为它们应用上事务相关功能。
TransactionInterceptor 能对方法进行增强,但是它却不知道该如何增强,比如是为方法新开一个独立事务还是沿用已有的事务?什么情况下需要回滚,什么情况下不需要?必须有一个『人』告诉它该如何增强,这个『人』便是 TransactionAttributeSource 。
@Transactional 注解定义了事务的基础信息,它表达了应用程序期望的事务形态。 TransactionAttributeSource 的主要作用就是解析 @Transactional 注解,提取其属性,包装成 TransactionAttribute ,这样 TransactionInterceptor 的增强便有了依据。
前面我们已经见过, spring-tx 使用 AnnotationTransactionAttributeSource 来做具体的解析工作,其父类 AbstractFallbackTransactionAttributeSource 定义了解析 TransactionAttribute 的优先级,核心方法是 computeTransactionAttribute() 。
AnnotationTransactionAttributeSource 默认只解析 public 修饰的方法,这也是导致 @Transactional 注解失效的一个原因,除此之外它还实现了父类中定义的两个模板方法:
同时为了支持 EJB 中定义的 javaxejbTransactionAttribute 和 JTA 中定义的 javaxtransactionTransactional 注解, AnnotationTransactionAttributeSource 选择将实际的提取工作代理给 TransactionAnnotationParser 。Spring 提供的 @Transactional 注解由 SpringTransactionAnnotationParser 进行解析。
SpringTransactionAnnotationParser 的源码还是很简单的,它使用 AnnotatedElementUtils 工具类定义的 find 语义来获取 @Transactional 注解信息。 RuleBasedTransactionAttribute 中 rollbackOn() 的实现还是挺有意思的,其它的都平平无奇。
RollbackRuleAttribute 是用来确定在发生特定类型的异常(或其子类)时是否应该回滚,而 NoRollbackRuleAttribute 继承自 RollbackRuleAttribute ,但表达的是相反的含义。 RollbackRuleAttribute 持有某个异常的名称,通过 getDepth(Throwable ex) 算法来计算指定的 Throwable 和持有的异常在继承链上的距离。
程序猿只有在拿到需求以后才能开工, TransactionInterceptor 也一样,有了 TransactionAttributeSource 之后就可以有依据的增强了。观察类图, TransactionInterceptor 实现了 MethodInterceptor 接口,那么自然要实现接口中的方法:
可以看到, TransactionInterceptor 本身是没有实现任何逻辑的,它更像一个适配器。这样分层以后, TransactionAspectSupport 理论上就可以支持任意类型的 Advice 而不只是 MethodInterceptor 。实现上 TransactionAspectSupport 确实也考虑了这一点,我们马上就会看到。
invokeWithinTransaction() 的流程还是非常清晰的:
第一步前文已经分析过了,我们来看第二步。
TransactionInfo 是一个非常简单的类,我们就不费什么笔墨去分析它了。接着看第三步,这一步涉及到两个不同的 *** 作——提交或回滚。
至此, TransactionInterceptor 于我们而言已经没有任何秘密了。
本篇我们一起分析了 spring-tx 是如何通过 spring-aop 的拦截器将普通方法增强为事务方法的,下篇就该说道说道 PlatformTransactionManager 抽象下的事务管理细节啦,我们下篇再见~~
你好,看了你的代码,id值为:rb_btn的li标签是没有click方法的,所以你的点击才会无效。li标签下的子标签a有click方法。因此建议获取到a标签来调用click()。
希望能帮到你。
以上就是关于UI自动化测试中,Selenium的三种等待方式详解全部的内容,包括:UI自动化测试中,Selenium的三种等待方式详解、auto.js如何解析html、python selenium drag_and_drop()和drag_and_drop_with_offset()怎么使用等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)