安装
selenium
pip install selenium
chromedriver(Chrome)
chrome://version/
【注】chromedriver的版本一定要与Chrome的版本一致,不然就不起作用。
https://code.google.com/p/chromedriver/downloads/list
http://chromedriver.storage.googleapis.com/index.html
https://npm.taobao.org/mirrors/chromedriver/
phantomjs(无头)
geckodriver(Firefox)
https://github.com/mozilla/geckodriver/releases/
IEdriver(IE)
https://www.nuget.org/packages/Selenium.WebDriver.IEDriver/
说明
浏览器驱动需放到python.exe
同目录或有环境变量的目录下。
使用
引入
from selenium import webdriver
启动浏览器和打开网页
# 谷歌浏览器
browser = webdriver.Chrome()
browser.get('http://www.baidu.com/')
# PhantomJS浏览器
browser = webdriver.PhantomJS()
browser.get('http://www.baidu.com/')
# 火狐浏览器
browser = webdriver.Firefox()
browser.get('http://www.baidu.com/')
# IE浏览器
browser = webdriver.Ie()
browser.get('http://www.baidu.com/')
选择器
1. 通过ID查找元素
当你知道一个元素的 id 时,你可以使用本方法。在该策略下,页面中第一个该 id 元素 会被匹配并返回。如果找不到任何元素,会抛出 NoSuchElementException
异常。
作为示例,页面元素如下所示:
<html>
<body>
<form id="loginForm">
<input name="username" type="text" />
<input name="password" type="password" />
<input name="continue" type="submit" value="Login" />
</form>
</body>
<html>
可以这样查找表单(form)元素:
login_form = driver.find_element_by_id('loginForm')
2. 通过Name查找元素
当你知道一个元素的 name 时,你可以使用本方法。在该策略下,页面中第一个该 name 元素 会被匹配并返回。如果找不到任何元素,会抛出 NoSuchElementException
异常。
作为示例,页面元素如下所示:
<html>
<body>
<form id="loginForm">
<input name="username" type="text" />
<input name="password" type="password" />
<input name="continue" type="submit" value="Login" />
<input name="continue" type="button" value="Clear" />
</form>
</body>
<html>
name属性为 username & password 的元素可以像下面这样查找:
username = driver.find_element_by_name('username')
password = driver.find_element_by_name('password')
这会得到 “Login” 按钮,因为他在 “Clear” 按钮之前:
continue = driver.find_element_by_name('continue')
3. 通过XPath查找元素
XPath是XML文档中查找结点的语法。因为HTML文档也可以被转换成XML(XHTML)文档, Selenium的用户可以利用这种强大的语言在web应用中查找元素。 XPath扩展了(当然也支持)这种通过id或name属性获取元素的简单方式,同时也开辟了各种新的可能性, 例如获取页面上的第三个复选框。
使用XPath的主要原因之一就是当你想获取一个既没有id属性也没有name属性的元素时, 你可以通过XPath使用元素的绝对位置来获取他(这是不推荐的),或相对于有一个id或name属性的元素 (理论上的父元素)的来获取你想要的元素。XPath定位器也可以通过非id和name属性查找元素。
绝对的XPath是所有元素都从根元素的位置(HTML)开始定位,只要应用中有轻微的调整,会就导致你的定位失败。 但是通过就近的包含id或者name属性的元素出发定位你的元素,这样相对关系就很靠谱, 因为这种位置关系很少改变,所以可以使你的测试更加强大。
作为示例,页面元素如下所示:
<html>
<body>
<form id="loginForm">
<input name="username" type="text" />
<input name="password" type="password" />
<input name="continue" type="submit" value="Login" />
<input name="continue" type="button" value="Clear" />
</form>
</body>
<html>
可以这样查找表单(form)元素:
login_form = driver.find_element_by_xpath("/html/body/form[1]")
login_form = driver.find_element_by_xpath("//form[1]")
login_form = driver.find_element_by_xpath("//form[@id='loginForm']")
- 绝对定位 (页面结构轻微调整就会被破坏)
- HTML页面中的第一个form元素
- 包含 id 属性并且其值为 loginForm 的form元素
username元素可以如下获取:
username = driver.find_element_by_xpath("//form[input/@name='username']")
username = driver.find_element_by_xpath("//form[@id='loginForm']/input[1]")
username = driver.find_element_by_xpath("//input[@name='username']")
- 第一个form元素中包含name属性并且其值为 username 的input元素
- id为 loginForm 的form元素的第一个input子元素
- 第一个name属性为 username 的input元素
“Clear” 按钮可以如下获取:
clear_button = driver.find_element_by_xpath("//input[@name='continue'][@type='button']")
clear_button = driver.find_element_by_xpath("//form[@id='loginForm']/input[4]")
- Input with attribute named name and the value continue and attribute named type and the value button
- Fourth input child element of the form element with attribute named id and value loginForm
4. 通过链接文本获取超链接
当你知道在一个锚标签中使用的链接文本时使用这个。 在该策略下,页面中第一个匹配链接内容锚标签 会被匹配并返回。如果找不到任何元素,会抛出 NoSuchElementException
异常。
作为示例,页面元素如下所示:
<html>
<body>
<p>Are you sure you want to do this?</p>
<a href="continue.html">Continue</a>
<a href="cancel.html">Cancel</a>
</body>
<html>
continue.html 超链接可以被这样查找到:
continue_link = driver.find_element_by_link_text('Continue')
continue_link = driver.find_element_by_partial_link_text('Conti')
5. 通过标签名查找元素
当你向通过标签名查找元素时使用这个。 在该策略下,页面中第一个匹配该标签名的元素 会被匹配并返回。如果找不到任何元素,会抛出 NoSuchElementException
异常。
作为示例,页面元素如下所示:
<html>
<body>
<h1>Welcome</h1>
<p>Site content goes here.</p>
</body>
<html>
h1 元素可以如下查找:
heading1 = driver.find_element_by_tag_name('h1')
6. 通过Class name 定位元素
当你向通过class name查找元素时使用这个。 在该策略下,页面中第一个匹配该class属性的元素 会被匹配并返回。如果找不到任何元素,会抛出 NoSuchElementException
异常。
作为示例,页面元素如下所示:
<html>
<body>
<p class="content">Site content goes here.</p>
</body>
<html>
p 元素可以如下查找:
content = driver.find_element_by_class_name('content')
7. 通过CSS选择器查找元素
当你向通过CSS选择器查找元素时使用这个。 在该策略下,页面中第一个匹配该CSS 选择器的元素 会被匹配并返回。如果找不到任何元素,会抛出 NoSuchElementException
异常。
作为示例,页面元素如下所示:
<html>
<body>
<p class="content">Site content goes here.</p>
</body>
<html>
p 元素可以如下查找:
content = driver.find_element_by_css_selector('p.content')
基本的页面操作
定位到元素后,可对该元素进行一些操作(比如输入内容、点击按钮等)
输入内容
ele_input_id.send_keys('Hello World')
点击按钮
ele_btn.click()
获取内容
获取标签文字:
print(xxx.text)
获取标签class:
print(a.get_attribute('class'))
填充表单
以下拉选项卡的处理为例:
element = driver.find_element_by_xpath("//select[@name='name']")
all_options = element.find_elements_by_tag_name("option")
for option in all_options:
print("Value is: %s" % option.get_attribute("value"))
option.click()
首先获取了第一个 select 元素,也就是下拉选项卡。然后轮流设置了 select 选项卡中的每一个 option 选项。但是这并不是一个非常有效的方法。 其实 WebDriver 中提供了一个叫 Select 的方法,可以帮助我们完成这些事情。
from selenium.webdriver.support.ui import Select
select = Select(driver.find_element_by_name('name'))
select.select_by_index(index)
select.select_by_visible_text("text")
select.select_by_value(value)
它可以根据索引来选择,可以根据值来选择,可以根据文字来选择。是十分方便的。 全部取消选择怎么办呢?很简单
select = Select(driver.find_element_by_id('id'))
select.deselect_all()
这样便可以取消所有的选择。 另外我们还可以通过下面的方法获取所有的已选选项。
select = Select(driver.find_element_by_xpath("xpath"))
all_selected_options = select.all_selected_options
获取所有可选选项是
options = select.options
如果你把表单都填好了,最后肯定要提交表单对吧。怎吗提交呢?很简单
driver.find_element_by_id("submit").click()
这样就相当于模拟点击了 submit 按钮,做到表单提交。 当然你也可以单独提交某个元素
element.submit()
页面切换
针对多窗口场景,切换窗口的方法如下:
driver.switch_to_window("windowName")
另外可以使用 window_handles 方法来获取每个窗口的操作对象。例如
for handle in driver.window_handles:
driver.switch_to_window(handle)
另外切换 frame 的方法如下
driver.switch_to_frame("frameName.0.child")
这样焦点会切换到一个 name 为 child 的 frame 上。
弹窗处理
假设触发某个事件之后,页面出现了弹窗提示
alert = driver.switch_to_alert()
可通过上述方法可以获取弹窗对象。
历史记录
操作页面的前进和后退:
driver.forward()
driver.back()
获取 cookies
在爬虫领域中,常常使用 selenium
获取 cookies
应付反爬虫。
比如,爬取微博的内容,需要登陆状态,而保存的 cookies
会在一定时间后失效,这时候 selenium
就派上了用场,使用预先设置的账号密码登陆,然后获取 cookies
发送给脚本使用。
1. 使用内置函数
获取 cookies
使用 get_cookies
函数,以百度为例:
cookie_r = driver.get_cookies()
打印出 cookie_r 是这样的:
[{'domain': '.baidu.com', 'httpOnly': False, 'name': 'H_PS_PSSID', 'path': '/', 'secure': False, 'value': '22584_1465_21087_18560_22581'}, {'domain': '.baidu.com', 'expiry': 3639883283.649732, 'httpOnly': False, 'name': 'BAIDUID', 'path': '/', 'secure': False, 'value': '69F8E8C5CF050F0C8CA3C358CB08BAC6:FG=1'}, {'domain': '.baidu.com', 'expiry': 3639883283.649952, 'httpOnly': False, 'name': 'PSTM', 'path': '/', 'secure': False, 'value': '1492399635'}, {'domain': '.baidu.com', 'expiry': 3639883283.649925, 'httpOnly': False, 'name': 'BIDUPSID', 'path': '/', 'secure': False, 'value': '69F8E8C5CF050F0C8CA3C358CB08BAC6'}, {'domain': 'www.baidu.com', 'expiry': 1493263637, 'httpOnly': False, 'name': 'BD_UPN', 'path': '/', 'secure': False, 'value': '12314753'}, {'domain': 'www.baidu.com', 'expiry': 1492399637.649969, 'httpOnly': False, 'name': 'BD_LAST_QID', 'path': '/', 'secure': False, 'value': '18362646771513028098'}, {'domain': 'www.baidu.com', 'httpOnly': False, 'name': 'BD_HOME', 'path': '/', 'secure': False, 'value': '0'}, {'domain': '.www.baidu.com', 'expiry': 1492399642.240359, 'httpOnly': False, 'name': '__bsi', 'path': '/', 'secure': False, 'value': '16804089554912194086_00_0_I_R_3_0303_C02F_N_I_I_0'}, {'domain': 'www.baidu.com', 'expiry': 1492399647, 'httpOnly': False, 'name': 'WWW_ST', 'path': '/', 'secure': False, 'value': '1492399637585'}]
此形式的 cookies
是不能直接传递给脚本使用的,我们需要进一步的处理,提取出每个字典 name
和 value
值,将其组合在一起。
cookies_list = []for i in cookie_r:
cookie = i['name'] + '=' + i['value']
cookies_list.append(cookie)
cookies_str = ';'.join(cookies_list)
经过处理的 cookies_str 就可以直接发送给 脚本使用了,打印出 cookies_str 如下:
H_PS_PSSID=22583_1433_21106_17001_20929;BAIDUID=456846B8C2CECBC077CA6A700DA24A89:FG=1;PSTM=1492400387;BIDUPSID=456846B8C2CECBC077CA6A700DA24A89;BD_HOME=0;BD_UPN=12314753;__bsi=16211205307050373314_00_0_I_R_2_0303_C02F_N_I_I_0;WWW_ST=1492400389401
2. 执行 js 函数
获取 cookies
不仅仅可以通过 get_cookies
函数,还可以直接使用 javascript
代码,示例:
# 字符串形式的 js 代码
js_code = 'return document.cookie'
# 执行 js 代码
cookies = driver.execute_script(js_code)
print(cookies)
此时获取的 cookies
可以直接发给脚本使用,结果如下:
BAIDUID=7ABCBA83953DC58B59943B0967D10098:FG=1; BIDUPSID=7ABCBA83953DC58B59943B0967D10098; PSTM=1492400818; BD_HOME=0; H_PS_PSSID=22584_1457_21106_17001_21673_20927; BD_UPN=12314753; __bsi=17435099398291019533_00_0_I_R_2_0303_C02F_N_I_I_0; WWW_ST=1492400820362
页面等待
针对采用了 Ajax
的网页,程序便不能确定何时某个元素完全加载出来,从而使得元素定位困难而且会提高产生 ElementNotVisibleException
的概率。 对此 Selenium
提供了两种等待方式:隐式等待和显式等待。 隐式等待是等待特定的时间,显式等待是指定某一条件直到这个条件成立时继续执行。
1. 显式等待
显式等待指定某个条件,然后设置最长等待时间。如果在这个时间还没有找到元素,那么便会抛出异常了。
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.Chrome()
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()
程序默认会 500ms 调用一次来查看元素是否已经生成,如果本来元素就是存在的,那么会立即返回。 下面是一些内置的等待条件,你可以直接调用这些条件,而不用自己写某些等待条件了。
- 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
from selenium.webdriver.support import expected_conditions as EC
wait = WebDriverWait(driver, 10)
element = wait.until(EC.element_to_be_clickable((By.ID,'someid')))
2. 隐式等待
隐式等待比较简单,就是简单地设置一个等待时间,单位为秒。
from selenium import webdriver
driver = webdriver.Chrome()
driver.implicitly_wait(10) # seconds
driver.get("http://somedomain/url_that_delays_loading")
myDynamicElement = driver.find_element_by_id("myDynamicElement")
当然如果不设置,默认等待时间为 0。
实战一
需求:
实现cookie的自动获取,及cookie过期自动更新。
社交网站中的很多信息需要登录才能获取到,以微博为例,不登录账号,只能看到大V的前十条微博。保持登录状态,必须要用到Cookie。以登录www.weibo.cn 为例:
在chrome中输入:http://login.weibo.cn/login/
分析控制台的Headers的请求返回,会看到weibo.cn有几组返回的cookie。
实现步骤:
1,采用selenium自动登录获取cookie,保存到文件;
2,读取cookie,比较cookie的有效期,若过期则再次执行步骤1;
3,在请求其他网页时,填入cookie,实现登录状态的保持。
过程:
1. 在线获取cookie
采用selenium + PhantomJS 模拟浏览器登录,获取cookie;
cookies一般会有多个,逐个将cookie存入以.weibo后缀的文件。
def get_cookie_from_network():
from selenium import webdriver
url_login = 'http://login.weibo.cn/login/'
driver = webdriver.PhantomJS()
driver.get(url_login)
driver.find_element_by_xpath('//input[@type="text"]').send_keys('your_weibo_accout') # 改成你的微博账号
driver.find_element_by_xpath('//input[@type="password"]').send_keys('your_weibo_password') # 改成你的微博密码
driver.find_element_by_xpath('//input[@type="submit"]').click() # 点击登录
# 获得 cookie信息
cookie_list = driver.get_cookies()
print cookie_list
cookie_dict = {}
for cookie in cookie_list:
#写入文件
f = open(cookie['name']+'.weibo','w')
pickle.dump(cookie, f)
f.close()
if cookie.has_key('name') and cookie.has_key('value'):
cookie_dict[cookie['name']] = cookie['value']
return cookie_dict
2. 从文件中获取cookie
从当前目录中遍历以.weibo结尾的文件,即cookie文件。采用pickle解包成dict,比较expiry值与当前时间,若过期则返回为空;
def get_cookie_from_cache():
cookie_dict = {}
for parent, dirnames, filenames in os.walk('./'):
for filename in filenames:
if filename.endswith('.weibo'):
print filename
with open(self.dir_temp + filename, 'r') as f:
d = pickle.load(f)
if d.has_key('name') and d.has_key('value') and d.has_key('expiry'):
expiry_date = int(d['expiry'])
if expiry_date > (int)(time.time()):
cookie_dict[d['name']] = d['value']
else:
return {}
return cookie_dict
3. 若缓存cookie过期,则再次从网络获取cookie
def get_cookie():
cookie_dict = get_cookie_from_cache()
if not cookie_dict:
cookie_dict = get_cookie_from_network()
return cookie_dict
4. 带cookie请求微博其他主页
def get_weibo_list(self, user_id):
import requests
from bs4 import BeautifulSoup as bs
cookdic = get_cookie()
url = 'http://weibo.cn/stocknews88'
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36'}
timeout = 5
r = requests.get(url, headers=headers, cookies=cookdic,timeout=timeout)
soup = bs(r.text, 'lxml')
...
# 用BeautifulSoup 解析网页
...
实战二
需求:
尝试多种操作
过程:
import requests
import sys
import io
from selenium import webdriver
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf8') #改变标准输出的默认编码
#建立Phantomjs浏览器对象,括号里是phantomjs.exe在你的电脑上的路径
browser = webdriver.PhantomJS('d:/tool/07-net/phantomjs-windows/phantomjs-2.1.1-windows/bin/phantomjs.exe')
#登录页面
url = r'http://ssfw.xmu.edu.cn/cmstar/index.portal'
# 访问登录页面
browser.get(url)
# 等待一定时间,让js脚本加载完毕
browser.implicitly_wait(3)
#输入用户名
username = browser.find_element_by_name('user')
username.send_keys('学号')
#输入密码
password = browser.find_element_by_name('pwd')
password.send_keys('密码')
#选择“学生”单选按钮
student = browser.find_element_by_xpath('//input[@value="student"]')
student.click()
#点击“登录”按钮
login_button = browser.find_element_by_name('btn')
login_button.submit()
#网页截图
browser.save_screenshot('picture1.png')
#打印网页源代码
print(browser.page_source.encode('utf-8').decode())
browser.quit()
实战三
需求:
示例代码来自佬潘,针对部分设置反爬网站加以伪装得以实现爬虫,原理详解见参考资料。
过程:
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
bilibili_url = 'https://www.bilibili.com/video/BV1W64y1t7gq'
#伪装
chrome_options = Options()
chrome_options.add_argument('headless')#不显示浏览器
chrome_options.add_argument("--disable-blink-features=AutomationControlled")
chrome_options.add_argument('user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36')
chrome_options.add_argument("--disable-blink-features=AutomationControlled")
driver = webdriver.Chrome(options=chrome_options)
driver.get('https://bilibili.iiilab.com/')
URL_entry = driver.find_element_by_class_name('form-control.link-input')
URL_entry.send_keys(bilibili_url)
#有时会出现解析失败的情况,多点击几次即可
while driver.find_elements_by_class_name('alert.alert-danger'):
start_button = driver.find_element_by_class_name('btn.btn-default')
try:
start_button.click()
except:
break
#通过while循环,一旦解析成功就直接返回MP4的地址
mp4_url = '0'
while not mp4_url or len(mp4_url) == 1:
try:
dowload_button = driver.find_element_by_class_name('btn.btn-success')
except:
pass
else:
mp4_url = dowload_button.get_attribute('href')
driver.close()
print(mp4_url)
参考资料:
www.selenium.dev/documentation/zh-cn/
selenium-python-zh.readthedocs.io/en/latest/
www.cnblogs.com/lfri/p/10542797.html
blog.csdn.net/azsx02/article/details/68947429
www.php.cn/js-tutorial-399684.html
www.cnblogs.com/mghhzAnne/p/12575400.html
www.cnblogs.com/ittop/p/9639449.html
blog.csdn.net/lly1122334/article/details/107352181
版权属于:soarli
本文链接:https://blog.soarli.top/archives/538.html
转载时须注明出处及本声明。