事情是酱紫的,我在使用 selenium 的鼠标事件,用了 ActionChains 这个库; 该说不说,我有些时候不能理解 ActionChains 是咋回事; 比如:
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(browser)
actions.move_by_offset(10, 20).click().perform()
比如这样的代码,就是鼠标的链式操作嘛,移动到指定 xy 坐标,然后点击,然后执行链式操作;
那么问题来了,这个鼠标的移动啊,它不能回归到默认的 0,0 的位置啊。
就是我再次执行 move_by_offset 的时候,xy 坐标是根据上次鼠标所在的位置继续叠加的,这分分钟就超出浏览器范围了;
根据各大网资料,得知了这个链式操作底层实际上是 self._actions.append
就是他的链式操作就是把所有要执行的东西依次添加到一个数组里,然后执行;
那么问题来了,如何可以清空鼠标位置到 0 的位置呢?我不想每次都在上次移动的位置叠加执行。 这个时候有人就说了,你干嘛不试试 move_by_offset(-10, -20) 这样让他回归呢? 嗯,因为我不知道 xy 坐标,所以没办法回归原来,我就是想让他每次鼠标在左上角,就那么难吗?
大佬们,你们遇到了吗?有解决办法嘛?
1
gdtdpt 2020-07-01 15:41:11 +08:00
reset_actions
|
2
nullboy 2020-07-01 15:46:48 +08:00
这玩意很少用,几乎没用过
|
3
smallgoogle OP @gdtdpt 试过这个,好像并不能让鼠标回归到原来的 xy 坐标,这只是能让他不再执行一次之前数组里已有的。
|
4
gdtdpt 2020-07-01 17:36:02 +08:00
@smallgoogle 用 move_to_element_with_offset 吧,好像没什么好的方法,要不就每次 preform 后再 move_to_element 移动到某个元素来重置鼠标位置吧,不知道能不能 move_to_element('body')。
|
5
jiejiss 2020-07-01 22:28:24 +08:00 1
TL;DR 用 move_by_offset 的话只能通过先移过去再移回来的操作来复位。
看了看 selenium 方法源码 https://www.selenium.dev/selenium/docs/api/py/_modules/selenium/webdriver/common/action_chains.html#ActionChains.move_by_offset 多追一层,发现是 webdriver 层的实现,直接 POST 到 remote 去了 https://github.com/SeleniumHQ/selenium/blob/trunk/py/selenium/webdriver/remote/remote_connection.py#L279 看了看别的 webdriver implement,发现都是用的 xoffset 和 yoffset,没有直接让指定 x 和 y 的 https://webdriver.io/docs/api/element/moveTo.html https://api.flutter.dev/flutter/webdriver.sync_io/Mouse/moveTo.html 遂怀疑是标准定义的问题,于是去翻了 w3c 标准,发现 w3c 根本就没有在 webdriver 标准里给出 moveTo 的定义,只有 https://www.w3.org/TR/webdriver/#h-note-28 这里提了一嘴。顺着找下去就发现了 https://www.w3.org/TR/cssom-view-1/#dom-window-moveto 但显然这个 moveTo 不是 webdriver 规范的一部分,而且它是基于页面左上角的绝对坐标,下面的 moveBy 才是基于 offset 的相对坐标。 百思不得其解,联想到别的 webdriver implement 和 Selenium 的一致性,就又去看了 Google ChromeDriver 的文档: https://chromium.googlesource.com/chromium/src/+/master/docs/chromedriver_status.md 里面根本就没提 moveTo,和 w3c 标准保持了一致,所以 moveTo 可能是类似于一个非标准的灰色 API 于是又去读 ChromeDriver 源码,发现了这个: https://github.com/bayandin/chromedriver/blob/master/server/http_handler.cc#L574 https://github.com/bayandin/chromedriver/blob/master/window_commands.cc#L1029 所以你看最后的方法实现,也就是 window_commands.cc 1045 行附近,先判断有没有指定 element ( Selenium 吃掉了这个配置,所以无法用 move_by_offset 指定 element ),如果没有就 location = session->mouse_position; location.Offset(x_offset, y_offset); 并在最后又 session->mouse_position = location; 考虑到 Offset 的实现是 void WebPoint::Offset(int x_, int y_) { x += x_; y += y_; } 总结:我觉得没啥整出 dirty hack 的希望,老老实实吃这坨 bad design 造出来的翔吧。 |
6
ClericPy 2020-07-01 23:08:49 +08:00
如果是想直接移动鼠标到指定位置的话, chrome devtools protocol 里有 dispatch 鼠标移动和点击的事件, 不过有点好奇 selenium 里的操作鼠标移动时候是和 cdp 一样发事件, 还是从驱动层面控制鼠标动的?
|
7
jiejiss 2020-07-01 23:19:15 +08:00
@ClericPy #6 Selenium 是一层封装 用 HTTP 和 browser 通信,控制鼠标移动是 browser 的事情,和 Selenium 无关
|
8
ClericPy 2020-07-01 23:22:35 +08:00
@jiejiss 好奇的是它操作 webdriver 的时候, 鼠标行为是事件, 还是其他操作, 因为之前用 cdp 发鼠标事件被反爬识别到了, 用 pyautogui 操作鼠标就没被识别到
|
9
freakxx 2020-07-01 23:28:35 +08:00
要不就 继承 ActionChains
定义多 2 个 变量 _x = 0 _y = 0 复写下 move_by_offset, 叠加 self._x self._y 再写个 def move_to_start() x = - _x y = - _y |
10
jiejiss 2020-07-01 23:29:07 +08:00
@ClericPy #8 Status status = web_view->DispatchMouseEvents(
events, session->GetCurrentFrameId(), false); 是 dispatch |
12
smallgoogle OP @jiejiss 我也追到了你看的这些资料,发现真的是一坨。。。 我以为大家有什么骚操作。
|
13
smallgoogle OP @freakxx 你这算曲线救国了。
|