添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
【Python】爬虫篇 Selenium运用你熟练到了什么程度?

【Python】爬虫篇 Selenium运用你熟练到了什么程度?

利用selenium这个库去做爬虫其目的是模仿人的鼠标和键盘操作,爬虫中主要用来解决JavaScript渲染问题。

比如有些代码是用动态的JS写的,传统的爬虫是很难爬取到数据的,而使用selenium去模拟人的鼠标和键盘操作则可以避免这个问题。

但也不算十全十美,问题就是爬虫的速度并不能算很高效。

1、支持的浏览器

Edge、Firefox、Safari,Google、Chrome等等;

2、实际简要的执行过程

Selenium模仿真正用户的操作,如打开浏览器,跳转指定url,查看指定数据等;

① 驱动浏览器

② 根据代码中设定的url,进行跳转并加载内容

③ 当然可以插入点击,输入查询内容等操作

④ 爬取并分析数据

3、安装

用python写爬虫的时候,主要用的是selenium的Webdriver;
#安装selenium库
pip install selenium
#安装对应浏览器驱动
# 我们可以通过下面的方式先看看Selenium.Webdriver支持哪些浏览器
from selenium import webdriver
print(help(webdriver))

适用浏览器:

PACKAGE CONTENTS
    android (package)    blackberry (package)    chrome (package)
    common (package)     edge (package)          firefox (package)
    ie (package)         opera (package)         phantomjs (package)
    remote (package)     safari (package)        support (package)    webkitgtk (package)
#这里要说一下比较重要的PhantomJS
#PhantomJS是一个而基于WebKit的服务端JavaScript API
#支持Web而不需要浏览器支持
#其快速、原生支持各种Web标准:Dom处理,CSS选择器,JSON等等
#PhantomJS可以用用于页面自动化、网络监测、网页截屏,以及无界面测试
注意下载版本号一致的WebDriver,否则出现异常,chrome地址栏输入chrome://version/ 查看自己的Chrome版本;

示例:

from selenium import webdriver
# #声明浏览器对象
browser1 = webdriver.Chrome()
browser2 = webdriver.Firefox()
# #访问页面
browser1.get("百度一下,你就知道")
print(browser1.page_source)
#关闭当前窗口
browser1.close()

下方也有分享学习Python常用到的安装资源以及一些语法学习、爬虫项目练习资源,自学Python的朋友有需要的可以下载↓↓

<Python基础学习资料 安装资源+项目练习>

4、Selenium常用操作

(1)请求

调用WebDriver 的 get 方法打开一个链接,WebDriver 将等待,直到页面完全加载完毕(onload 方法执行完毕), 然后才会继续执行get 方法后面的代码。

值得注意的是,如果你的页面使用了大量的Ajax加载, WebDriver可能不知道什么时候页面才会完成加载。

如果你想确保这些页面被完全加载,可以使用Waits操作。

from selenium import webdriver
driver = webdriver.Chrome()  # 打开谷歌浏览器
driver.get("https://github.com") # 请求一个页面
driver.close() # 关闭浏览器

这里用 get 方法访问百度,示例:

import time
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.baidu.com')
print(browser.page_source)
time.sleep(10)
browser.close()

运行代码后会弹出 Chrome 浏览器并且自动访问百度,然后控制台会输出百度页面的源代码,10秒后浏览器关闭。

(2)查找元素

Selenium提供了以下方法用来定位一个元素:

  • 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

下列方法用来一次查找多个元素 (返回一个list列表):

  • 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"

1)单个元素查找

from selenium import webdriver
browser = webdriver.Chrome()
browser.get("http://www.taobao.com")
input_first = browser.find_element_by_id("q")
input_second = browser.find_element_by_css_selector("#q")
input_third = browser.find_element_by_xpath('//*[@id="q"]')
print(input_first)
print(input_second)
print(input_third)
browser.close()

这里我们通过三种不同的方式去获取响应的元素,第一种是通过id的方式,第二个中是CSS选择器,第三种是xpath选择器。

结果都是相同的:

2)多个元素查找

多个元素和单个元素的区别,find_elements,单个元素是find_element,其他使用上没什么区别;

通过其中的一个例子演示:

from selenium import webdriver
browser = webdriver.Chrome()
browser.get("http://www.taobao.com")
lis = browser.find_elements_by_css_selector('.service-bd li')
print(lis)
browser.close()

这样获得就是一个列表:

