Selenium的使用
Selenium 的使用
??利用Ajax的接口,找到其规律,可以通过某些参数构造出对应的请求,可以轻松的爬取到数据。
??但是在很多情况下,Ajax请求的接口通常含有加密参数,例如token,sign等。这种情况我们如果不深入分析找到加密的构造逻辑,很难直接模拟Ajax请求。
??对于这种情况如果我们深挖其逻辑进行破解难度比较高,这里我们可以采用另外一种办法可以用selenium模拟浏览器的运行来获取数据。
??Selenium是一个自动化测试工具,利用它可以驱动浏览器完成一些特定的操作。也可以获取当前页面的网页源代码,做到可见即可爬,对于JavaScript渲染的页面来说,Selenium非常有效。
1.准备
??以Chrome浏览器为例讲解。首先安装Selenium包。并且需要对应浏览器版本的ChromeDriver驱动,将其放在浏览器安装目录下。
pip install selenium
# 最新版本语句可能略微发生变化,本文以3.3.1为例。
#pip install selenium==3.3.1
2.基本用法
??示例代码如下:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait
browser = webdriver.Chrome()
try:
browser.get('https://www.baidu.com')
input = browser.find_element_by_id('kw')
input.send_keys('Python')
input.send_keys(Keys.ENTER)
wait = WebDriverWait(browser,10)
wait.until(EC.presence_of_element_located((By.ID,'content_left')))
print(browser.current_url)
print(browser.get_cookies())
print(browser.page_source)
finally:
browser.close()
??运行代码会自动弹出Chrome浏览器。会跳转到百度页面,在搜索框输入python,然后跳转到搜索页。此时控制台会输出URL,Cookie内容,和真实的网页源代码。
用selenium驱动加载浏览器网页,可以获得JavaScript渲染的结果,无需关心是否加密。
3.初始化浏览器对象
??Selenium支持许多浏览器。像Chrome、Firefox、Edge、Safari等,一些手机端的浏览器也支持。
# 初始化浏览器对象
from selenium import webdriver
browser = webdriver.Chrome()
# 或者Chrome替换为Firebox、Edge等等
4访问页面
??可以使用get方法请求网页,向参数传入指定的URL链接即可,以访问淘宝,并打印网页源代码,然后关闭浏览器为例。
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://taobao.com')
print(browser.page_source)
browser.close()
5.查找节点
- 单个节点
?? 以提取淘宝首页搜索框节点为例。
??这里可以看到淘宝搜索框id属性值是q,name也是q。可以采用多种方式获取。例如name:browser.find_element_by_name;id:browser.find_element_by_id,此外还有Xpath,CSS等方式
# 以id属性为例
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://taobao.com')
input = browser.find_element_by_id('q')
print(input)
browser.close()
??获取单个节点可以使用find_element_by_id,find_element_by_name,find_element_by_xpath,find_element_by_link_text,find_element_by_partial_link_text,find_element_by_tag_name,find_element_by_class_name,find_element_by_css_selector。
此外,selenium提供了通用方法find_element,使用这个方法需要传入查找方式和方式的取值两个参数。例如find_element_by_id(id)等价于find_element(By.ID,id)
- 多个节点
??查找的目标节点如果有多个,使用find_element只会放回第一个,查找多个时可以使用find_elements方法。
# 查找淘宝左侧导航栏所有条目
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://taobao.com')
lis = browser.find_elements_by_css_selector('.service-bd li')
print(lis)
browser.close()
??该结果得到的类型为列表类型,列表每个节点都是WebElement类型,如果使用find_element方法只会返回一个节点,该节点为WebElement类型。
??获取多个节点是也可以使用上面单个节点的方法element后面加个s而已。
??同理,lis = browser.find_elements_by_css_selector(‘.service-bd li’)等价于
lis = browser.find_elements(By.CSS_SELECTOR, ‘.service-bd li’)。
6.节点交互
??Selenium可以驱动浏览器执行一些操作。比如常见的用法:send_keys输入文字,clear清空文字,click点击按钮等。
# 打开淘宝,在搜索框输入iphone,等待一秒,清空输入框,在输入ipad点击搜索
from selenium import webdriver
import time
browser = webdriver.Chrome()
browser.get('https://taobao.com')
input = browser.find_element_by_id('q')
input.send_keys('iphone')
time.sleep(1)
input.clear()
input.send_keys('ipad')
button = browser.find_element_by_class_name('btn-search')
button.click()
7.动作链
??上个实例是针对于某个节点执行的。但是在一些操作中,没有特定的执行对象,比如鼠标拖拽、键盘按键等,这种需要动作链来执行。
# 打开一个拖拽实例,将选中的节点拖拽到另一个节点
from selenium import webdriver
from selenium.webdriver import ActionChains# 执行高级用户行为(鼠标移动拖拽等)
browser = webdriver.Chrome()
url = 'https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
browser.get(url)
browser.switch_to.frame('iframeResult')# 找到此窗口
source = browser.find_element_by_id('draggable')
target = browser.find_element_by_id('droppable')
actions = ActionChains(browser)
actions.drag_and_drop(source,target)# 拖拽操作
actions.perform()# 触发
8.运行JavaScript
??有些操作,selenium并没有提供API,例如下拉进度条,这种情况可以模拟运行JavaScript,下拉进度条使用execute_script方法即可实现。
# 利用execute_script方法将进度条拉到最底部,然后弹出警告提示框
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.zhihu.com/explore')
browser.execute_script('window.scrollTo(0,document.body.scrollHeight)')
browser.execute_script('alert("To Buttom")')
??有了该方法,没有被提供API的功能都可以运行JavaScript的方式实现。
9.获取节点信息
??前面我们通过page_source已经获取了网页源码,这是可以通过之前的方法解析库(正则表达式、xpath、BeautifulSoup等)来提取所需信息。
??不过,selenium提供了选择节点的方法,同时也提供了相关的方法和属性来获取节点信息,例如属性、文本值等。
- 获取属性
??运行代码,打开示例界面,获取class名称为logo-image的节点,最后输出其src属性。
from selenium import webdriver
browser = webdriver.Chrome()
url = 'https://spa2.scrape.center/'
browser.get(url)
logo = browser.find_element_by_class_name('logo-image')
print(logo)
print(logo.get_attribute('src'))
- 获取文本值
??打开示例界面,获取class名称为logo-title的节点,再将其内部的文本值打印出来。
from selenium import webdriver
browser = webdriver.Chrome()
url = 'https://spa2.scrape.center/'
browser.get(url)
input = browser.find_element_by_class_name('logo-title')
print(input.text)
- 获取ID、位置、标签名和大小
??除了属性和文本值,WebElement节点还具有其他属性。例如id属性用于获取节点ID,location用于获取节点在页面中的相对位置,tag_name用于获取标签名称,size获取节点大小(宽高)。
??下面示例首先获取名称为logo-title的节点,然后分别调用节点的id、location、tag_name、size属性获取其对应的属性值。
from selenium import webdriver
browser = webdriver.Chrome()
url = 'https://spa2.scrape.center/'
browser.get(url)
input = browser.find_element_by_class_name('logo-title')
print(input.id)
print(input.location)
print(input.tag_name)
print(input.size)
10.切换Frame
??在网页中,有一种节点叫做iframe,即子Frame。其结构与外部网页结构完全一致。selenium打开一个网页默认在父Frame里面操作。如果页面中有子Frame,默认是获取不了其节点信息的,这时需要使用swtch_to.frame方法切换Frame。
??这里和动作链时演示的网页一样,首先通过swtch_to.frame方法切换到子Frame里,然后尝试获取其中的logo节点,如果找不到(子Frame没有logo节点)抛出NoSuchElementException异常,捕捉异常输出NO LOGO,接着切回父Frame,重新获取。
from selenium import webdriver
import time
from selenium.common.exceptions import NoSuchElementException
browser = webdriver.Chrome()
url = 'https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
browser.get(url)
browser.switch_to.frame('iframeResult')
try:
logo = browser.find_element_by_class_name('logo')
except NoSuchElementException:
print('NO LOGO')
browser.switch_to.parent_frame()
logo = browser.find_element_by_class_name('logo')
print(logo)
print(logo.text)
??所以,当页面中包含子Frame时,需要使用swtch_to.frame方法切换到对应的Frame,在进行操作。
11.延时等待
??在Selenium中,get方法在网页框架加载结束后才会结束执行,如果我们尝试在get方法之行结束后获取网页源码,可能并不完整,因为某些页面还有额外的Ajax请求,页面还会经由JavaScript渲染。所以在必要的时候时候可以设置浏览器延时来确保节点已经被加载出来。
??这里有隐式和显式两种等待方式。
- 隐式等待
??在查找节点没有立即出现时,隐式等待会先等待一段时间再查找DOM,在超出设定的时间后,抛出异常(默认等待时间是0)。
from selenium import webdriver
browser = webdriver.Chrome()
browser.implicitly_wait(10)
browser.get('https://spa2.scrape.center/')
input = browser.find_element_by_class_name('logo-image')
print(input)
??这里用implicitly_wait方法实现的。
- 显式等待
??隐式等待效果其实并不大好,因为只规定了一个固定时间,页面的加载时间会受网络条件所影响。
??这是还可以采用另一种方式:显式等待。这种方式会指定要查找的节点和最长等待时间;如果超出最长等待时间还未加载出该节点,就抛出异常。
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
browser = webdriver.Chrome()
browser.get('https://www.taobao.com')
wait = WebDriverWait(browser, 10)
input = wait.until(EC.presence_of_element_located((By.ID,'q')))
button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR,'.btn-search')))
print(input,button)
??这里引用WebDriverWait对象,指定最长等待时间为10,赋值给wait变量。然后调用wait的until方法,传入等待条件。
??presence_of_element_located代表节点出现其参数是节点的定位元组(By.ID,‘q’)即搜索框。在10秒内加载不出来则抛出异常。
??element_to_be_clickable代表按钮可点击。在10秒内加载不出来则抛出异常。
12.前进和后退
??Selenium可以使用forward实现前进,使用back方法实现后退。
import time
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.baidu.com')
browser.get('https://www.taobao.com')
browser.get('https://www.python.org')
browser.back()
time.sleep(2)
browser.forward()
browser.close()
??这里连续访问三个页面,使用back方法回到第二个页面,接着调用forward方法前进到第三个页面。
13.Cookie
??使用Selenium还可以对Cookie进行操作,比如获取、添加、删除等。
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.zhihu.com/explore')
print(browser.get_cookies())
browser.add_cookie({'name':'name','domain':'www.zhihu.com','value':'germey'})
print(browser.get_cookies())
browser.delete_all_cookies()
print(browser.get_cookies())
??该案例访问知乎,获取所有的cookie,然后添加一个cookie,传入一个字典。之后再次获取所有的cookie,最后delete_all_cookies方法删除所有cookie再输出。
14.选项卡管理
??访问网页的时候,有时会开启一个个选项卡,在使用Selenium时同样可以对选项卡操作。
import time
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.baidu.com')
browser.execute_script('window.open()')
print(browser.window_handles)
browser.switch_to.window(browser.window_handles[1])
browser.get('https://taobao.com')
time.sleep(1)
browser.switch_to.window(browser.window_handles[0])
browser.get('https://python.org')
??这里首先访问百度,调用execute_script方法,传入参数window.open()这个javascript语句,表示开启一个新的选项卡。window_handles表示获取当前开启的所有的选项卡,返回值是选项卡的代号列表。切换选项卡可以使用switch_to.window方法,参数是目的选项卡的代号。
15.异常处理
??使用Selenium时难免遇到一些异常,这是同样可以使用try except语句捕获各种异常。
??我们可以使用访问百度,尝试选择一个不存在的节点。
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.baidu.com')
browser.find_element_by_id('hello')
??这时我们可以看到抛出了NoSuchElementException异常,这通常表示节点未找到,我们可以使用try except语句捕获各类异常,改写如下:
from selenium import webdriver
from selenium.common.exceptions import TimeoutException,NoSuchElementException
browser = webdriver.Chrome()
try:
browser.get('https://www.baidu.com')
except TimeoutException:
print('Time Out')
try:
browser.find_element_by_id('hello')
except NoSuchElementException:
print('No Element')
finally:
browser.close()
16.反屏蔽
??现在很多的网站也加强了对Selenium检测,防止爬虫恶意爬取,对Selenium进行了屏蔽。
??检测的原理就是当前浏览器窗口下的window.navigator对象是否包含webdriver属性。因为正常使用浏览器时,这个属性应该是undefined,一旦使用了Selenium,这个就会设置为webdriver属性。很多网站通过JavaScript语句判断是否存在webdriver属性,如果存在直接屏蔽。
??这里很多人会想如果我直接使用下面的语句JavaScript语句把webdriver属性置空不就解决了吗。
Object.defineProperty(navigator,"webdriver",{get:()=>undefined})
??这行代码缺失可以将webdriver属性留空,但是execute_script方法在页面加载完毕后才调用这行语句的,在此之前浏览器已经检测到了,太晚了。
??在Selenium中可以使用DCP解决这个问题,利用它可以实现在每个页面刚加载时就执行JavaScript语句。这里使用执行DCP的方法叫做Page.addScriptToEvaluateOnNewDocument,将上方JavaScript传入即可。
from selenium import webdriver
from selenium.webdriver import ChromeOptions
option = ChromeOptions()
option.add_experimental_option('excludeSwitches',['enable-automation'])
option.add_experimental_option('useAutomationExtension',False)
browser = webdriver.Chrome(options=option)
browser.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument',{
'source':'Object.defineProperty(navigator,"webdriver",{get:()=>undefined})'
})
browser.get('https://antispider1.scrape.center/')
17.无头模式
??前面每次访问都会打开一个浏览器窗口,这样无疑很麻烦,而且还会增加资源加载的时间和网络带宽。
??Chrome浏览器在60版本之后,就支持了无头模式。
from selenium import webdriver
from selenium.webdriver import ChromeOptions
option = ChromeOptions()
option.add_argument('--headless')
browser = webdriver.Chrome(options=option)
browser.set_window_size(1366,748)
browser.get('https://www.baidu.com')
browser.get_screenshot_as_file('preview.png')
??运行后就会发现,窗口不会再弹出,代码依然正常,并且输出了页面截图。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!