Selenium及web自动化的伪初体验和踩坑

难得一篇技术文,难道不值得诸君评论一下吗。嘤嘤嘤。

这篇文章主要来说说这几天我用python写selenium踩的一些坑,可能还涉及到正则还有sqlite3的一些操作。

需求

首先先说说需求,学校下发了一些网课学习的任务。比如大家都深受其害的同济大学的《军事理论》这门课。所谓道高一尺魔高一丈,需求紧迫,伟大的阿邓曾经说过:“懒惰是第一生产力”(此处纯粹调侃,绝无冒犯之意)。由我来产出供给。虽然已经有学长提供这个服务,但是我觉得还是得自己整一个。这样既可以解放自己的双手,又可以解放自己的钱包,说不定还可以用这个机会赚点外快。

任务是一门课程里面有很多小节,小节包括一个不能拖动进度条的视频和几个题目。看完视频或者做完题目视为完成了一个任务点,完成两个任务点视为完成了一个小节,只有完成了一个小节才能advance到下一个小节。

思路

页面是动态加载的,我选择比较熟悉的python(实际上是非常不熟悉)用web自动化完成这个任务。库就选择selenium了,因为对selenium比较熟悉,一两年前用过一回。

播放视频还是比较好解决的,直接用js拿到video元素,然后直接设置playbackRate成16.0就可以开成16x播放。我发现safari是不限制播放速度的,可以设置成几千几万倍甚至更高,可以让几分钟的视频一秒刷完。然而chrome限定了maxinum为16。

可能有人疑惑为什么不直接改currentTime呢,其实我一开始也是这么打算的。但是不知道为何对这个视频没起到作用,所以就暂时搁置了。

对于题目,百度上有同济大学2020军事理论的题库答案,一共500道,整理成数据库,回答问题的时候直接从数据库select出答案,然后click对应的radio就可以了。

要说这个项目有何技术上的难点,实际上没有。难的是自己对web的不熟悉,所以有很多对于我来说新的东西需要学,比如xpath,正则表达式等(正则每次写都需要查,可以说是很痛苦了)。以及selenium文档本身并不很细节,不知道哪些类有哪些propertymethod,以至于每用每查。不过熟悉了一段时间后,写起来就会比较顺畅了。

获取元素

Selenium有一系列从文档中获取元素的方法,常见的有find_element_by_xxx这一套函数,还有同样一套复数的叫做find_elements_by_xxx,这个xxx可以是id,也可以是class name,同样也包括css选择器和xpath

但是一般不建议用这种方式来直接从文档中取出元素,因为把控不好时间。当然,
你可以选择用sleep,睡一个固定时间,但是这样容易把握不好度。一般来说sleep个两三秒肯定是有点过头了。所以sleep一般不是推荐的办法。

Explicit Wait

获取元素最好的时机就是等他一加载完就get,所以就有了explicit wait,解决的就是这个问题。

我觉得应用explicit wait代替所有盲目的find_element,写法也比较简单:


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

element = WebDriverWait(driver, 10).until(
    EC.presence_of_element_located((By.XPATH, “//div[@class='button']//a”))
)
# 获得div.button下的第一层a标签,xpath可能是查找元素最为常用的方式

对于explicit wait的更多的资料,建议读一读官方文档:https://selenium-python.readthedocs.io/waits.html#explicit-waits

Frame和Window

Frame

目前还有很多网站在使用iframeframe标签,这俩的存在会让网页复杂很多。

我正在折腾的站子就用了iframe标签,因为对web不熟悉,所以这个是真的把我狠狠地坑了一回。

原因在于iframe中的代码并不是直接被加载的。所以我一开始遇到的麻烦便是明明在网页源码里面可以看到的元素,但却赤裸裸的报错:ElementNotFound。

这个问题切换到frame就可以解决:

driver.switch_to.frame(element)

element可以是已经找到的元素,也可以是iframeid字符串。

顺带一提,如果有iframe里面套iframe的情况,需要先进入第一层iframe,然后进入第二层iframe。以此类推。

如果要切回上一层iframe,也需要一层层往外切,大概这样写:

driver.switch_to.parent_frame()

当然,也有方法提供一步到位,切到最初的位置。

driver.switch_to.default_content()

以上就是目前我所知道的关于selenium中frame的切换问题。

Window

selenium提供了一个数组储存打开的window的句柄。获取这个数组的办法:
handles = driver.window_handles()

这个数组是顺序存储的,所以最后一个打开的窗口就是handles[-1]

如果想要得到目前打开的窗口的句柄,可以用driver.current_window_handle

切换window的方法和切换frame的方法是非常类似的。比如我想切换到最后一个打开的窗口,我应该用driver.switch_to.window(driver.window_handles()[-1])

后记:刷课存在的问题

发现cx有着较为严格的审查机制,在网上可以查到不少关于cx的误判问题。在cx的 前几个版本,不良记录对于学生是可见的,现在cx的做法是不良记录对学生不可见,知道自己是否有不良行为记录的办法只有等上面的通知。然而如果犯下不良后果还是比较严重的,意味着一门课的挂科。是故刷课需谨慎。

2019-2020 Sunshine+Ice