Fork me on GitHub

为什么Selenium点不到元素

这是崔斯特的第六十篇原创文章

明明可以显示,为什么就是点不到呢 (๑• . •๑)

最近做了许多登陆项目,我会优先选择使用requests来模拟请求,但是有些参数实在是很难获取,这个时候我会使用Selenium,也还是遇到了各种坑,也算是见识到了很多的验证措施。

今天说说如何解决selenium点选不到数据的问题。

等待

这还是最常见的一种情况,推荐最多的是使用显示等待:

1
2
3
4
5
6
7
8
9
10
11
12
13
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Firefox()
driver.get("http://somedomain/url_that_delay_loading")
try:
element = WebDriverWait(driver,10).until(
EC.presence_of_element_located((By.ID,"myDynamicElement"))
)
finally:
driver.quit()

这段代码会等待10秒,如果10秒内找到元素则立即返回,否则会抛出TimeoutException异常。

但是我比较懒,因为time.sleep()可以达到同样效果。

鼠标事件

官方把它叫做“行为链”。ActionChains可以完成简单的交互行为,例如鼠标移动,鼠标点击事件,键盘输入,以及内容菜单交互。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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) ——发送某个键到指定元素

深入了解可以参考 https://blog.csdn.net/huilan_same/article/details/52305176

move_to_element_with_offsetclick_and_hold会经常用到破解验证码中。

触摸操作 (TouchAction)

该事件仅仅针对移动端、触屏版

1
2
3
4
5
6
7
8
9
10
11
12
13
flick_element(on_element, xoffset, yoffset, speed) # 以元素为起点以一定速度向下滑动
scroll_from_element(on_element xoffset yoffset) #以元素为起点向下滑动
double_tap(on_element) #双击
flick_element(on_element, xoffset, yoffset, speed) #从元素开始以指定的速度移动
long_press(on_element)   #长按不释放
move(xcoord, ycoord)   #移动到指定的位置
perform()   #执行链中的所有动作
release(xcoord, ycoord)   #在某个位置松开操作
scroll(xoffset, yoffset) #滚动到某个位置
scroll_from_element(on_element, xoffset, yoffset) #从某元素开始滚动到某个位置
tap(on_element) #单击
tap_and_hold(xcoord, ycoord) #某点按住

为什么要说到移动端,在做登陆时,移动端往往会更加简单,但是触屏版的点击和PC端时完全不同的,点击与按住时不同的。

在某个项目我换成TouchAction后,神奇的发现,注册不再需要处理验证码了,真是太棒了。

使用js

当你使用浏览器已经找到该元素,使用click()方法但是不起作用时,这个时候建议尝试js,例如在我的主页 https://www.zhihu.com/people/cuishite/activities,点击 “查看详细资料”

1
2
js = 'document.getElementsByClassName("Button ProfileHeader-expandButton Button--plain")[0].click();'
driver.execute_script(js)

你可以先在控制台调试

js通常可以解决绝大多是问题,如果还是解决不了,那你可能和我遇到了同样的问题,比如说,我在处理某移动端网站登陆,处理如下验证码时,我会使用到move_to_element_with_offset,该方法是“移动到距某个元素(左上角坐标)多少距离的位置”。

计算出坐标后,会调用该方法,如action.move_to_element_with_offset(element, width, height).click().perform(),然而实际上问题并没有这么简单,多次点击失效。具体的有时间再说。

实用方法

提取selenium的cookies

介绍把selenium的cookies船体给requests使用的方法:

1
2
3
4
5
cookies = driver.get_cookies()
s = requests.Session()
for cookie in cookies:
s.cookies.set(cookie['name'], cookie['value'])

How do I load session and cookies from Selenium browser to requests library in Python?

元素截图方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from selenium import webdriver
from PIL import Image
fox = webdriver.Firefox()
fox.get('https://stackoverflow.com/')
# now that we have the preliminary stuff out of the way time to get that image :D
element = fox.find_element_by_id('hlogo') # find part of the page you want image of
location = element.location
size = element.size
fox.save_screenshot('screenshot.png') # saves screenshot of entire page
fox.quit()
im = Image.open('screenshot.png') # uses PIL library to open image in memory
left = location['x']
top = location['y']
right = location['x'] + size['width']
bottom = location['y'] + size['height']
im = im.crop((left, top, right, bottom)) # defines crop points
im.save('screenshot.png') # saves new cropped image

selenium cannot screenshot a web element

最后推荐一个神器 appium/python-client

至于验证码部分,现在主要还是靠第三方工具,并没有自己尝试机器学习等方法处理。