上面的方式也是可以通过导入from selenium.webdriver.common.by import By 这种方式实现;

(3)切换 Frame

在很多网页中都有Frame标签,我们爬取数据的时候就涉及到切入到frame中以及切出来的问题;

Selenium 打开页面后,默认是在父级 Frame 里面操作,而此时如果页面中还有子 Frame,Selenium 是不能获取到子 Frame 里面的节点的。

这时需要使用 switch_to.frame 方法来切换 Frame:

from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException
browser = webdriver.Chrome()
url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable'
browser.get(url)
browser.switch_to.frame('iframeResult')
    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)

首先通过 switch_to.frame 方法切换到子 Frame 里面,然后尝试获取子 Frame 里的 logo 节点(实际上是不能找到的),若找不到,就会抛出 NoSuchElementException 异常。

接下来切换回父级 Frame,然后重新获取节点,则可以成功获取。

NO LOGO
<selenium.webdriver.remote.webelement.WebElement (session="70eb5ef47851fceefb1b7cd9a0c5026e", element="a4000bc2-1bb7-4e88-960d-a61a7b69eb7c")>
RUNOOB.COM
当页面中包含子 Frame 时,如果想获取子 Frame 中的节点,需要先调用 switch_to.frame 方法切换到对应的 Frame,然后再进行操作。

在使用selenium模块进行数据爬取时,会遇到爬取iframe中的内容,可能会因为定位的作用域问题爬取不到数据。

菜鸟教程的运行例子:

会以文本块生成xpath为/html/body/text(),代码如下:

#!/user/bin/
# -*- coding:UTF-8 -*-
# Author:Master
from selenium import webdriver
import time
driver = webdriver.Chrome(executable_path="./chromedriver")
driver.get('https://www.runoob.com/try/runcode.php?filename=HelloWorld&type=python3')
time.sleep(2)
text = driver.find_element_by_xpath('/html/body').text
print(text)
time.sleep(5)
driver.quit()

执行结果

很明显这并不是想要的结果;

当我们打开抓包工具定位到Hello, World!文本的时候会发现,该文本是在一个iframe中。

这样的话我们xpath所定位到的内容则是大的html中的路径,我们需要的内容则是在iframe中的小的html中。

想要解决问题的实质就是改变作用域,通过switch_to.frame(‘id’)方法来改变作用域就可以了。

#!/user/bin/
# -*- coding:UTF-8 -*-
# Author:Master
from selenium import webdriver
import time
driver = webdriver.Chrome(executable_path="./chromedriver")
driver.get('https://www.runoob.com/try/runcode.php?filename=HelloWorld&type=python3')
time.sleep(2)
driver.switch_to.frame('iframeResult')
text = driver.find_element_by_xpath('/html/body').text
print(text)
time.sleep(5)
driver.quit()

运行结果:

(4)在不同的窗口和框架之间移动

对于现在的web应用来说,没有任何frames或者只包含一个window窗口是比较罕见的。

WebDriver 支持在不同的窗口之间移动,只需要调用switch_to_window方法即可:

driver.switch_to.window("windowName")

所有的 driver 将会指向当前窗口,但是怎么知道当前窗口的名字呢,查看打开它的javascript或者连接代码:

<a href="somewhere.html" target="windowName">Click here to open a new window</a>

还可以在switch_to_window()中使用窗口句柄来打开它,也可以使用窗口句柄迭代所有已经打开的窗口:

for handle in driver.window_handles:
    driver.switch_to.window(handle)

WebDriver也支持在不同的frame中切换,比如

driver.switch_to.frame("frameName")
driver.switch_to.frame("frameName.0.child") # 切换到frameName的第一个子frame中名称为child的frame
driver.switch_to.default_content() # 返回父frame
element = driver.switch_to.active_element 
alert = driver.switch_to.alert # 切换到弹出对话框
driver.switch_to.parent_frame()

(5)等待方式

现在的大多数的Web应用程序都使用Ajax技术;

当一个页面被加载到浏览器时, 该页面内的元素可以在不同的时间点被加载,使得定位元素变得困难。

如果元素不在页面之中,会抛出 ElementNotVisibleException 异常,使用 Waits, 我们可以解决这个问题。

Waits提供了一些操作之间的时间间隔- 主要是定位元素或针对该元素的任何其他操作;

Selenium Webdriver 提供两种类型的Waits - 隐式和显式;

① 显式等待

