selenium

selenium本身是一个自动化测试工具。它可以让python代码调用浏览器。并获取到浏览器中加载的各种资源。 我们可以利用selenium提供的各项功能。 帮助我们完成数据的抓取

selenium概述

我们在抓取一些普通网页的时候requests基本上是可以满足的. 但是, 如果遇到一些特殊的网站. 它的数据是经过加密的. 但是呢, 浏览器却能够正常显示出来. 那我们通过requests抓取到的内容可能就不是我们想要的结果了. 例如,

电影票房数据. 在浏览器上看的时候是正常的. 那么按照之前的逻辑. 我们只需要看看数据是通过哪个请求拿到的就可以进行模拟请求了. 但是!

数据找到了. 接着看"预览"吧

我们发现这个数据是经过加密算法的. 这就头疼了. 直接通过requests拿到这些内容必须要解密才能看到真实数据. 但是该网站采用的加密方式又不是那么容易破解. 此时, 各位想想如果我能通过我的程序直接调用浏览器. 让浏览器去解密这些内容. 我们直接拿结果岂不妙哉. 哎~这就引出了我们本章要讲解的selenium了. 它可以完美解决上述问题

简单介绍一下selenium, 它本身是一个自动化测试的工具. 可以启动一个全新的浏览器.并从浏览器中提取到你想要的内容. 随着各种网站的反爬机制的出现. selenium越来越受到各位爬sir的喜爱. selenium最大的缺点其实就一个, 慢! 你想啊. 他要启动一个第三方的软件(浏览器), 并且还要等待浏览器把数据渲染完毕. 这个过程必然是很耗时的. 所以它慢.

接下来, 我们来聊聊selenium如何安装和使用.

就像其他第三方库一样, selenium直接用pip就可以安装了

pip install selenium

但是呢, 它与其他库不同的地方是他要启动你电脑上的浏览器, 这就需要一个驱动程序来辅助.

chrome驱动地址:     

http://chromedriver.storage.googleapis.com/index.html

或者:  https://registry.npmmirror.com/binary.html?path=chromedriver/

谷歌浏览器老版本下载: https://www.chromedownloads.net/chrome64win/

这里推荐用chrome浏览器. 其他浏览器的驱动请自行百度.

根据你电脑的不同自行选择吧. win64选win32即可.

然后关键的来了. 把你下载的浏览器驱动放在python解释器所在的文件夹

Windwos: py -0p 查看Python路径

Mac: open + 路径

例如:open /usr/local/bin/

OK~ 前期准备工作完毕. 上代码看看, selenium是个什么鬼

from selenium.webdriver import Chrome  # 导入谷歌浏览器的类

# 创建浏览器对象
web = Chrome()  # 如果你的浏览器驱动放在了解释器文件夹

web.get("http://www.baidu.com")  # 输入网址
print(web.title)  # 打印title

运行一下你会发现神奇的事情发生了. 浏览器自动打开了. 并且输入了网址. 也能拿到网页上的title标题.

cool~

selenium的基本使用

加载网页:

selenium通过控制浏览器,所以对应的获取的数据都是elements中的内容

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Chrome()
# 访问百度
driver.get("http://www.baidu.com/")
# 截图
driver.save_screenshot("baidu.png")

访问谷歌

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time

driver = webdriver.Chrome()
# 访问谷歌
driver.get("https://www.google.com/")
input_select = driver.find_element(By.CLASS_NAME, "gLFyf")
input_select.send_keys("python", Keys.ENTER)
time.sleep(999)

定位和操作:

# 搜索关键字 杜卡迪
driver.find_element(By.ID, "kw").send_keys("杜卡迪")
# 点击id为su的搜索按钮
driver.find_element(By.ID, "su").click()

查看请求信息:

driver.page_source   # 获取页面内容
driver.get_cookies()
driver.current_url

退出

driver.close()  # 退出当前页面
driver.quit()   # 退出浏览器

 

元素定位的方法

