近段时间,因熊孩子沉迷于电视而致命学习成绩下降,遂有了在不影响家里老人正常观看电视的情况下对电视进行限制的需求,经过思索后准备利用爬虫技术来实现,即在熊孩子放学时自动登录网管交换机来对机顶盒进行限速,熊孩子上学后再解除针对机顶盒的限速。
English Version
现在的熊孩子岁数不大却非常聪明,可以自己开电视并换台寻找喜爱的电视节目,之前采用拨网线的暴力方法来禁止熊孩子无休止的观看电视,后因经常拨网线后忘了重新插上而导致老人无法看电视,遂此方法废弃了………
好在经过思索,发现家里的 IPTV 连在了 Netgear 网管交换机上,我们可以在交换机上对 IPTV 端口进行限速,以使电视无法正常观看。
说起爬虫,首先想到的就是 Python,经过一翻google ,确定了使用Selenium 、Firefox /Chrome 来实现爬虫功能。
什么是Selenium
Github-Selenium
Selenium 是一个用于 Web 应用程序的测试工具。Selenium 直接调用浏览器来进行测试,就像真正的用户在操作一样。它支持 IE(7, 8, 9, 10, 11),Mozilla Firefox ,Safari,Google Chrome ,Opera,HtmlUnit,phantomjs,Android(需安装 Selendroid 或 appium),IOS(需安装ios-driver 或 appium)等。
Selenium 支持 C# / JavaScript / Java / Python / Ruby 开发语言。它使用 WebDriver 来操作浏览器进行网页测试。
在爬虫中 Selenium 主要用来解决 JavaScript 的渲染问题。
什么是 WebDriver Webdriver 是一个用来和浏览器进行交互的编程接口,通过它可以操作浏览器打开或关闭、发送鼠标点击、模拟键盘输入等等。
W3C 定义了 WebDriver 规范。现在最流行的 WebDrver 为开源软件 Selenium WebDriver 。
WebDriver 包含多个模块:
支持多编程语言
自动化框架,提供网页的元素查找、点击、输入等自动化功能,减少重复编码。
JSON 协议,自动化框架与浏览器驱动的中间层,它提供了跨平台跨语言的能力。
浏览器驱动,通过它来调用浏览器。
浏览器,对网页进行渲染。
安装 Selenium 1 2 :~$ apt-get install python3 python3-pip :~$ pip3 install selenium
WebDriver
ChromeDriver1 :`$ apt-get install chromium-driver
Firefox,自github-geckodriver 下载。1 2 3 :~$ wget https://github.com/mozilla/geckodriver/releases/download/v0.26.0/geckodriver-v0.26.0-linux64.tar.gz :~$ tar -xvf geckodriver-v0.26.0-linux64.tar.gz :~$ mv geckodriver /usr/local/bin/
Selenium 的使用方法 这里我们使用 Python 语言。
浏览网页 先来一段简单爬虫界的Hello World
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import timefrom selenium import webdriverprint ("初始化 ChromeDriver,并打开 Chrome" )driver = webdriver.Chrome() print ("打开 shixuen.com 网址" )driver.get("https://www.shixuen.com" ) time.sleep(5 ) print ("关闭浏览器" )driver.close() print ("初始化 geckodriver 并打开 Firefox 浏览器" )driver = webdriver.Firefox() driver.get("https://www.shixuen.com" ) time.sleep(5 ) driver.close()
声明浏览器1 2 3 4 from selenium import webdriverdriver = webdriver.Chrome() driver = webdriver.Firefox()
使用driver.get( “https://www.shixuen.com “ ) 来打开网站
关闭浏览器,driver.close() 。
模拟鼠标点击 我们再添加点新功能,打开https://www.shixuen.com
后,点击文章VIM Plugin - YouCompleteMe
。
依旧先看代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import timefrom selenium import webdriverprint ("初始化 ChromeDriver,并打开 Chrome" )driver = webdriver.Chrome() print ("打开 shixuen.com 网址" )driver.get("https://www.shixuen.com" ) print ("搜索指定文本的链接" )article = driver.find_element_by_link_text("VIM Plugin - YouCompleteMe" ) print ("点击此链接" )article.click() time.sleep(5 ) print ("关闭浏览器" )driver.close()
关键代码为driver.find_element_by_link_text( “VIM Plugin - YouCompleteMe” ) ,搜索文字内容为VIM Plugin - YouCompleteMe
的链接,找到后返回此节点的对象。
搜索指定元素 如网页节点的代码:<a id="btn_apply" class="btn_class">Apply</a>
以ID
进行搜索,driver.find_element_by_id( “btn_apply” )
以链接的文本内容
进行搜索,driver.find_element_by_link_text( “Apply” )
以class
进行搜索,driver.find_element_by_class_name( “btn_class” )
以xpath
进行搜索,driver.find_element_by_xpath( “//a[@id=’btn_apply’ and @class=’btn_class’]” )
/
:从根节点开始进行搜索
//
:搜索所有节点
./
:搜索本节点下的子节点
点击此节点 代码 article.click() 来执行鼠标点击操作。
查找网页节点代码的方法
首先使用浏览器打开页面
按[F12]
调出开发者工具
点击工具左上角的按钮对元素进行定位
上面的代码算是爬虫界的Hello World
吧。
登录并配置 Netgear 网管交换机 下面,进入本文的正题,登录并配置 Netgear 网管交换机。依旧不废话,先上代码。
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 import time, sys, getopt, osfrom selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.support.select import Select_gs105e_rate_limit = { "unlimit" : 1 , "limit" : 3 } gs105e_conf = { "url" : "http://192.168.1.2" , "password" : "0123456789" , "rate" : _gs105e_rate_limit["limit" ], "port" : "port2" } def browser ( driver ): print ( "打开交换机网页" ) driver.get( gs105e_conf["url" ] ) print ( "输入密码" ) passwd_input = WebDriverWait( driver, 10 ).until( EC.presence_of_element_located( (By.ID,"password" ) ) ) passwd_input.send_keys(gs105e_conf["password" ]) print ("点击登录" ) btn_login = driver.find_element_by_id("loginBtn" ) bt_login.click() print ("点击 <QoS> 菜单" ) menu_qos = WebDriverWait(driver,10 ).until( EC.presence_of_element_located( (By.ID, "QoS" ) ) ) menu_qos.click() print ("点击 <Qos 速率限制> 菜单" ) menu_qos_ratelimit = driver.find_element_by_id("QoS_RateLimit" ) menu_qos_ratelimit.click() print ("等待子页面加载完成" ) time.sleep(4 ) print ("跳转至子页面" ) iframe = driver.find_element_by_xpath("//iframe[@id='maincontent']" ) driver.switch_to.frame( iframe ) print ("点击端口对应的 <checkbox>" ) WebDriverWait(driver,10 ).until( EC.presence_of_element_located( (By.NAME,gs105e_conf["port" ]) ) ).click() print ("选择入站速率" ) btn_select = driver.find_element_by_name("IngressRate" ) Select( btn_select ).select_by_index( gs105e_conf["rate" ] ) print ("选择出站速率" ) btn_select = driver.find_element_by_name("EgressRate" ) Select( btn_select ).select_by_index( gs105e_conf["rate" ] ) print ("由子页面返回主页面" ) driver.switch_to.default_content() print ("点击 <应用> 按钮" ) btn_apply = driver.find_element_by_id("btn_Apply" ) btn_apply.click() print ("点击 <退出> 按钮" ) btn_logout = WebDriverWait(driver,10 ).until( EC.presence_of_element_located( (By.ID,"logout" ) ) ) btn_logout.click() def main (): try : driver = webdriver.Firefox() browser(driver) except : print ("error in script!" ) finally : print ("Close Brwoser!" ) driver.close() if __name__ == "__main__" : main()
上面的代码已经可以实现登录 Netgear网管交换机并自动对 TV 端口进行限速了。每一步都写得非常清楚,只不过多了几个函数和页面间的跳转而已。
下面对代码进行解析:
使用driver.get( “url” ) 打开 Netgear 网管交换机的登录界面。
输入密码并登录。在这里使用了WebDriverWait( driver,10 ).until( EC.presence_of_element_located( (By.ID,”password”) ) ) ,它的意思就是在10秒
内使用driver
来获取ID
为password
的元素,如果获取成功则返回元素对象,如果超时则报错。 这里需要注意By
、EC
和WebDriverWait
需要提前import
。By.ID
即以ID
进行查找,同理还有By.NAME
、By.XPATH
、By.CLASS_NAME
、By.LINK_TEXT
等等。 这里为什么要使用它呢?因为我们的网页需要加载时间,如果页面还未加载ID
为 password 的元素,我们代码就会报错。所以上面那句代码也可以改为1 2 3 print ("等待页面加载完成后,获取 ID 为 password 的元素" )time.sleep(10 ) passwd_input = driver.find_element_by_id("password" )
而passwd_input.send_keys( gs105e_conf[“password”] ) 则是模拟键盘输入字符
按顺序点击菜单 QoS --> 速率限制
,见下图。
因为QoS 速率限制
是以iframe
方式加载,所以我们需要先将driver
的当前页面跳转至iframe
上,使用driver.switch_to.frame( iframe ) 来进行跳转。见下图。
点击CheckBox
以选中端口,然后对修改入站速率与出站速率,这里因为出入站速率为下拉列表,所以这里需要使用selenium.webdriver.support.select
类来进行选择,Select( btn_select ).select_by_index( 3 ) 为选择从上数第四选项,第一选项索引为0
.
从iframe
返回主页面并点击应用
。
应用完成后点击退出登录
。
完整代码 上面的代码必须在图形界面运行,因为其会弹出浏览器窗口。而我们的服务器因为没有图形界面运行时会报错,所以这里我们使用--headless
选项来禁止浏览器加载图形界面,使其可以在终端里运行。
下面是最终代码,对上面的代码进行了修饰,并添加了选项功能,可以对 Netgear 网管交换机的某端口进行限速,也可以解除某端口的限速。
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 import time, sys, getopt, osfrom selenium import webdriverfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.support.select import Select_gs105e_dict = { "unlimit" :1 , "limit" :3 , "huawei" :"port2" , "phicomm" :"port5" } gs105e_conf = { "url" :"http://192.168.1.2" , "password" :"0123456789" , "rate" :_gs105e_dict["unlimit" ], "port" :_gs105e_dict["huawei" ], "browser" :"" } def browser (driver ): print ("open gs105 webpage" ) driver.get(gs105e_conf["url" ]) print ("login" ) WebDriverWait(driver, 10 ).until(EC.presence_of_element_located((By.ID,"password" )) ).send_keys(gs105e_conf["password" ]) driver.find_element_by_id("loginBtn" ).click() print ("goto Qos page" ) WebDriverWait(driver,10 ).until(EC.presence_of_element_located((By.ID, "QoS" ))).click() driver.find_element_by_id("QoS_RateLimit" ).click() time.sleep(4 ) driver.switch_to.frame(driver.find_element_by_xpath("//iframe[@id='maincontent']" )) print ("modify the rate" ) WebDriverWait(driver,10 ).until(EC.presence_of_element_located((By.NAME,gs105e_conf["port" ]))).click() Select(driver.find_element_by_name("IngressRate" )).select_by_index(gs105e_conf["rate" ]) Select(driver.find_element_by_name("EgressRate" )).select_by_index(gs105e_conf["rate" ]) print ("click Apply button" ) driver.switch_to.default_content() driver.find_element_by_id("btn_Apply" ).click() WebDriverWait(driver,10 ).until(EC.presence_of_element_located((By.ID,"logout" ))).click() def main (): try : if gs105e_conf["browser" ] == "firefox" : print ("Open [geckodriver]" ) firefox_options = webdriver.FirefoxOptions() firefox_options.add_argument('--headless' ) firefox_options.add_argument("user-agent='Mozilla/5.0 (X11; Linux i686; rv:67.0) Gecko/20100101 Firefox/67.0'" ) driver = webdriver.Firefox(options=firefox_options) elif gs105e_conf["browser" ] == "chrome" : print ("Open [chromedriver]" ) chrome_options = webdriver.ChromeOptions() chrome_options.add_argument('--headless' ) chrome_options.add_argument('--disable-gpu' ) chrome_options.add_argument("user-agent='Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3831.6 Safari/537.36'" ) driver = webdriver.Chrome(options=chrome_options) else : show_help() browser(driver) except : print ("error in script!" ) finally : print ("Close Brwoser!" ) driver.close() def show_help (): print ("automanagetv.py -r <unlimit|limit> -p [tv|phicomm] -b [firefox|chrome]" ) print ("made by haven200@gmail.com, version 0.0.1\n" ) print (" -r, --rate limit: limit the network speed to 1Mb/s" ) print (" unlimit: release network speed limit" ) print (" -p, --port huawei: Huawei Set_Top_Box" ) print (" phicomm: phicomm_n1 Set_Top_Box" ) print (" -b, --browser firefox: use firefox" ) print (" chrome: use chrome" ) sys.exit() if __name__ == "__main__" : if len (sys.argv) == 1 : show_help() if len (os.popen("whereis geckodriver | awk '{print $2}'" ).read()) > 5 : gs105e_conf["browser" ] = "firefox" elif len (os.popen("whereis chromedriver | awk '{print $2}'" ).read()) > 5 : gs105e_conf["browser" ] = "chrome" try : opts, args = getopt.getopt(sys.argv[1 :], "hr:p:b:" , ["rate=" , "port=" , "browser=" ]) except getopt.GetoptError: show_help() for opt, arg in opts: if opt == '-h' : show_help() elif opt in ("-r" , "--rate" ): if arg == "limit" or arg == "unlimit" : gs105e_conf["rate" ] = _gs105e_dict[arg] elif opt in ("-p" , "--port" ): if arg == "huawei" or arg == "phicomm" : gs105e_conf["port" ] = _gs105e_dict[arg] elif opt in ("-b" , "--browser" ): if arg == "firefox" or arg == "chrome" : gs105e_conf["browser" ] = arg main()
脚本的使用方法:
1 2 3 4 :~$ python3 automanagetv.py -r limit -p tv :~$ python3 automanagetv.py -r unlimit -p tv
最后,我们在Cron
里添加定时运行即可。
1 2 3 4 5 :~$ crontab -e 00 12 * * 1-5 /etc/init.d/automanagetv.py -r limit -p tv 00 13 * * 1-5 /etc/init.d/automanagetv.py -r unlimit -p tv 00 19 * * 1-5 /etc/init.d/automanagetv.py -r limit -p tv 40 20 * * 1-5 /etc/init.d/automanagetv.py -r unlimit -p tv
周一至周五,中午12点至13点进行限速,晚上19:00至20:40进行限速。而周六、周日电视就属于熊孩子的了。
References:
selenium.org
testproject
hongkiat
csdn