Selenium是一个Web的自动化测试工具,最初是为网站自动化测试而开发的,可以按指定的命令自动操作,但是他需要与第三方浏览器结合在一起才能使用。如果我们把 Selenium和第三方浏览器(比如Chrome)结合在一起,就可以运行一个非常强大的网络爬虫了,这个爬虫可以处理 JavaScrip、Cookie、headers,以及任何我们真实用户需要做的事情。
一、安装
sudo pip3 install selenium
二、快速入门
from selenium import webdriver # 导入webdriver from selenium.webdriver.common.keys import Keys #Keys`类提供键盘按键的支持,比如:RETURN, F1, ALT等 from auth_proxy import proxyauth_plugin_path #auth_proxy模块代码在下面单独实现,用于私密代理设置 #options参数的一些常见设置 options = webdriver.ChromeOptions() #设置谷歌浏览器的一些配置选项 options.add_argument('lang=zh_CN.UTF-8') #设置编码格式 options.add_argument('--headless') #无界面浏览器 options.add_argument('--proxy-server=http://ip:port') #设置无账号密码代理 options.add_extension(proxyauth_plugin_path) #设置私密代理 options.add_argument('user-agent=Mozilla/5.0 (iPhone; CPU iPhone OS 9_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13B143 Safari/601.1') #移动版网站的反爬虫的能力比较弱,模拟iPhone6 #登录时关闭弹出的密码保存提示框,添上以下几句 prefs={"credentials_enable_service":False,"profile.password_manager_enabled":False} options.add_experimental_option("prefs", prefs) # 禁用浏览器弹窗 prefs = {'profile.default_content_setting_values':{'notifications' :2}} options.add_experimental_option('prefs',prefs) #不加载图片, 提升速度 prefs = {'profile.managed_default_content_settings.images': 2} options.add_experimental_option('prefs',prefs) #屏蔽谷歌浏览器正在接收自动化软件控制提示,加上以下两行 options.add_experimental_option('useAutomationExtension',False) options.add_experimental_option('excludeSwitches', ['enable-automation']) options.add_argument("disable-blink-features=AutomationControlled")#去掉webdriver痕迹 options.add_argument('--incognito') #隐身模式(无痕模式) options.add_argument('window-size=1920x3000') #指定浏览器分辨率 options.add_argument('--no-sandbox') #取消沙盒模式,解决DevToolsActivePort文件不存在的报错 options.add_argument('--disable-gpu') #规避bug options.add_argument('--disable-dev-shm-usage') #克服有限的资源问题,用于Linux系统 options.add_argument('--hide-scrollbars') #隐藏滚动条, 应对一些特殊页面 options.add_argument('–-start-maximized') #启动就最大化 driver = webdriver.Chrome(chrome_options=options) #创建浏览器对象 driver.get("https://www.baidu.com/") # get方法会打开一个页面 driver.implicitly_wait(10) # 通常打开页面后会等待一会,让页面加载 driver.get_window_size() # 获取浏览器窗口大小 driver.set_window_size(1200,800) #设置浏览器窗口大小 driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") #执行js,滚动页面到底部 print (driver.title) # 打印页面标题 "百度一下,你就知道" driver.save_screenshot("baidu.png") # 生成当前页面快照并保存 binary_data = driver.get_screenshot_as_png() #获取当前页面快照的二进制数据 elem = driver.find_element_by_id('kw') #根据id查找元素 elem.screenshot(文件名) #对元素截图,并存储到本地 elem_data = elem.screenshot_as_png #获取当前元素截图的二进制数据 elem.size #获取当前元素大小,如{'height': 44, 'width': 108} elem.location #获取当前元素位置,即左上角坐标,例如{'x': 844, 'y': 188} elem.get_attribute(属性名)#获取元素的属性值 elem.text #获取当前元素的文本内容 elem.tag_name #获取当前元素标签名 elem.find_elment...#可以在当前元素内继续查找元素 elem.clear() #清空搜索框 elem.send_keys('中国') #搜索中国 elem.send_keys(Keys.RETURN) #按回车键 print(driver.page_source) # 打印网页渲染后的源代码 print(driver.get_cookies()) # 获取当前页面Cookie print(driver.current_url) # 获取当前url # driver.close() 关闭当前页面,如果只有一个页面,会关闭浏览器 driver.quit() # 关闭浏览器
auth_proxy.py代码实现如下:
from selenium import webdriver def create_proxyauth_extension(proxy_host, proxy_port, proxy_username, proxy_password, scheme='http', plugin_path=None): """Proxy Auth Extension args: proxy_host (str): domain or ip address, ie proxy.domain.com proxy_port (int): port proxy_username (str): auth username proxy_password (str): auth password kwargs: scheme (str): proxy scheme, default http plugin_path (str): absolute path of the extension return str -> plugin_path """ import string import zipfile if plugin_path is None: plugin_path = 'vimm_chrome_proxyauth_plugin.zip' manifest_json = """ { "version": "1.0.0", "manifest_version": 2, "name": "Chrome Proxy", "permissions": [ "proxy", "tabs", "unlimitedStorage", "storage", "<all_urls>", "webRequest", "webRequestBlocking" ], "background": { "scripts": ["background.js"] }, "minimum_chrome_version":"22.0.0" } """ background_js = string.Template( """ var config = { mode: "fixed_servers", rules: { singleProxy: { scheme: "${scheme}", host: "${host}", port: parseInt(${port}) }, bypassList: ["foobar.com"] } }; chrome.proxy.settings.set({value: config, scope: "regular"}, function() {}); function callbackFn(details) { return { authCredentials: { username: "${username}", password: "${password}" } }; } chrome.webRequest.onAuthRequired.addListener( callbackFn, {urls: ["<all_urls>"]}, ['blocking'] ); """ ).substitute( host=proxy_host, port=proxy_port, username=proxy_username, password=proxy_password, scheme=scheme, ) with zipfile.ZipFile(plugin_path, 'w') as zp: zp.writestr("manifest.json", manifest_json) zp.writestr("background.js", background_js) return plugin_path proxyauth_plugin_path = create_proxyauth_extension( proxy_host="http-xxxxxxxx.com", ##代理服务器 proxy_port=端口号, ##代理端口 proxy_username="用户名", ##认证用户名 proxy_password="密码" ##认证密码 )
三、查找元素
-
查找单个元素(如果有多个匹配的元素,则返回第一个匹配到的元素)
-
find_element_by_id 通过id查找
login_form = driver.find_element_by_id('loginForm')
-
find_element_by_name 通过name查找
username = driver.find_element_by_name('username') password = driver.find_element_by_name('password')
-
find_element_by_xpath 通过xpath查找
login_form = driver.find_element_by_xpath("//form[@id='loginForm']")
-
find_element_by_link_text 通过链接文本获取超链接元素
continue_link = driver.find_element_by_link_text('Continue')
-
find_element_by_partial_link_text 通过部分链接文本获取超链接元素
continue_link = driver.find_element_by_partial_link_text('Conti')
-
find_element_by_tag_name 通过标签名查找
heading1 = driver.find_element_by_tag_name('h1')
-
find_element_by_class_name 通过类名查找
content = driver.find_element_by_class_name('content')
-
find_element_by_css_selector 通过css选择器查找
content = driver.find_element_by_css_selector('p.content')
-
-
一次查找多个元素(返回的是元素列表)
-
find_elements_by_name
-
find_elements_by_xpath
-
find_elements_by_link_text
-
find_elements_by_partial_link_text
-
find_elements_by_tag_name
-
find_elements_by_class_name
-
find_elements_by_css_selector
-
-
此外还有两个私有方法find_element和find_elements
from selenium.webdriver.common.by import By driver.find_element(By.XPATH, '//button[text()="Some text"]') driver.find_elements(By.XPATH, '//button')
By类的其他属性还包括:
ID = "id" XPATH = "xpath" LINK_TEXT = "link text" PARTIAL_LINK_TEXT = "partial link text" NAME = "name" TAG_NAME = "tag name" CLASS_NAME = "class name" CSS_SELECTOR = "css selector"
四、鼠标动作链
有些时候,我们需要再页面上模拟一些鼠标操作,比如双击、右击、拖拽甚至按住不动等,我们可以通过导入ActionChains 类来做到。
-
导入ActionChains类
from selenium.webdriver import ActionChains
-
一般样式
ActionChains(driver).操作方法1.操作方法2.....perform()
ActionChains对象上的操作方法存储在对象的队列中,在调用perform()方法时,才会按照队列中的顺序去触发操作
-
常见方法
-
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)
- 鼠标点击元素,并按住偏移量进行拖放
-
move_by_offset(xoffset,yoffset)
- 鼠标从当前位置按照偏移量进行移动
-
move_to_element(to_element)
- 鼠标移动到元素中间位置
-
release()
- 松掉鼠标
-
五、页面下拉框处理
我们已经知道了怎样向文本框中输入文字,但是有时候我们会碰到<select> </select>
标签的下拉框。直接点击下拉框中的选项不一定可行。
-
WebDriver的支持类包括一个叫做 Select的类,他提供有用的方法处理这些内容:
from selenium.webdriver.support.ui import Select select = Select(driver.find_element_by_name('name')) select.select_by_index(index) #index索引从0开始 select.select_by_visible_text("text") #text是在option标签文本的值,是显示在下拉框的值 select.select_by_value(value) #value是option标签的一个属性值,并不是显示在下拉框中的值
-
取消选择
select.deselect_all()
六、弹窗处理
Selenium WebDriver 内置了对处理弹出对话框的支持。在你的某些动作之后可能会触发弹出对话框,你可以像下面这样访问对话框:
alert = driver.switch_to.alert() # 切换进alert print(alert.text())# 打印alert文本内容 alert.accept()# 关闭弹框(接受) # alert.dismiss() 关闭弹窗(拒绝) # alert.send_keys('selenium') 向弹窗里输入内容
七、页面切换
浏览器一般会打开多个窗口,切换窗口的方法如下:
driver.switch_to.window("窗口名")
或者你也可以在”switch_to.window()”中使用”窗口句柄”来打开它
script = 'window.open(https://www/zhihu.com/)' driver.execute_script(script)#利用js在新窗口中打开知乎网 driver.switch_to.window(driver.window_handles[-1])#切换到新打开的网页
八、访问浏览器历史记录
-
在浏览历史中前进和后退你可以使用:
driver.forward() driver.back()
九、cookies
-
获取所有cookies
driver.get_cookies()
-
删除某个cookie
driver.delete_cookie("CookieName")
-
删除所有cookies
driver.delete_all_cookies()
-
添加cookie
driver.add_cookie(cookie) #cookie为dict类型
十、页面等待(Waits)
现在的大多数的Web应用程序是使用Ajax技术。当一个页面被加载到浏览器时,该页面内的元素可以在不同的时间点被加载。这使得定位元素变得困难,如果元素不再页面之中,会抛出ElementNotVisibleException异常。使用 waits, 我们可以解决这个问题。waits提供了一些操作之间的时间间隔,主要是定位元素或针对该元素的任何其他操作。
Selenium Webdriver 提供两种类型的waits:隐式和显式。显式等待会让WebDriver等待满足一定的条件以后再进一步的执行。而隐式等待让Webdriver等待一定的时间后再才是查找某元素。
-
显示等待
-
显式等待是你在代码中定义等待一定条件发生后再进一步执行你的代码。
-
这里有一些方便的方法让你只等待需要的时间。WebDriverWait结合ExpectedCondition 是实现的一种方式。
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_delays_loading") try: element = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "myDynamicElement")) ) finally: driver.quit()
在抛出TimeoutException异常之前将等待10秒。 WebDriverWait 默认情况下会每0.5秒调用一次ExpectedCondition直到结果成功返回。 ExpectedCondition成功的返回结果是一个布尔类型的true或是不为null的返回值。
-
下面是一些内置的等待条件,你可以直接调用这些条件:
title_is title_contains presence_of_element_located visibility_of_element_located visibility_of presence_of_all_elements_located text_to_be_present_in_element text_to_be_present_in_element_value frame_to_be_available_and_switch_to_it invisibility_of_element_located element_to_be_clickable – it is Displayed and Enabled. staleness_of element_to_be_selected element_located_to_be_selected element_selection_state_to_be element_located_selection_state_to_be alert_is_present
-
-
隐式等待
-
隐式等待比较简单,就是简单地设置一个等待时间,单位为秒。
-
如果不设置,默认等待时间为0秒。
from selenium import webdriver driver = webdriver.Firefox() driver.get("http://somedomain/url_that_delays_loading") driver.implicitly_wait(10) # seconds myDynamicElement = driver.find_element_by_id("myDynamicElement")
-
十一、执行JS
可以在加载完成的页面上使用execute_script方法执行js。 比如调用javascript API滚动页面到底部或页面的任何位置
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") #滚动到当前页面底部 driver.execute_script("document.documentElement.scrollTop=10000") #向下滚动10000像素
十二、传文件到文件上传控件
选择 <input type=”file”> 元素并且调用 send_keys() 方法传入要上传文件的路径,可以 是对于测试脚本的相对路径,也可以是绝对路径。