显式等待是一种条件触发式等待;

直到设置的某一条件达成时才会继续执行,可以设置超时时间,如果超过超时时间元素依然没被加载,就会抛出异常。

from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
drive = webdriver.Chrome()
url = 'http://www.baidu.com/'
drive.get(url)
	WebDriverWait(self.driver,10).until(EC.presence_of_element_located(By.ID,"LoginForm[username]"))  #显示等待
except:
  print('%s页面未找到元素'% loc)

以上代码加载 baidu.com/ ,并定位id为"LoginForm[username]"的元素;

设置超时时间10秒,webDriverWait默认会500ms检测一下元素是否存在。

selenium提供了一些内置的用于显示等待的方法,位于expected_conditions类中:

② 隐式等待

隐式等待是在尝试定位某个元素时,如果没能立刻发现,就等待固定时长。

类似于socket超时,默认设置是0秒,即相当于最长等待时长;

在浏览器界面直观感受是:等待直到网页加载完成(地址栏这个地方不是× 变成如下)时继续执行,网页加载超过设置等待时长才报错。

方法示例:

from selenium import webdriver
drive = webdriver.Chrome()
url = 'http://www.baidu.com/'
#设置最大等待时长 10秒
drive.implicitly_wait(10)
drive.get(url)
user = drive.find_element_by_name("LoginForm[username]")

(6)异常处理

在使用 Selenium 的过程中,难免会遇到一些异常,例如超时、节点未找到等错误,一旦出现此类错误,程序便不会继续运行了。

这里我们可以使用 try except 语句来捕获各种异常;

示例节点未找到的异常:

from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.baidu.com/')
browser.find_element_by_id('hello')

输出

NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"[id="hello"]"}
  (Session info: chrome=83.0.4103.116)

此处抛出了 NoSuchElementException 异常,代表节点未找到。

为了防止程序遇到异常而中断,我们需要捕获异常:

from selenium import webdriver
from selenium.common.exceptions import TimeoutException, NoSuchElementException
browser = webdriver.Chrome()
    browser.get('https://www.baidu.com/')
except TimeoutException:
    print('Time Out')
    browser.find_element_by_id('hello')
except NoSuchElementException:
    print('No Element')
finally:
    browser.close()

输出

No Element

(7)操作Cookies

Selenium 可以方便地对 Cookies 进行操作,例如获取、添加、删除 Cookies 等;

# 打开一个页面
driver.get("http://www.example.com")
# 现在设置Cookies,这个cookie在域名根目录下(”/”)生效
cookie = {"name" : "foo", "value" : "bar"}
driver.add_cookie(cookie)
# 现在获取所有当前URL下可获得的Cookies
driver.get_cookies()

(8)使用selenium爬取动态网页信息

示例:获取网易云音乐

from selenium import webdriver
#打开浏览器
brower = webdriver.Chrome()
url='https://music.163.com/#/discover/toplist'
brower.get(url)
#寻找logo文字
#logo = brower.find_elements_by_class_name('logo')[0]
#print(logo.text)
#一般情况下动态加载的内容都可以找到
#有一种情况就没有
#就是网页内存在网页框架iframe
#需要切换网页的层级
#语法:brower.switch_to.frame(iframe的id或者你提前获取这个对象,放入此处)
#方法一:id
#brower.switch_to.frame('g_iframe')
#方法二:name
#brower.switch_to.frame('contentFrame')
#方法三:提前用变量存iframe
iframe = brower.find_element_by_id('g_iframe')
brower.switch_to.frame(iframe)
#寻找大容器
toplist = brower.find_element_by_id('toplist')
#寻找tbody 通过标签名
tbody = toplist.find_elements_by_tag_name('tbody')[0]
#寻找所有tr
trs = tbody.find_elements_by_tag_name('tr')
dataList = []
for each in trs:
    rank = each.find_elements_by_tag_name('td')[0].find_elements_by_class_name('num')[0].text
    musicName = each.find_elements_by_tag_name('td')[1].find_elements_by_class_name('txt')[0].\
        find_element_by_tag_name('b').get_attribute('title')
    #print(musicName)
    singer = each.find_elements_by_tag_name('td')[3].find_elements_by_class_name('text')[0].\
        get_attribute('title')
    #print(singer)
    dataList.append([rank,musicName,singer])
#print(dataList)
from openpyxl import Workbook
wb = Workbook()
ws = wb.active