selenium的定位操作

  1. 元素定位的两种写法:

    • 直接调用型(老版本方法)

      el = driver.find_element_by_xxx(value)
       # xxx是定位方式,后面我们会讲,value为该方式对应的值
    • 使用By类型(需要导入By) ,新版本方法,建议使用这种方式

      # 直接掉用的方式会在底层翻译成这种方式
      from selenium.webdriver.common.by import By
      driver.find_element(By.xxx,value)
  2. 元素定位的两种方式:

    • 精确定位一个元素,返回结果为一个element对象,定位不到则报错

      driver.find_element(By.xx, value)  # 建议使用
      driver.find_element_by_xxx(value)
    • 定位一组元素,返回结果为element对象列表,定位不到返回空列表

      driver.find_elements(By.xx, value)  # 建议使用
      driver.find_elements_by_xxx(value)
  3. 元素定位的八种方法:

    以下方法在element之后添加s就变成能够获取一组元素的方法

    • By.ID 使用id值定位

      el = driver.find_element(By.ID, '')
      el = driver.find_element_by_id()
    • By.XPATH 使用xpath定位

      el = driver.find_element(By.XPATH, '')
      el = driver.find_element_by_xpath()
    • By.TAG_NAME. 使用标签名定位

      el = driver.find_element(By.TAG_NAME, '')
      el = driver.find_element_by_tag_name()
    • By.LINK_TEXT使用超链接文本定位

      el = driver.find_element(By.LINK_TEXT, '')
      el = driver.find_element_by_link_text()
    • By.PARTIAL_LINK_TEXT 使用部分超链接文本定位

      el = driver.find_element(By.PARTIAL_LINK_TEXT  , '')
      el = driver.find_element_by_partial_link_text()
    • By.NAME 使用name属性值定位

      el = driver.find_element(By.NAME, '')
      el = driver.find_element_by_name()
    • By.CLASS_NAME 使用class属性值定位

      el = driver.find_element(By.CLASS_NAME, '')   
      el = driver.find_element_by_class_name()
    • By.CSS_SELECTOR 使用css选择器定位

      el = driver.find_element(By.CSS_SELECTOR, '')  
      el = driver.find_element_by_css_selector()

注意:

  • 建议使用find_element/find_elements

  • find_elementfind_elements的区别

  • by_link_textby_partial_link_text的区别: 全部文本和包含某个文本

 

注意:

find_element与find_elements区别

  1. 只查找一个元素的时候:可以使用find_element(),find_elements() find_element()会返回一个WebElement节点对象,但是没找到会报错,而find_elements()不会,之后返回一个空列表

  2. 查找多个元素的时候:只能用find_elements(),返回一个列表,列表里的元素全是WebElement节点对象

  3. 找到都是节点(标签)

  4. 如果想要获取相关内容(只对find_element()有效,列表对象没有这个属性) 使用 .text

  5. 如果想要获取相关属性的值(如href对应的链接等,只对find_element()有效,列表对象没有这个属性):使用 .get_attribute("href")

元素的操作

find_element_by_xxx方法仅仅能够获取元素对象,接下来就可以对元素执行以下操作 从定位到的元素中提取数据的方法

从定位到的元素中获取数据

el.get_attribute(key)           # 获取key属性名对应的属性值
el.text                         # 获取开闭标签之间的文本内容

对定位到的元素的操作

el.click()                      # 对元素执行点击操作

el.submit()                     # 对元素执行提交操作

el.clear()                      # 清空可输入元素中的数据

el.send_keys(data)              # 向可输入元素输入数据

使用示例:

from selenium import webdriver
from selenium.webdriver.common.by import By

driver =webdriver.Chrome()

driver.get("https://www.douban.com/")
# 打印页面内容 (获取到以后可以进行后续的xpath,bs4 或者存储等)
print(driver.page_source)

ret4 = driver.find_elements(By.TAG_NAME, "h1")
print(ret4[0].text)
#输出:豆瓣

ret5 = driver.find_elements(By.LINK_TEXT, "下载豆瓣 App")
print(ret5[0].get_attribute("href"))
#输出:https://www.douban.com/doubanapp/app?channel=nimingye

driver.close()

selenium的其他操作

无头浏览器

我们已经基本了解了selenium的基本使用了. 但是呢, 不知各位有没有发现, 每次打开浏览器的时间都比较长. 这就比较耗时了. 我们写的是爬虫程序. 目的是数据. 并不是想看网页. 那能不能让浏览器在后台跑呢? 答案是可以的

from selenium.webdriver import Chrome
from selenium.webdriver.chrome.options import Options

opt = Options()
opt.add_argument("--headless")
opt.add_argument('--disable-gpu')
opt.add_argument("--window-size=4000,1600")  # 设置窗口大小

web = Chrome(options=opt)

 

selenium 处理cookie

通过driver.get_cookies()能够获取所有的cookie

  • 获取cookie

    dictCookies = driver.get_cookies()
  • 设置cookie

    driver.add_cookie(dictCookies)
  • 删除cookie

    #删除一条cookie
    driver.delete_cookie("CookieName")
    # 删除所有的cookie
    driver.delete_all_cookies()

页面等待

  • 为什么需要等待 如果网站采用了动态html技术,那么页面上的部分元素出现时间便不能确定,这个时候就可以设置一个等待时间,强制等待指定时间,等待结束之后进行元素定位,如果还是无法定位到则报错

  • 页面等待的三种方法

    • 强制等待

      import time
      time.sleep(n)      # 阻塞等待设定的秒数之后再继续往下执行
    • 显式等待(自动化web测试使用,爬虫基本不用)

      from selenium.webdriver.common.keys import Keys
      from selenium.webdriver.common.by import By
      from selenium.webdriver.support.ui import WebDriverWait
      from selenium.webdriver.support import expected_conditions as EC
      
      WebDriverWait(driver, 10,0.5).until( EC.presence_of_element_located((By.ID, "myDynamicElement"))
      # 显式等待指定某个条件,然后设置最长等待时间10,在10秒内每隔0.5秒使用指定条件去定位元素,如果定位到元素则直接结束等待,如果在10秒结束之后仍未定位到元素则报错
    • 隐式等待 隐式等待设置之后代码中的所有元素定位都会做隐式等待

      driver.implicitly_wait(10)    # 在指定的n秒内每隔一段时间尝试定位元素,如果n秒结束还未被定位出来则报错

注意:

Selenium显示等待和隐式等待的区别 1、selenium的显示等待 原理:显示等待,就是明确要等到某个元素的出现或者是某个元素的可点击等条件,等不到,就一直等,除非在规定的时间之内都没找到,就会跳出异常Exception

(简而言之,就是直到元素出现才去操作,如果超时则报异常)

2、selenium的隐式等待

原理:隐式等待,就是在创建driver时,为浏览器对象创建一个等待时间,这个方法是得不到某个元素就等待一段时间,直到拿到某个元素位置。 注意:在使用隐式等待的时候,实际上浏览器会在你自己设定的时间内部断的刷新页面去寻找我们需要的元素

switch方法切换的操作

1 一个浏览器肯定会有很多窗口,所以我们肯定要有方法来实现窗口的切换。切换窗口的方法如下:
也可以使用 window_handles 方法来获取每个窗口的操作对象。例如:

# 1. 获取当前所有的窗口
current_windows = driver.window_handles

# 2. 根据窗口索引进行切换
driver.switch_to.window(current_windows[1])

driver.switch_to.window(web.window_handles[-1])  # 跳转到最后一个窗口
driver.switch_to.window(current_windows[0])  # 回到第一个窗口
2 iframe是html中常用的一种技术,即一个页面中嵌套了另一个网页,selenium默认是访问不了frame中的内容的,对应的解决思路是
driver.switch_to.frame(name/el/id)     #传入的参数可以使iframe对应的id值,也可以是用元素定位之后的元素对象

动手:qq邮箱

在使用selenium登录qq邮箱的过程中,我们会发现,无法在邮箱的登录input标签中输入内容,通过观察源码可以发现,form表单在一个frame中,所以需要切换到frame中

3 当你触发了某个事件之后,页面出现了弹窗提示,处理这个提示或者获取提示信息方法如下:
alert = driver.switch_to_alert()
4. 页面前进和后退
driver.forward()     # 前进
driver.back()        # 后退
driver.refresh()         # 刷新
driver.close()       # 关闭当前窗口
5、设置浏览器最大窗口
driver.maximize_window()  #最大化浏览器窗口

 

抓取boss直聘python工程师的招聘信息

准备工作

from selenium.webdriver import Chrome
import time

web = Chrome()

web.get("https://www.zhipin.com/beijing/")
time.sleep(999)  # 放置Chrome闪退。

搜索python

人的过程: 找到文本框输入"python", 点击"搜索"按钮.

机器的过程: 找到文本框输入"python", 点击"搜索"按钮.

发现没, 用selenium最爽的地方就是这里. 人是怎么操作的. 机器就怎么操作. 爽到极点

此时由于selenium是利用浏览器来进行操作。 所以它的环境和requests完全不同。在selenium中可以放心的使用这个copy xpath功能。

from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys  # 所有按键的指令
import time


web = Chrome()

web.get("https://www.zhipin.com/beijing/")

# 找到那个框框
# web.find_element_by_id()  # 过时的方案. 新版本中可能不再支持
# web.find_element_by_xpath()
# 当你使用find_xxxx如果找不到东西. 它会报错. 有可能不是下面代码的问题. 而是浏览器没有加载完成.
input_el = web.find_element(By.XPATH, '//*[@id="wrap"]/div[3]/div/div[1]/div[1]/form/div[2]/p/input')
input_el.send_keys("python", Keys.ENTER)  # 输入回车
time.sleep(1)
# 剩下的事情. 抓数据就完了
li_list = web.find_elements(By.XPATH, "//div[@class='job-list']/ul/li")
for li in li_list:
    # selenium用的不是一个标准的xpath语法规则
    # 最后一项不可以是@xxx, text()
    a = li.find_element(By.XPATH, ".//span[@class='job-name']/a")
    name = a.text  # 直接    节点.text
    href = a.get_property("href")  # @href
    price = li.find_element(By.XPATH, ".//span[@class='red']")
    print(name, href, price.text)

    a.click()  # 点击
    time.sleep(5)
    # 如果弹出了新窗口. 那么你需要把程序调整到新窗口里. 才能开始采集数据. 否则会报错.
    web.switch_to.window(web.window_handles[-1])  # 进入新窗口
    details = web.find_element(By.XPATH, "//div[@class='job-detail']").text
    print(details)
    print("================")
    # 关闭当前窗口
    web.close()  # 关闭了新窗口之后. selenium需要手动调整窗口到原来的窗口上
    web.switch_to.window(web.window_handles[0])

 

处理iframe, 多窗口调度

我们书接上回. 上回说到我们已经可以通过selenium拿到zhpin网的招聘信息了. 但是, 信息不够全面. 我们希望得到的不仅仅是一个岗位名称和公司名称, 我更想知道更加详细的职位描述以及岗位要求.

此时问题就来了. 我们可以在搜索页面点击进入到这个详情页. 然后就可以看到想要的职位描述了. 但是, 这时就涉及到如何从一个窗口转向另一个窗口了(切换选项卡).

注意! 我们看到的是新窗口的内容, 但是在selenium的视角里, 窗口依然停留在刚才那个窗口. 此时, 必须要将窗口调整到最新的窗口上才可以. 

# 窗口切换
# web.window_handles表示当前被打开的各个窗口
web.switch_to.window(web.window_handles[-1])  # 跳转到最后一个窗口
print(job_detail)
# 干一些事情
web.close()  # 关闭窗口 
web.switch_to.window(web.window_handles[0]) # 切换回来

完整代码

from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
import time

# 1.创建浏览器
web = Chrome()

# 2.输入网址
web.get("https://www.zhipin.com/beijing/")
# 爬取python相关的招聘信息

time.sleep(3)
​
# 建议再这里先等待一下下
time.sleep(1)

# 找到那个文本框, 输入python. 然后输入回车.
input_element = web.find_element(By.XPATH, '//*[@id="wrap"]/div[3]/div/div[1]/div[1]/form/div[2]/p/input')
input_element.send_keys("python", Keys.ENTER)  # 输入内容

time.sleep(3)
# 获取岗位名称和公司名称
# bs4 find   findall
# find_element    find_elements
li_list = web.find_elements(By.XPATH, '//*[@class="job-list"]/ul/li')
print(len(li_list))
for li in li_list:
    job_name_span = li.find_element(By.XPATH, './/span[@class="job-name"]')

    name = job_name_span.text  # 拿到里面的文本 -> 和原来的xpath不一样
    price_span = li.find_element(By.XPATH, ".//div[@class='job-limit clearfix']/span")
    price = price_span.text

    a = li.find_element(By.XPATH, './/span[@class="job-name"]/a')
    href = a.get_property("href")  # 获取属性
    print(name, price, href)
    a.click()
    time.sleep(3)
    web.switch_to.window(web.window_handles[-1])
    content = web.find_element(By.XPATH, "//div[@class='detail-content']")
    print(content.text)
    web.close()
    web.switch_to.window(web.window_handles[0])

time.sleep(999)
web.close()

iframe切换

接下来我们来看另一种操作.

之前我们抓取过一个网站. 里面把视频内容嵌套在一个iframe中. 那如果换成了selenium应该如何应对呢?

from selenium.webdriver import Chrome
from selenium.webdriver.common.by import By

web = Chrome()
web.get("http://www.wbdy.tv/play/42491_1_1.html")

# 找到那个iframe
iframe = web.find_element(By.XPATH, '//iframe[@id="mplay"]')

web.switch_to.frame(iframe)
val = web.find_element(By.XPATH, '//input[@class="dplayer-comment-input"]').get_attribute("placeholder")
print(val)

# 调整回上层结构
web.switch_to.parent_frame()
xxx = web.find_element(By.XPATH, '/html/body/div[2]/div[3]/div[2]/div/div[2]/h2').text
print(xxx)

 

抓取电影票房无头浏览器

抓取电影票房. 并且用正常的有浏览器窗口的方式来抓取. 然后再改成后台运行

from selenium.webdriver import Chrome
from selenium.webdriver.support.select import Select
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.common.by import By

import time

opt = Options()
opt.add_argument("--headless")   # 下面这三个参数只是针对谷歌浏览器,如果是其他浏览器,其他浏览器参数不一样
opt.add_argument('--disable-gpu')
opt.add_argument("--window-size=4000,1600")  # 设置窗口大小

web = Chrome(options=opt)
web.get('https://www.endata.com.cn/BoxOffice/BO/Year/index.html')

# 切换select
sel = Select(web.find_element(By.XPATH,'//*[@id="OptionDate"]'))
for i in range(len(sel.options)):
    sel.select_by_index(i)  # 按照索引位置切换
    time.sleep(1)
    table = web.find_element(By.XPATH,'//*[@id="TableList"]/table')
    print("===========================================")
    print(table.text)

 

selenium的优缺点

  • 优点

    • selenium能够执行页面上的js,对于js渲染的数据和模拟登陆处理起来非常容易

    • 使用难度简单

    • 爬取速度慢,爬取频率更像人的行为,天生能够应对一些反爬措施

  • 缺点

    • 由于selenium操作浏览器,因此会将发送所有的请求,因此占用网络带宽

    • 由于操作浏览器,因此占用的内存非常大(相比较之前的爬虫)

    • 速度慢,对于效率要求高的话不建议使用

注意! selenium不是无敌的, 不要认为selenium学会了。 所有的爬取工作都用它。 这东西偶尔拿来干一些小活。 数据量不大,不急,客户只要数据,不要源码的情况下可以考虑使用selenium. 

 

posted @ 2022-05-24 09:58  屠魔的少年  阅读(4)  评论(0)    收藏  举报