经常会被问到如何解决验证码的问题,在此记录一下我所知道的几种方式。
对于web应用来说,大部分的系统在用户登录时都要求用户输入验证码,验证码的类型的很多,有字母数字的,有汉字的,甚至还要用户输入一条算术题的答案的,对于系统来说使用验证码可以有效果的防止采用机器猜测方法对口令的刺探,在一定程度上增加了安全性。但对于测试人员来说,不管是进行性能测试还是自动化测试都是一个棘手的问题。
下面来谈一下处理验证码的几种方法。
去掉验证码
这是最简单的方法,对于开发人员来说,只是把验证码的相关代码注释掉即可,如果是在测试环境,这样做可省去了测试人员不少麻烦,如果自动化脚本是要在正式环境跑,这样就给系统带来了一定的风险。
设置万能码
去掉验证码的主要是安全问题,为了应对在线系统的安全性威胁,可以在修改程序时不取消验证码,而是程序中留一个“后门”---设置一个“万能验证码”,只要用户输入这个“万能验证码”,程序就认为验证通过,否则按照原先的验证方式进行验证。
#coding=utf-8 import random #生成0到10之间的随机数 #d = random.uniform(0,10) #print d #生成一个1000到9999之间的随机整数 d = random.randint(1000,9999) print u"生成的随机数:%d " %d i = input(u"请输入随机数:") print i if i == d: print u"登录成功!!" elif i == 1111: print u"登录成功!!" else: print u"请重新输入验证码!"
运行结果:
>>> ================================ restart ================================ >>> 生成的随机数:3764 请输入随机数:1111 1111 登录成功!! >>> ================================ restart ================================ >>> 生成的随机数:3763 请输入随机数:3763 3763 登录成功!! >>> ================================ restart ================================ >>> 生成的随机数:1928 请输入随机数:1354646 1354646 请重新输入验证码!
random
random用于生成随机数
randint()
randint()方法用于生成随机整数,传递的两个参数分别是随机数的范围,randint(1000,9999)第二个参数要大于第一个参数。
我们要求用户输入随机数,并且对用户输入做判断,如果等于生成的随机数那么,登录成功,如果等于1111也算登录成功,否则失败。那么等于1111的判断就是一个万能码。
验证码识别技术
例如可以通过python-tesseract 来识别图片验证码,python-tesseract是光学字符识别tesseract ocr引擎的python封装类。能够读取任何常规的图片文件(jpg, gif ,png , tiff等)。不过,目前市面上的验证码形式繁多,目前任何一种验证码识别技术,识别率都不是100% 。
记录cookie
(适用于ui自动化测试,且目前在大部应用的用户名密码不记录在cookie 或 进行加密处理。)
通过向浏览器中添加cookie 可以绕过登录的验证码,这是比较有意思的一种凯发天生赢家一触即发官网的解决方案。我们可以在用户登录之前,通过add_cookie()方法将用户名密码写入浏览器cookie ,再次访问系统登录链接将自动登录。例如下面的方式:
.... #访问xxxx网站 driver.get("http://www.xxxx.cn/") #将用户名密码写入浏览器cookie driver.add_cookie({'name':'login_usernumber', 'value':'username'}) driver.add_cookie({'name':'login_passwd', 'value':'password'}) #再次访问xxxx网站,将会自动登录 driver.get("http://www.xxxx.cn/") time.sleep(3) .... driver.quit()
使用cookie进行登录最大的难点是如何获得用户名密码的name ,如果找到不到name 的名字,就没办法向value 中输用户名、密码信息。
我建议是可以通过get_cookies()方法来获取登录的所有的cookie信息,从而进行找到用户名、密码的name 对象的名字;当然,最简单的方法还是询问前端开发人员。
总结:
最简单安全,行之有效的方式就是设置万能码,稍微和开发沟通一下就ok了。如果乐于“闷头苦干自力更生”的话也可研究验证码识别技术。
学习unittest 很好的一个切入点就是从selenium ide 录制导出脚本。相信不少新手学习selenium 也是从ied 开始的。
ide学习参考:
借助ied 录制脚本
将脚本导出,保存为baidu.py ,通过python idle编辑器打开。如下:
from selenium import webdriver
from selenium.webdriver.common.by import by
from selenium.webdriver.common.keys import keys
from selenium.webdriver.support.ui import select
from selenium.common.exceptions import nosuchelementexception
import unittest, time, re
class baidu(unittest.testcase):
def setup(self):
self.driver = webdriver.firefox()
self.driver.implicitly_wait(30)
self.base_url = "http://www.baidu.com/"
self.verificationerrors = []
self.accept_next_alert = true
def test_baidu(self):
driver = self.driver
driver.get(self.base_url "/")
driver.find_element_by_id("kw").send_keys("selenium webdriver")
driver.find_element_by_id("su").click()
driver.close()
def is_element_present(self, how, what):
try: self.driver.find_element(by=how, value=what)
except nosuchelementexception, e: return false
return true
def is_alert_present(self):
try: self.driver.switch_to_alert()
except noalertpresentexception, e: return false
return true
def close_alert_and_get_its_text(self):
try:
alert = self.driver.switch_to_alert()
alert_text = alert.text
if self.accept_next_alert:
alert.accept()
else:
alert.dismiss()
return alert_text
finally: self.accept_next_alert = true
def teardown(self):
self.driver.quit()
self.assertequal([], self.verificationerrors)
if __name__ == "__main__":
unittest.main()
加入unittest框架后,看上去比我们之前见的脚本复杂了很多,除了中间操作浏览器的几行,其它都看不懂,不要急,我们来分析一下~!
框架分析
import unittest
相想使用unittest框架,首先要引入unittest 包,这个不多解释。
class baidu(unittest.testcase):
baidu类继承unittest.testcase 类,从testcase类继承是告诉unittest模块的方式,这是一个测试案例。
def setup(self):
self.driver = webdriver.firefox()
self.base_url = "http://www.baidu.com/"
setup 用于设置初始化的部分,在测试用例执行前,这个方法中的函数将先被调用。这里将浏览器的调用和url的访问放到初始化部分。
self.verificationerrors = []
脚本运行时,错误的信息将被打印到这个列表中。
self.accept_next_alert = true
是否继续接受下一下警告(字面意思,没找到解释!)
def test_baidu(self):
driver = self.driver
driver.get(self.base_url "/")
driver.find_element_by_id("kw").send_keys("selenium webdriver")
driver.find_element_by_id("su").click()
test_baidu中放置的就是我们的测试脚本了,这部分我们并不陌生;因为我们执行的脚本就在这里。
def is_element_present(self, how, what):
try: self.driver.find_element(by=how, value=what)
except nosuchelementexception, e: return false
return true
is_element_present函数用来查找页面元素是否存在,在这里用处不大,通常删除。
因为判断页面元素是否存在一般都加在testcase中。
def is_alert_present(self):
try: self.driver.switch_to_alert()
except noalertpresentexception, e: return false
return true
对弹窗异常的处理
def close_alert_and_get_its_text(self):
try:
alert = self.driver.switch_to_alert()
alert_text = alert.text
if self.accept_next_alert:
alert.accept()
else:
alert.dismiss()
return alert_text
finally: self.accept_next_alert = true
关闭警告和对得到文本框的处理,如果不熟悉python的异常处理和if 语句的话,请去补基础知识,这里不多解释。
def teardown(self):
self.driver.quit()
self.assertequal([], self.verificationerrors)
teardown 方法在每个测试方法执行后调用,这个地方做所有清理工作,如退出浏览器等。
self.assertequal([], self.verificationerrors) 是个难点,对前面verificationerrors方法获得的列表进行比较;如查verificationerrors的列表不为空,
输出列表中的报错信息。
而且,这个东西,也可以将来被你自己更好的调用和使用,根据自己的需要写入你希望的信息。(rabbit 告诉我的)
if __name__ == "__main__":
unittest.main()
unitest.main()函数用来测试 类中以test开头的测试用例
执行结果
这样一一分析下来,我们对unittest 框架有了初步的了解。运行脚本,因为引入了unittest 框架,所以控制台输出了脚本执行情况的信息。
>>> ========================= restart ================================
>>>
.
----------------------------------------------------------------------
ran 1 test in 10.656s
ok
>>>
很帅吧!? 后面将以unittest 为基础,向新的征程进发~!
actionchains 类
测试的产品中有一个操作是右键点击文件列表会弹出一个快捷菜单,可以方便的选择快捷菜单中的选择对文件进行操作(删除、移动、重命名),之前学习元素的点击非常简单:
driver.find_element_by_id(“xxx”).click()
那么鼠标的双击、右击、拖动等是否也是这样的写法呢?例如右击:
driver.find_element_by_id(“xxx”).context_click()
经过运行脚本得到了下面的错误提示:
attributeerror: 'webelement' object has no attribute 'context_click'
提示右点方法不属于webelement 对象,通过查找文档,发现属于actionchains 类,但文档中没有具体写法。这里要感谢 北京-qc-rabbit 的指点,其实整个python selenium 学习过程都要感谢 北京-qc-rabbit 的指点。
下面介绍鼠标右键的用法,以快播私有云为例:
#coding=utf-8 from selenium import webdriver from selenium.webdriver.common.action_chains import actionchains import time driver = webdriver.firefox() driver.get("http://passport.kuaibo.com/login/?referrer=http://webcloud.kuaibo.com/") #登陆快播私有云 driver.find_element_by_id("user_name").send_keys("username") driver.find_element_by_id("user_pwd").send_keys("123456") driver.find_element_by_id("dl_an_submit").click() time.sleep(3) #定位到要右击的元素 qqq =driver.find_element_by_xpath("/html/body/div/div[2]/div[2]/div/div[3]/table/tbody/tr/td[2]") #对定位到的元素执行鼠标右键操作 actionchains(driver).context_click(qqq).perform() ''' #你也可以使用三行的写法,但我觉得上面两行写法更容易理解 chain = actionchains(driver) implement = driver.find_element_by_xpath("/html/body/div/div[2]/div[2]/div/div[3]/table/tbody/tr/td[2]") chain.context_click(implement).perform() ''' time.sleep(3) #休眠3秒 driver.close()
这里需要注意的是,在使用actionchains 类之前,要先将包引入。
右击的操作会了,下面的其它方法比葫芦画瓢也能写出来。
鼠标双击的写法:
#定位到要双击的元素 qqq =driver.find_element_by_xpath("xxx") #对定位到的元素执行鼠标双击操作 actionchains(driver).double_click(qqq).perform()
鼠标拖放操作的写法:
#定位元素的原位置 element = driver.find_element_by_name("source") #定位元素要移动到的目标位置 target = driver.find_element_by_name("target") #执行元素的移动操作 actionchains(driver).drag_and_drop(element, target).perform()
actionchains 类不仅仅是只包含了上面的三个方法,下面将方法列出:
class actionchains(driver)
driver:the webdriver instance which performs user actions.
generate user actions. all actions are stored in the actionchains object. call perform() to fire stored actions.
– perform()
performs all stored actions.
– click(on_element=none)
clicks an element.
on_element:the element to click. if none, clicks on current mouse position.
– click_and_hold(on_element)
holds down the left mouse button on an element.
on_element:the element to mouse down. if none, clicks on current mouse position.
– context_click(on_element)
performs a context-click (right click) on an element.
on_element:the element to context-click. if none, clicks on current mouse position.
– double_click(on_element)
double-clicks an element.
on_element:the element to double-click. if none, clicks on current mouse position.
– drag_and_drop(source, target)
holds down the left mouse button on the source element, then moves to the target element and releases the mouse button.
source:the element to mouse down.
target: the element to mouse up.
– key_down(key, element=none)
sends a key press only, without releasing it. should only be used with modifier keys (control, alt andshift).
key:the modifier key to send. values are defined in keys class.
element:the element to send keys. if none, sends a key to current focused element.
– key_up(key, element=none)
releases a modifier key.
key:the modifier key to send. values are defined in keys class.
element:the element to send keys. if none, sends a key to current focused element.
– move_by_offset(xoffset, yoffset)
moving the mouse to an offset from current mouse position.
xoffset:x offset to move to.yoffset:y offset to move to.
– move_to_element(to_element)
moving the mouse to the middle of an element.
to_element: the element to move to.
– move_to_element_with_offset(to_element, xoffset, yoffset)
move the mouse by an offset of the specificed element. offsets are relative to the top-left corner of the
element.
to_element: the element to move to.xoffset:x offset to move to.yoffset:y offset to move to.
– release(on_element)
releasing a held mouse button.
on_element:the element to mouse up.
– send_keys(*keys_to_send)
sends keys to current focused element.
keys_to_send:the keys to send.
– send_keys_to_element(self, element,*keys_to_send):
sends keys to an element.
element:the element to send keys.keys_to_send:the keys to send.
通过webdriver 操作cookie 是一件非常有意思的事儿,有时候我们需要了解浏览器中是否存在了某个cookie 信息,webdriver 可以帮助我们读取、添加,删除cookie信息。
打印cookie信息
#coding=utf-8 from selenium import webdriver import time driver = webdriver.chrome() driver.get("http://www.youdao.com") # 获得cookie信息 cookie= driver.get_cookies() #将获得cookie的信息打印 print cookie driver.quit()
运行打印信息:
[{u'domain': u'.youdao.com', u'secure': false, u'value': u'agfzbg9nz2vkpxrydwu=', u'expiry': 1408430390.991375, u'path': u'/', u'name': u'_pref_anonyuser__myth'}, {u'domain': u'.youdao.com', u'secure': false, u'value': u'1777851312@218.17.158.115', u'expiry': 2322974390.991376, u'path': u'/', u'name': u'outfox_search_user_id'}, {u'path': u'/', u'domain': u'www.youdao.com', u'name': u'jsessionid', u'value': u'abcux9zdw0minadihtvcu', u'secure': false}]
对cookie的操作
上面的方式打印了所有cookie信息表,太多太乱,我们只想有真对性的打印自己想要的信息,看下面的例子
#coding=utf-8 from selenium import webdriver import time driver = webdriver.firefox() driver.get("http://www.youdao.com") #向cookie的name 和value添加会话信息。 driver.add_cookie({'name':'key-aaaaaaa', 'value':'value-bbbb'}) #遍历cookies中的name 和value信息打印,当然还有上面添加的信息 for cookie in driver.get_cookies(): print "%s -> %s" % (cookie['name'], cookie['value']) # 下面可以通过两种方式删除cookie # 删除一个特定的cookie driver.delete_cookie("cookiename") # 删除所有cookie driver.delete_all_cookies() time.sleep(2) driver.close()
运行打印信息:
youdao_mobile_access_type -> 1 _pref_anonyuser__myth -> agfzbg9nz2vkpxrydwu= outfox_search_user_id -> -1046383847@218.17.158.115 jsessionid -> abc7qse_sbgsvgnvlbvcu key-aaaaaaa -> value-bbbb # 这一条是我们自己添加的
通过博客园登陆来分析cookie
#coding=utf-8 from selenium import webdriver import time driver = webdriver.firefox() driver.get("http://passport.cnblogs.com/login.aspx?returnurl=http://www.cnblogs.com/fnng/admin/editposts.aspx") time.sleep(3) driver.maximize_window() # 浏览器全屏显示 #通过用户名密码登陆 driver.find_element_by_id("tbusername").send_keys("fnngj") driver.find_element_by_id("tbpassword").send_keys("123456") #勾选保存密码 driver.find_element_by_id("chkremember").click() time.sleep(3) #点击登陆按钮 driver.find_element_by_id("btnlogin").click() #获取cookie信息并打印 cookie= driver.get_cookies() print cookie time.sleep(2) driver.close()
运行打印信息:
#第一次执行信息 >>> [{u'domain': u'.cnblogs.com', u'name': u'.dottextcookie', u'value': u'c709f15a8bc0b3e8d9ad1f68b371053849f7fee31f73f1292a150932ff09a7b0d4a1b449a32a6b24ad986cdb05b9998471a37f39c3b637e85e481aa986d3f8c187d7708028f9d4ed3b326b46dc43b416c47b84d706099ed1d78b6a0fc72dcf948db9d5cbf99d7848fdb78324', u'expiry': none, u'path': u'/', u'secure': false}] >>> ========================= restart ================================ #第二次执行信息 >>> [{u'domain': u'.cnblogs.com', u'name': u'.dottextcookie', u'value': u'5bb735cad62e99f8ccb9331c32724e2975a0150d199f4243ad19357b3f99a416a93b2e803f4d5c9d065429713be8b5db4ed760edcbaf492eabe2158b3a6fbbea2b95c4da3d2efeadacc3247040906f1462731f652199e2a8befd8a9b6aae87cf3059a3caeb9ab0d8b1b7ad2a', u'expiry': 1379502502, u'path': u'/', u'secure': false}] >>>
第一次注释掉勾选保存密码的操作,第二次通过勾选保存密码获得cookie信息 ;来看两次运行结果的cookie的何不同:
u'expiry': none
u'expiry': 1379502502
通过对比发现,不勾选保存密码时expiry 的值为none ; 那么就可以初步判断勾选保存密码的操作在cookie 中起到了作用。至于是否准确可以再做进一步的分析。
键盘按键键用法:
#coding=utf-8 from selenium import webdriver from selenium.webdriver.common.keys import keys #需要引入keys包 import os,time driver = webdriver.firefox() driver.get("http://passport.kuaibo.com/login/?referrer=http://webcloud.kuaibo.com/") time.sleep(3) driver.maximize_window() # 浏览器全屏显示 driver.find_element_by_id("user_name").clear() driver.find_element_by_id("user_name").send_keys("fnngj") #tab的定位相相于清除了密码框的默认提示信息,等同上面的clear() driver.find_element_by_id("user_name").send_keys(keys.tab) time.sleep(3) driver.find_element_by_id("user_pwd").send_keys("123456") #通过定位密码框,enter(回车)来代替登陆按钮 driver.find_element_by_id("user_pwd").send_keys(keys.enter) ''' #也可定位登陆按钮,通过enter(回车)代替click() driver.find_element_by_id("login").send_keys(keys.enter) ''' time.sleep(3) time.sleep(3) driver.quit()
要想调用键盘按键操作需要引入keys包:
from selenium.webdriver.common.keys import keys
通过send_keys()调用按键:
send_keys(keys.tab) # tab
send_keys(keys.enter) # 回车
注意:这个操作和页面元素的遍历顺序有关,假如当前定位在账号输入框,按键盘的tab键后遍历的不是密码框,那就不法输入密码。 假如输入密码后,还有需要填写验证码,那么回车也起不到登陆的效果。
这并不是一种好的方法,这里只是为了验证单个按键的用法
键盘组合键用法:
#coding=utf-8 from selenium import webdriver from selenium.webdriver.common.keys import keys import os,time driver = webdriver.firefox() driver.get("http://www.baidu.com") #输入框输入内容 driver.find_element_by_id("kw").send_keys("selenium") time.sleep(3) #ctrl a 全选输入框内容 driver.find_element_by_id("kw").send_keys(keys.control,'a') time.sleep(3) #ctrl x 剪切输入框内容 driver.find_element_by_id("kw").send_keys(keys.control,'x') time.sleep(3) #输入框重新输入内容,搜索 driver.find_element_by_id("kw").send_keys(u"虫师 cnblogs") driver.find_element_by_id("su").click() time.sleep(3) driver.quit()
上面的操作没有实际意义,但向我们演示了键盘组合按键的用法。
输入中文问题
顺便解决了一个困扰我到现的一个输入中文件的问题。selenium2 python在send_keys()中输入中文一直报错,其实前面加个小u 就解决了:
send_keys(u"输入中文")
基本上键盘上所有的按键都可以模拟
module:selenium.webdriver.common.keys • class keys() – null = u’ue000’ – cancel = u’ue001’ # ^break – help = u’ue002’ – back_space = u’ue003’ – tab = u’ue004’ – clear = u’ue005’ – return = u’ue006’ – enter = u’ue007’ – shift = u’ue008’ – left_shift = u’ue008’ # alias – control = u’ue009’ – left_control = u’ue009’ # alias – alt = u’ue00a’ – left_alt = u’ue00a’ # alias – pause = u’ue00b’ – escape = u’ue00c’ – space = u’ue00d’ – page_up = u’ue00e’ – page_down = u’ue00f’ – end = u’ue010’ – home = u’ue011’ – left = u’ue012’ – arrow_left = u’ue012’ # alias – up = u’ue013’ – arrow_up = u’ue013’ # alias – right = u’ue014’ – arrow_right = u’ue014’ # alias – down = u’ue015’ – arrow_down = u’ue015’ # alias – insert = u’ue016’ – delete = u’ue017’ – semicolon = u’ue018’ – equals = u’ue019’ – numpad0 = u’ue01a’ # numbe pad keys – numpad1 = u’ue01b’ – numpad2 = u’ue01c’ – numpad3 = u’ue01d’ – numpad4 = u’ue01e’ – numpad5 = u’ue01f’ – numpad6 = u’ue020’ – numpad7 = u’ue021’ – numpad8 = u’ue022’ – numpad9 = u’ue023’ – multiply = u’ue024’ – add = u’ue025’ – separator = u’ue026’ – subtract = u’ue027’ – decimal = u’ue028’ – divide = u’ue029’ – f1 = u’ue031’ # function keys – f2 = u’ue032’ – f3 = u’ue033’ – f4 = u’ue034’ – f5 = u’ue035’ – f6 = u’ue036’ – f7 = u’ue037’ – f8 = u’ue038’ – f9 = u’ue039’ – f10 = u’ue03a’ – f11 = u’ue03b’ – f12 = u’ue03c’ – meta = u’ue03d’ – command = u’ue03d’
--------------------------
学习更多selenium 内容:
下拉框是我们最常见的一种页面元素,对于一般的元素,我们只需要一次就定位,但下拉框里的内容需要进行两次定位,先定位到下拉框,再定位到下拉框内里的选项。
drop_down.html
<html> <body> <select id="shippingmethod" onchange="updateshipping(options[selectedindex]);" name="shippingmethod"> <option value="12.51">ups next day air ==> $12.51option> <option value="11.61">ups next day air saver ==> $11.61option> <option value="10.69">ups 3 day select ==> $10.69option> <option value="9.03">ups 2nd day air ==> $9.03option> <option value="8.34">ups ground ==> $8.34option> <option value="9.25">usps priority mail insured ==> $9.25option> <option value="7.45">usps priority mail ==> $7.45option> <option value="3.20" selected="">usps first class ==> $3.20option> select> body> html>
将上面的代码保存成html通过浏览器打开会看到一个最简单常见的下拉框,下拉列表有几个选项。
现在我们来选择下拉列表里的$10.69
#-*-coding=utf-8 from selenium import webdriver import os,time driver= webdriver.firefox() file_path = 'file:///' os.path.abspath('drop_down.html') driver.get(file_path) time.sleep(2) m=driver.find_element_by_id("shippingmethod") m.find_element_by_xpath("//option[@value='10.69']").click() time.sleep(3) driver.quit()
解析:
这里可能和之前的操作有所不同,首先要定位到下拉框的元素,然后选择下拉列表中的选项进行点击操作。
m=driver.find_element_by_id("shippingmethod")
m.find_element_by_xpath("//option[@value='10.69']").click()
百度搜索设置下拉框操作
#-*-coding=utf-8 from selenium import webdriver import os,time driver= webdriver.firefox() driver.get("http://www.baidu.com")
#进入搜索设置页 driver.find_element_by_link_text("搜索设置").click()
#设置每页搜索结果为100条 m=driver.find_element_by_name("nr") m.find_element_by_xpath("//option[@value='100']").click() time.sleep(2)
#保存设置的信息 driver.find_element_by_xpath("//input[@value='保存设置']").click() time.sleep(2) driver.switch_to_alert().accept()
#跳转到百度凯发k8网页登录首页后,进行搜索表(一页应该显示100条结果) driver.find_element_by_id("kw").send_keys("selenium") driver.find_element_by_id("su").click() time.sleep(3) driver.quit()
解析:
当我们在保存百度的设置时会会弹出一个确定按钮;我们并没按照常规的方法去定位弹窗上的“确定”按钮,而是使用:
driver.switch_to_alert().accept()
完成了操作,这是因为弹窗比较是一个具有唯一性的警告信息,所以可以用这种简便的方法处理。
– switch_to_alert()
焦点集中到页面上的一个警告(提示)
– accept()
接受警告提示
--------------------------
学习更多selenium 内容:
调用js方法
在当前窗口/框架 同步执行javascript
脚本:javascript的执行。
*参数:适用任何javascript脚本。
使用:
driver.execute_script(‘document.title’)
使快播登陆用户名输入框标红显示:
#coding=utf-8 from selenium import webdriver import time driver = webdriver.firefox() driver.get("http://passport.kuaibo.com/login/?referrer=http://vod.kuaibo.com/?t=home") #给用户名的输入框标红 js="var q=document.getelementbyid(\"user_name\");q.style.border=\"1px solid red\";" #调用js driver.execute_script(js) time.sleep(3) driver.find_element_by_id("user_name").send_keys("username") driver.find_element_by_id("user_pwd").send_keys("password") driver.find_element_by_id("dl_an_submit").click() time.sleep(3) driver.quit()
js解释:
q=document.getelementbyid(\"user_name\")
元素q的id 为user_name
q.style.border=\"1px solid red\
元素q的样式,边框为1个像素红色
隐藏元素
js.html
<html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8" /> <title>jstitle> <script type="text/javascript" async="" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">script> <link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet" /> <script type="text/javascript"> $(document).ready(function(){ $('#tooltip').tooltip({"placement": "right"}); }); script> head> <body> <h3>jsh3> <div class="row-fluid"> <div class="span6 well"> <a id="tooltip" href="#" data-toggle="tooltip" title=" selenium-webdriver(python)">hover to see tooltipa> <a class="btn">buttona> div> div> body> <script src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js">script> html>
(保持html文件与执行脚本在同一目录下)
执行js一般有两种场景:
#coding=utf-8 from selenium import webdriver import time,os driver = webdriver.firefox() file_path = 'file:///' os.path.abspath('js.html') driver.get(file_path) #######通过js 隐藏选中的元素######### #第一种方法: driver.execute_script('$("#tooltip").fadeout();') time.sleep(5) #第二种方法: button = driver.find_element_by_class_name('btn') driver.execute_script('$(arguments[0]).fadeout()',button) time.sleep(5) driver.quit()
js解释:
arguments对象,它是调用对象的一个特殊属性,用来引用arguments对象。arugments对象就像数组。
fadeout() 方法使用淡出效果来隐藏被选元素,假如该元素是隐藏的。
ps:可以看到js 可以做selenium 做不到的事情,但是在什么样的自动化的时候才能(或必须)要js帮忙,我还没遇到过。不过js可以selenium完成更强大的功能,这是不容置疑的。
另外,之前没有学过js ,所以js代码很陌生。如果有时间的话也建议各位同学补充这方面的知识。ui自动化离不开前端技术。
本节知识点:
多层框架或窗口的定位:
智能等待:
对于一个现代的web应用,经常会出现框架(frame) 或窗口(window)的应用,这也就给我们的定位带来了一个难题。
有时候我们定位一个元素,定位器没有问题,但一直定位不了,这时候就要检查这个元素是否在一个frame中,seelnium webdriver 提供了一个switch_to_frame方法,可以很轻松的来解决这个问题。
frame.html
<html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8" /> <title>frametitle> <script type="text/javascript" async="" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js ">script> <link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet" /> <script type="text/javascript"> $(document).ready(function(){ }); script> head> <body> <div class="row-fluid"> <div class="span10 well"> <h3>frameh3> <iframe id="f1" src="inner.html" width="800", height="600">iframe> div> div> body> <script src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js">script> html>
inner.html
<html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8" /> <title>innertitle> head> <body> <div class="row-fluid"> <div class="span6 well"> <h3>innerh3> <iframe id="f2" src="http://www.baidu.com" width="700" height="500">iframe> <a href="javascript:alert('watir-webdriver better than selenium webdriver;')">clicka> div> div> body> html>
frame.html 中嵌套inner.html ,两个文件和我们的脚本文件放同一个目录下:
switch_to_frame()
操作上面页面,代码如下:
#coding=utf-8 from selenium import webdriver import time import os browser = webdriver.firefox() file_path = 'file:///' os.path.abspath('frame.html') browser.get(file_path) browser.implicitly_wait(30) #先找到到ifrome1(id = f1) browser.switch_to_frame("f1") #再找到其下面的ifrome2(id =f2) browser.switch_to_frame("f2")
#下面就可以正常的操作元素了 browser.find_element_by_id("kw").send_keys("selenium") browser.find_element_by_id("su").click() time.sleep(3)
browser.quit()
driver.switch_to_window()
有可能嵌套的不是框架,而是窗口,还有真对窗口的方法:switch_to_window
用法与switch_to_frame 相同:
driver.switch_to_window("windowname")
implicitly_wait()
细心的话会发现上面的例子中有browser.implicitly_wait(30),它的用法应该比time.sleep() 更智能,后者只能选择一个固定的时间的等待,前者可以在一个时间范围内智能的等待。
文档解释:
selenium.webdriver.remote.webdriver.implicitly_wait(time_to_wait)
隐式地等待一个无素被发现或一个命令完成;这个方法每次会话只需要调用一次
time_to_wait: 等待时间
用法:
driver.implicitly_wait(30)
--------------------------
学习更多selenium 内容:
操作对象:
webelement 另一些常用方法:
======================================
操作测试对象
前面讲到了不少知识都是定位元素,定位只是第一步,定位之后需要对这个原素进行操作。
鼠标点击呢还是键盘输入,这要取决于我们定位的是按钮还输入框。
一般来说,webdriver中比较常用的操作对象的方法有下面几个
· click 点击对象
· send_keys 在对象上模拟按键输入
· clear 清除对象的内容,如果可以的话
在我们本系列开篇的第一个例子里就用到了到click 和send_skys ,别翻回去找了,我再贴一下代码:
# coding = utf-8 from selenium import webdriver browser = webdriver.firefox() browser.get("http://www.baidu.com") browser.find_element_by_id("kw").clear() browser.find_element_by_id("kw").send_keys("selenium") browser.find_element_by_id("su").click() browser.quit()
send_keys("xx") 用于在一个输入框里输入内容。
click() 用于点击一个按钮。
clear() 用于清除输入框的内容,比如百度输入框里默认有个“请输入关键字”的信息,再比如我们的登陆框一般默认会有“账号”“密码”这样的默认信息。clear可以帮助我们清除这些信息。
webelement 另一些常用方法:
· text 获取该元素的文本
· submit 提交表单
· get_attribute 获得属性值
text
用于获取元素的文本信息
下面把百度凯发k8网页登录首页底部的声明打印输出
#coding=utf-8 from selenium import webdriver import time driver = webdriver.firefox() driver.get("http://www.baidu.com") time.sleep(2) #id = cp 元素的文本信息 data=driver.find_element_by_id("cp").text print data #打印信息 time.sleep(3) driver.quit()
输出:
>>> ©2013 baidu 使用百度前必读 京icp证030173号
submit
提交表单
我们把“百度一下”的操作从click 换成submit :
#coding=utf-8 from selenium import webdriver import time driver = webdriver.firefox() driver.get("http://www.baidu.com") driver.find_element_by_id("kw").send_keys("selenium") time.sleep(2) #通过submit() 来操作 driver.find_element_by_id("su").submit() time.sleep(3) driver.quit()
这里用submit 与click的效果一样,我暂时还没想到只能用submit 不能用click的场景。他们之间到底有啥区别,知道的同学请留言告诉我。
get_attribute
获得属性值。
这个函数的用法前面已经有出现过,在定位一组元素的时候有使用到它,只是我们没有做过多的解释。
一般用法:
select = driver.find_element_by_tag_name("select") alloptions = select.find_elements_by_tag_name("option") for option in alloptions: print "value is: " option.get_attribute("value") option.click()
具体应用参考:
定位一组元素:http://www.cnblogs.com/fnng/p/3190966.html
小结:
学到这里我们是不是已经撑握了不少知识,简单的操作浏览器,定位元素,操作元素以及打印一些信息。其实,我们前面的学习中大多使用的是webelement 里的方法。
webelement的方法:
一般来说,所有有趣的操作与页面进行交互的有趣的操作,都通过 webelement 完成
classselenium.webdriver.remote.webelement.webelement(parent, id_)
这个类代表html页面元素
id_ #当前元素的id tag_name #获取元素标签名的属性 text #获取该元素的文本。 click() #单击(点击)元素 submit() #提交表单 clear() #清除一个文本输入元素的文本 get_attribute(name) #获得属性值 s_selected(self) #元素是否被选择 whether the element is selected. is_enabled() #元素是否被启用 find_element_by_id(id_) find_elements_by_id(id_) #查找元素的id find_element_by_name(name) find_elements_by_name(name) #查找元素的name find_element_by_link_text(link_text) find_elements_by_link_text(link_text) #查找元素的链接文本 find_element_by_partial_link_text(link_text) find_elements_by_partial_link_text(link_text) #查找元素的链接的部分文本 find_element_by_tag_name(name) find_elements_by_tag_name(name) #查找元素的标签名 find_element_by_xpath(xpath) #查找元素的xpath find_elements_by_xpath(xpath) #查找元素内的子元素的xpath find_element_by_class_name(name) #查找一个元素的类名 find_elements_by_class_name(name) #查找元素的类名 find_element_by_css_selector(css_selector) #查找并返回一个元素的css 选择器 find_elements_by_css_selector(css_selector) #查找并返回多个元素的css 选择器列表 send_keys(*value) #模拟输入元素
--------------------------
学习更多selenium 内容:
场景:
假如两个控件,他们长的一模样,还都叫“张三”,唯一的不同是一个在北京,一个在上海,那我们就可以通过,他们的城市,区,街道,来找到他们。
在实际的测试中也经常会遇到这种问题:页面上有很多个属性基本相同的元素,现在需要具体定位到其中的一个。由于属性基本相当,所以在定位的时候会有些麻烦,这时候就需要用到层级定位。先定位父元素,然后再通过父元素定位子孙元素。
<html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8" /> <title>level locatetitle> <script type="text/javascript" async="" src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">script> <link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet" /> head> <body> <h3>level locateh3> <div class="span3"> <div class="well"> <div class="dropdown"> <a class="dropdown-toggle" data-toggle="dropdown" href="#">link1a> <ul class="dropdown-menu" role="menu" aria-labelledby="dlabel" id="dropdown1" > <li><a tabindex="-1" href="#">actiona>li> <li><a tabindex="-1" href="#">another actiona>li> <li><a tabindex="-1" href="#">something else herea>li> <li class="divider">li> <li><a tabindex="-1" href="#">separated linka>li> ul> div> div> div> <div class="span3"> <div class="well"> <div class="dropdown"> <a class="dropdown-toggle" data-toggle="dropdown" href="#">link2a> <ul class="dropdown-menu" role="menu" aria-labelledby="dlabel" > <li><a tabindex="-1" href="#">actiona>li> <li><a tabindex="-1" href="#">another actiona>li> <li><a tabindex="-1" href="#">something else herea>li> <li class="divider">li> <li><a tabindex="-1" href="#">separated linka>li> ul> div> div> div> body> <script src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js">script> html>
将这段代码保存复制到记事本中,将保存成level_locate.html文件,(注意,这个页面需要和我们的自动化脚本放在同一个目录下)浏览器打开:
这里自制了一个页面,上面有两个文字链接,点击两个链接会弹出一模一样的的两个下拉菜单,这两个菜单的属性基本一样。那么我如何区分找到相应的菜单项呢?
方法如下:
# -*- coding: utf-8 -*- from selenium import webdriver from selenium.webdriver.support.ui import webdriverwait import time import os dr = webdriver.firefox() file_path = 'file:///' os.path.abspath('level_locate.html') dr.get(file_path) #点击link1链接(弹出下拉列表) dr.find_element_by_link_text('link1').click() #找到id 为dropdown1的父元素 webdriverwait(dr, 10).until(lambda the_driver: the_driver.find_element_by_id('dropdown1').is_displayed()) #在父亲元件下找到link为action的子元素 menu = dr.find_element_by_id('dropdown1').find_element_by_link_text('action') #鼠标定位到子元素上 webdriver.actionchains(dr).move_to_element(menu).perform() time.sleep(2) dr.quit()
定位思路:
具体思路是:先点击显示出1个下拉菜单,然后再定位到该下拉菜单所在的ul,再定位这个ul下的某个具体的link。在这里,我们定位第1个下拉菜单中的action这个选项。
---------------------------------------------------------------------
虽然我每行代码前叫了注释,但可能还是不太容易理解,因为里面多了不少以前没见过的新东东。
webdriverwait(dr, 10)
10秒内每隔500毫秒扫描1次页面变化,当出现指定的元素后结束。dr就不解释了,前面操作webdriver.firefox()的句柄
is_displayed()
该元素是否用户可以见
class actionchains(driver)
driver: 执行用户操作实例webdriver
生成用户的行为。所有的行动都存储在actionchains对象。通过perform()存储的行为。
move_to_element(menu)
移动鼠标到一个元素中,menu上面已经定义了他所指向的哪一个元素
to_element:元件移动到
perform()
执行所有存储的行为
------------------------------需要我们日常工作中细细品味、慢慢消化这些函数的用法
其实,啰嗦了这么多,我们只是想达到一种效果,“下拉列表中action选项处于被选中状态”,通过鼠标移动到选项上就达到到了这种效果,但通过程序模拟确实比较麻烦:
--------------------------
学习更多selenium 内容:
如何定位一组元素?
场景
从上一节的例子中可以看出,webdriver可以很方便的使用findelement方法来定位某个特定的对象,不过有时候我们却需要定位一组对象,
这时候就需要使用findelements方法。
定位一组对象一般用于以下场景:
· 批量操作对象,比如将页面上所有的checkbox都勾上
· 先获取一组对象,再在这组对象中过滤出需要具体定位的一些对象。比如定位出页面上所有的checkbox,然后选择最后一个
<html> <head> <meta http-equiv="content-type" content="text/html;charset=utf-8" /> <title>checkboxtitle> <script type="text/javascript" async="" src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js">script> <link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" rel="stylesheet" /> <script src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/js/bootstrap.min.js">script> head> <body> <h3>checkboxh3> <div class="well"> <form class="form-horizontal"> <div class="control-group"> <label class="control-label" for="c1">checkbox1label> <div class="controls"> <input type="checkbox" id="c1" /> div> div> <div class="control-group"> <label class="control-label" for="c2">checkbox2label> <div class="controls"> <input type="checkbox" id="c2" /> div> div> <div class="control-group"> <label class="control-label" for="c3">checkbox3label> <div class="controls"> <input type="checkbox" id="c3" /> div> div> <div class="control-group"> <label class="control-label" for="r">radiolabel> <div class="controls"> <input type="radio" id="r1" /> div> div> <div class="control-group"> <label class="control-label" for="r">radiolabel> <div class="controls"> <input type="radio" id="r2" /> div> div> form> div> body> html>
将这段代码保存复制到记事本中,将保存成checkbox.html文件。(注意,这个页面需要和我们的自动化脚本放在同一个目录下)
第一种方法:
通过浏览器打个这个页面我们看到三个复选框和两个单选框。下面我们就来定位这三个复选框。
# -*- coding: utf-8 -*- from selenium import webdriver import time import os dr = webdriver.firefox() file_path = 'file:///' os.path.abspath('checkbox.html') dr.get(file_path) # 选择页面上所有的input,然后从中过滤出所有的checkbox并勾选之 inputs = dr.find_elements_by_tag_name('input') for input in inputs: if input.get_attribute('type') == 'checkbox': input.click() time.sleep(2) dr.quit()
你可以试着把input.get_attribute('type') == 'checkbox' 中的checkbox 变成radio ,那这个脚本定位的会是两个单选框。
第二种定位方法:
# -*- coding: utf-8 -*- from selenium import webdriver import time import os dr = webdriver.firefox() file_path = 'file:///' os.path.abspath('checkbox.html') dr.get(file_path) # 选择所有的checkbox并全部勾上 checkboxes = dr.find_elements_by_css_selector('input[type=checkbox]') for checkbox in checkboxes: checkbox.click() time.sleep(2) # 打印当前页面上有多少个checkbox print len(dr.find_elements_by_css_selector('input[type=checkbox]')) time.sleep(2) dr.quit()
第二种写法与第一种写法差别不大,都是通过一个循环来勾选控件;如果你学过上一章的话,细心的你一定发现用的定位函数不一样,
第一种用的name ,第二种用的css 。
如何去掉勾选:
还有一个问题,有时候我们并不想勾选页面的所有的复选框(checkbox),可以通过下面办法把最后一个被勾选的框去掉。如下:
# -*- coding: utf-8 -*- from selenium import webdriver import time import os dr = webdriver.firefox() file_path = 'file:///' os.path.abspath('checkbox.html') dr.get(file_path) # 选择所有的checkbox并全部勾上 checkboxes = dr.find_elements_by_css_selector('input[type=checkbox]') for checkbox in checkboxes: checkbox.click() time.sleep(2) # 把页面上最后1个checkbox的勾给去掉 dr.find_elements_by_css_selector('input[type=checkbox]').pop().click() time.sleep(2) dr.quit()
其实,去掉勾选表也逻辑也非常简单,就是再次点击勾选的按钮。可能我们比较迷惑的是如何找到“最后一个”按钮。pop() 可以实现这个功能。
好吧!在web自动化的学习过程中,我们必须要知道一些前端的东西,这里扩展一下:
http://www.w3school.com.cn/js/jsref_pop.asp
尝试:
把find_elements_by_css_selector('input[type=checkbox]').pop().click() 中的checkbox 变成radio 会是什么效果,自己尝试一下吧!
打印url
上一节讲到,可以将浏览器的title打印出来,这里再讲个简单的,把当前url打印出来。其实也没啥大用,可以做个凑数的用例。
#coding=utf-8 from selenium import webdriver import time browser = webdriver.firefox() url= 'http://www.baidu.com' #通过get方法获取当前url打印 print "now access %s" %(url) browser.get(url) time.sleep(2) browser.find_element_by_id("kw").send_keys("selenium") browser.find_element_by_id("su").click() time.sleep(3) browser.quit()
其实,我们可以把这用户登录成功后的url打印,用于验证用户登录成功。
又或者,我们打印其它信息,比如,一般的登录成功页会出现“欢迎用户名”,可以将这个信息打印表明用户登录成功。(如何实现,你自己琢磨一下吧~!)
将浏览器最大化
我们知道调用启动的浏览器不是全屏的,这样不会影响脚本的执行,但是有时候会影响我们“观看”脚本的执行。
#coding=utf-8 from selenium import webdriver import time browser = webdriver.firefox() browser.get("http://www.baidu.com") time.sleep(2) print "浏览器最大化" browser.maximize_window() #将浏览器最大化显示 time.sleep(2) browser.find_element_by_id("kw").send_keys("selenium") browser.find_element_by_id("su").click() time.sleep(3) browser.quit()
设置浏览器固定宽、高
最大化还是不够灵活,能不能随意的设置浏览的宽、高显示?当然是可以的。
#coding=utf-8 from selenium import webdriver import time browser = webdriver.firefox() browser.get("http://m.mail.10086.cn") time.sleep(2) print "设置浏览器宽480、高800显示" browser.set_window_size(480, 800) #参数数字为像素点 time.sleep(3) browser.quit()
这个需求也还是有的,比如我们通过pc浏览器在访问一下手机网站时,调整浏览器为手机屏幕的宽、高,容易发现一些显示问题。(上面的手机邮箱网站就是笔者测试过的一个产品)
操控浏览器前进、后退
浏览器上有一个后退、前进按钮,对于浏览网页的人是比较方便的;对于做web自动化测试的同学来说应该算是一个比较难模拟的问题;其实很简单,下面看看python的实现方式
#coding=utf-8 from selenium import webdriver import time browser = webdriver.firefox() #访问百度凯发k8网页登录首页 first_url= 'http://www.baidu.com' print "now access %s" %(first_url) browser.get(first_url) time.sleep(2) #访问新闻页面 second_url='http://news.baidu.com' print "now access %s" %(second_url) browser.get(second_url) time.sleep(2) #返回(后退)到百度凯发k8网页登录首页 print "back to %s "%(first_url) browser.back() time.sleep(1) #前进到新闻页 print "forward to %s"%(second_url) browser.forward() time.sleep(2) browser.quit()
为了使过程让你看得更清晰,在每一步操作上都加了print 和sleep 。
说实话,这两个功能平时不太常用,所能想到的场景就是几个页面来回跳转,但又不想用get url的情况下。
--------------------------
学习更多selenium 内容:
pasting
官方api:
webdriver python:
之前的菜鸟系列是基于java的,一年没学其实也忘的差不多了,目前所测的产品部分也是python写的,而且团队也在推广python ,其实就测试人员来说,python也相当受欢迎。易学,易用。翻翻各测试招聘,python出现的概率也颇高。
平台搭建:
前一篇中已经介绍,如果你也想体验一下自动化魅力,那就赶快搭建自己的环境吧~!
第一个脚本:
下面看看python 穿上selenium webdriver 是多么的性感:
# coding = utf-8 from selenium import webdriver browser = webdriver.firefox() browser.get("http://www.baidu.com") browser.find_element_by_id("kw").send_keys("selenium") browser.find_element_by_id("su").click() browser.quit()
怎么样?相信不懂代码的人都能看懂,但还是请容我在这里啰嗦一下每一句的含义:
# coding = utf-8
可加可不加,开发人员喜欢加一下,防止乱码嘛。
from selenium import webdriver
要想使用selenium的webdriver 里的函数,首先把包导进来嘛
browser = webdriver.firefox()
我们需要操控哪个浏览器呢?firefox ,当然也可以换成ie 或 chrome 。browser可以随便取,但后面要用它操纵各种函数执行。
browser.find_element_by_id("kw").send_keys("selenium")
一个控件有若干属性id 、name、(也可以用其它方式定位),百度输入框的id 叫kw ,我要在输入框里输入 selenium 。多自然语言呀!
browser.find_element_by_id("su").click()
搜索的按钮的id 叫su ,我需要点一下按钮( click() )。
browser.quit()
退出并关闭窗口的每一个相关的驱动程序,它还有个类似的表弟。
browser.close()
关闭当前窗口 ,用哪个看你的需求了。
添加休眠
什么?你说刚才太快没看清浏览器的操作过程。请time出马,让他跑慢点。
# coding = utf-8 from selenium import webdriver import time #调入time函数 browser = webdriver.firefox() browser.get("http://www.baidu.com") time.sleep(0.3) #休眠0.3秒 browser.find_element_by_id("kw").send_keys("selenium") browser.find_element_by_id("su").click() time.sleep(3) # 休眠3秒 browser.quit()
time.sleep() 函数随意插,哪里太快插哪里,再也不用担心看不清脚本的运行过程了。
其实,这个函数的真正用途不是给我们看脚本的运行过程的,有时候网络原因,或页面加载慢。假设搜索框输入框输入了selenium ,搜索按钮还没加载出来,那么脚本就报错。在适当的位置加入time.sleep()有助于减少网络原因造成的脚本执行失败;
输出
什么?在运行脚本的时候,上了个厕所,你都不知道刚才的脚本是否运行成功了。把刚才访问页面的title 打印出来。
# coding = utf-8 from selenium import webdriver driver = webdriver.chrome() driver.get('http://www.baidu.com') print driver.title # 把页面title 打印出来 driver.quit()
虽然我没看到脚本的执行过程,但我在执行结果里看到了
>>> 百度一下,你就知道
说明页面正确被我打开了。
--------------------------
学习更多selenium 内容:
在我们日常上网浏览网页的时候,经常会看到一些好看的图片,我们就希望把这些图片保存下载,或者用户用来做桌面壁纸,或者用来做设计的素材。
我们最常规的做法就是通过鼠标右键,选择另存为。但有些图片鼠标右键的时候并没有另存为选项,还有办法就通过就是通过截图工具截取下来,但这样就降低图片的清晰度。好吧~!其实你很厉害的,右键查看页面源代码。
我们可以通过python 来实现这样一个简单的爬虫功能,把我们想要的代码爬取到本地。下面就看看如何使用python来实现这样一个功能。
一,获取整个页面数据
首先我们可以先获取要下载图片的整个页面信息。
getjpg.py
#coding=utf-8 import urllib def gethtml(url): page = urllib.urlopen(url) html = page.read() return html html = gethtml("http://tieba.baidu.com/p/2738151262") print html
urllib 模块提供了读取web页面数据的接口,我们可以像读取本地文件一样读取www和ftp上的数据。首先,我们定义了一个gethtml()函数:
urllib.urlopen()方法用于打开一个url地址。
read()方法用于读取url上的数据,向gethtml()函数传递一个网址,并把整个页面下载下来。执行程序就会把整个网页打印输出。
二,筛选页面中想要的数据
python 提供了非常强大的正则表达式,我们需要先要了解一点python 正则表达式的知识才行。
假如我们百度贴吧找到了几张漂亮的壁纸,通过到前段查看工具。找到了图片的地址,如:src=”http://imgsrc.baidu.com/forum......jpg”pic_ext=”jpeg”
修改代码如下:
import re import urllib def gethtml(url): page = urllib.urlopen(url) html = page.read() return html def getimg(html): reg = r'src="(. ?\.jpg)" pic_ext' imgre = re.compile(reg) imglist = re.findall(imgre,html) return imglist html = gethtml("http://tieba.baidu.com/p/2460150866") print getimg(html)
我们又创建了getimg()函数,用于在获取的整个页面中筛选需要的图片连接。re模块主要包含了正则表达式:
re.compile() 可以把正则表达式编译成一个正则表达式对象.
re.findall() 方法读取html 中包含 imgre(正则表达式)的数据。
运行脚本将得到整个页面中包含图片的url地址。
三,将页面筛选的数据保存到本地
把筛选的图片地址通过for循环遍历并保存到本地,代码如下:
#coding=utf-8 import urllib import re def gethtml(url): page = urllib.urlopen(url) html = page.read() return html def getimg(html): reg = r'src="(. ?\.jpg)" pic_ext' imgre = re.compile(reg) imglist = re.findall(imgre,html) x = 0 for imgurl in imglist: urllib.urlretrieve(imgurl,'%s.jpg' % x) x =1 html = gethtml("http://tieba.baidu.com/p/2460150866") print getimg(html)
这里的核心是用到了urllib.urlretrieve()方法,直接将远程数据下载到本地。
通过一个for循环对获取的图片连接进行遍历,为了使图片的文件名看上去更规范,对其进行重命名,命名规则通过x变量加1。保存的位置默认为程序的存放目录。
程序运行完成,将在目录下看到下载到本地的文件。
python shell
>>> open('abc.txt','r') traceback (most recent call last): file "", line 1, in ioerror: [errno 2] no such file or directory: 'abc.txt'
打开一个不存在的文件abc.txt 文件,当系统找不到abc.txt 文件时,就会抛出给我们一个ioerror类型的错误,no such file or directory:abc.txt (没有abc.txt这样的文件或目录)
try...except...
假如,我们已经知道这种类型的错误,那么就可以通过一个异常扑捉来扑捉这个错误。我们可以通过try...except 来接收这个错误。打开文件写入:
try: open("abc.txt",'r') except ioerror: pass
再来运行程序就会看不到任何错误,因为我们用except 接收了这个ioerror错误。pass 表示实现了相应的实现,但什么也不做。
假如我不是打开一个文件,而是输出一个没有定义的变量呢?
try: print aa except ioerror: pass
显然,在上面的代码中我并没有对aa 赋值,运行结果:
traceback (most recent call last): file "/home/fnngj/py_se/tryy.py", line 3, inprint aa nameerror: name 'aa' is not defined
我们已经用except 接收错误了,为什么错误还是还是抛出来了。如果你细心会发现这一次的错误类型是nameerror ,而我接收类型是ioerror ,所以要想接收这个print的错误,那么需要修改接收错误的类型为nameerror
虽然,我知道print 语句是可能会抛一个nameerror 类型的错误,虽然接收了这个类型错误,但我不知道具体的错误提示信息是什么。那么,我能不能把错误信息打印出来呢?当然可以:
try: print aa except nameerror, msg: print msg
我们在接收错误类型的后面定义一个变量msg用于接收具体错误信息, 然后将msg接收的错误信息打印。再来运行程序:
name 'aa' is not defined
现在只打印了一行具体错误信息。
异常的抛出机制:
1、如果在运行时发生异常,解释器会查找相应的处理语句(称为handler).
2、要是在当前函数里没有找到的话,它会将异常传递给上层的调用函数,看看那里能不能处理。
3、如果在最外层(全局“main”)还是没有找到的话,解释器就会退出,同时打印出traceback以便让用户找到错误产生的原因。
注意:虽然大多数错误会导致异常,但一个异常不一定代表错误,有时候它们只是一个警告,有时候它们可能是一个终止信号,比如退出循环等。
try...finally...
try...finally...子句用来表达这样的情况:
我们不管线捕捉到的是什么错误,无论错误是不是发生,这些代码“必须”运行,比如文件关闭,释放锁,把数据库连接返还给连接池等。
创建文件poem.txt
tryf.py
import time try: f = file('poem.txt') while true: # our usual file-reading idiom line = f.readline() if len(line) == 0: break time.sleep(2) print line,
finally: f.close() print 'cleaning up...closed the file'
运行程序(在windows命令提示符或linux终端下运行):
...$ python tryf.py abc efg ^ccleaning up...closed the file traceback (most recent call last): file "tryy.py", line 18, intime.sleep(2) keyboardinterrupt
程序读poem.txt文件中每一行数据打印,但是我有意在每打印一行之前用time.sleep方法暂停2秒钟。这样做的原因是让程序运行得慢一些。在程序运行的时候,按ctrl-c中断/取消程序。
我们可以观察到keyboardinterrupt异常被触发,程序退出。但是在程序退出之前,finally从句仍然被执行,把文件关闭。
到目前为止,我们只讨论了如何捕捉异常,那么如何抛出异常呢?
raise抛出异常
使用raise来抛出一个异常:
tryr.py
#coding=utf-8 filename = raw_input('please input file name:') if filename=='hello': raise nameerror('input file name error !')
程序要求用户输入一个文件名,如果用户输入的文件名是hello ,那么抛出一个nameerror的异常,用户输入hello 和nameerror异常之间没有任何必然联系,我只是人为的通过raise来这样定义,我当然也可以定义称typeerror ,但我定义的异常类型必须是python提供的。
附录:
常见的python异常类型
学习python 有段时间,最近博客更新比较慢了,空闲时间在零零碎碎的学python ,难成文,也就没整理成博客。
学习python 最苦恼的就是没有趁手ide ,之前学java 时 eclipse 肯定是不二之选。eclipse pydev 也可以开发python,但觉太重了。 所以一直用自带的idle ,偶尔也用一下notepad 这种小巧的万能编辑器。
idle 写写单小程序很好,但一个程序与执行信息两个窗口,更别说多标签了,程序找开的多了,就乱了。
pythonwin 也用过,窗口有些老土,窗口布局我不会设置,所以觉得也不好用。
安装wxpython
由于ulipad 是由wxpython 开发的,所以,需要先安装wxpython 。
windows安装wxpython :
exe 程序,下载安装即可
ubuntu 安装wxpython:
打开控制台,输入命令:
> apt-cache search wxpython
如果能返回wxpython的信息,则直接输入命令:
> sudo apt-get install python-wxtools
更新完成,打开python 输入
>>> import wx
没有报错,表示安装成功
安装ulipad
下载地址:
我的系统是window xp (别鄙视我的系统老,公司测试需要~!)
找了个4.1 版本(目前最新版)的来安装,安装完成,启动的时候居然报了下面错误~!
试着找了一下解决办法,不管用。豆瓣有人问这个问题,大多回答是卸载了装4.0 版本的;于是在csdn 上找了个4.0 版本的,装上之后果然就可以正常打开了。
安装过程就不说了,exe程序一跳“next”就行了。
ubuntu 安装:
下载ulipad.4.1.zip 安装包
root@fnngj-h24x:/home/fnngj/python# unzip ulipad.4.1.zip
root@fnngj-h24x:/home/fnngj/python# cd ulipad.4.1
root@fnngj-h24x:/home/fnngj/python# ./ulipad.py
运行
打开程序之后,界面非常友好,创建个程序运行一下,错误来了!
提示我没有设置python 的解释器。
于是,在菜单 编辑---参数---python 中找到了“设置python编辑”按钮。点击找到自己python 的安装目录,把python2.7.exe 和pythonw2.7.exe 的路径加进来,别忘了加“描述”
再来运行程序,发现可以正常了。
窗口布局
工具栏上有三个按钮一看就是布局的;
左侧显示目录结构,底部显示程序运行结果。这是使用ide 工具的标准布局,起码用习惯eclipse 的同学会感觉很舒适。
如果你已经有项目的话可以通过右键目录浏览空白处理,增加目录----打开新目录,将项目导入。
py> 按钮提供的就是python交互模式,只是自带的版本是2.5.4 的有点老了,还不知道怎么设置。
代码
有洁癖的同学一定对代码的着色有很高的要求,着色的代码 会使用程序看去清晰明了。而且语法错误也很容易识别。
自动补齐也是优秀开ide 必备的,可以提高开发效率,减少语法错误。
设置语法参数:文档----语法参数
不得不说,ulipad 的着色与自动补全功能很出色。
调试
要想成为一名优秀的ide 怎么能没有调试功能呢。你能想到的,作者自然也想到了。工具栏上有个七星瓢虫的图标,自然就是调试按钮了。
插件
要想变了更强大,必须要支持插件扩展;我们可以结合插件 实现脚本管理,正则、django 项目开发等。
菜单栏工具----插件管理
最后,特意看了一下ulipad 的内存占用很低10mb 左右;几乎可忽略它的内存占用。好吧!就是它了。
ulipad 项目地址: