逼疯反扒的爬虫利器Selenium(自动化测试工具) = ̄ω ̄= Python爬虫

Selenium 简介

Selenium 不同于requests这种模拟浏览器进行数据爬取的库,而是直接运行在浏览器中,就如同真正的用户进行操作selenium中文文档selenium暂未发现官方文档,上述文档为爱好者自制

Selenium 部署

selenium库并非内置,所以需要导入pip install selenium
selenium是一个自动化程序,自动操控浏览器,所以需要一个插件可以对浏览器进行操作

  1. Chrome(外网,需要魔法)国内用户可以去淘宝镜像下载 淘宝镜像Chrome
  2. Firefox
  3. edge

将下载的驱动放入环境变量中(如果不知道什么是环境变量就放在Python安装文件夹中即可)

Selenium使用方法

Selenium是一个很庞大的库,这里不建议直接使用import直接导入全库,而是用到什么导入什么。

启动Selenium

启动selenium的话我们只需导入from selenium import webdriver即可
顾名思义,这个是用于启动浏览器的方法webdriver后面可以跟着需要启动的浏览器名称
比如.Firefox()(火狐浏览器).Edge()windows10系统默认浏览器.Chrome()谷歌浏览器.Opera()欧朋浏览器.Safari()Mac等苹果设备自带浏览器,启用这些浏览器的前提是你已经下载好驱动并放入环境变量中(这些浏览器方法首字母需要大写!!!)
driver = webdriver.Firefox()启动火狐浏览器并将此对象赋值给driver

driver方法 作用
.name 检查驱动是基于什么浏览器(假设为.Firefox(),返回值则为firefox)
.get(网址) 已get方法打开指定网址
.title 返回当前页面标题
.current_url 当前url
.page_source 获取当前页面的源(重要方法)
.back() 在浏览器的历史记录中后腿一步。
.forward() 在浏览器历史记录中向前迈进了一步。
.refresh() 刷新页面
.implicitly_wait(时间) 隐式等待(下方有此方法详细使用说明)
.set_script_timeout(时间) 异步加载页面时等待的时间
.set_page_load_timeout(时间) 设置等待页面加载的时间,超出后报错(在get前使用)
.desired_capabilities 返回当前驱动的信息

对浏览器的操作

使用方法 作用
.current_window_handle 返回当前窗口的句柄(系统会给每个窗口分配一个句柄,可以根据句柄移动窗口、改变窗口大小、把窗口最小化等等。)
.window_handles 返回所以窗口的句柄
.maximize_window() 最大化窗口
.minimize_window() 最小化窗口
.fullscreen_window() 将窗口全屏
.set_window_size(宽,高) 指定窗口的大小
.get_window_size() 获取窗口的大小
,set_window_position(x,y) 设置窗口的位置
.get_window_position() 获取窗口的位置
.set_window_rect(x,y,宽,高) 设置状况的位置以及大小
.get_window_rect() 获取当前窗口的位置以及大小
.close() 关闭窗口
.quit() 退出驱动程序并关闭窗口
.switch_to 用于切换焦点(多窗口切换与切换到页面中内置页面frame元素中使用)

查找元素

常用查找方法 作用
.find_element_by_id(id名) 按id查找元素。(找到第一个符号要求的元素后立即返回)
.find_elements_by_id(id名) 按id查找多个元素。(找到多个符合要求的元素后合并成列表返回)
.find_element_by_class_name(class名) 按class名查找元素。(同样拥有查找多个元素的elements方法)
.find_element_by_xpath(xpath规则) 按xpath规则查找元素。(同样拥有查找多个元素的elements方法)
.find_element_by_link_text(文本) 根据标签中的文本查找元素,必须要全文匹配,比如<p>全文匹配</p>时文本必须为’全文匹配’(同样拥有查找多个元素的elements方法)
.find_element_by_partial_link_text(文本) 根据标签中的文本查找元素,部分匹配即可,比如<p>全文匹配</p>时文本可以为’匹配’(同样拥有查找多个元素的elements方法)
.find_element_by_name(name名) 按名称查找元素。(同样拥有查找多个元素的elements方法)
.find_element_by_tag_name(HTML标签) 按HTML标签查找元素。(同样拥有查找多个元素的elements方法)
.find_element_by_css_selector(css选择器) 根据css选择器查找元素。(同样拥有查找多个元素的elements方法)
.find_element(策略器 ,'元素') 策略器包含By.ID,By.TAG_NAME,By.CLASS_NAME,By.NAME(同样拥有查找多个元素的elements方法) 策略器需要导入from selenium.webdriver.common.by import By

行为链(模拟鼠标与键盘)

模拟鼠标的单击,双击,右击,悬浮等操作。需要引入from selenium.webdriver.common.action_chains import ActionChains 。创建行为链需要先实例化一个鼠标ActionChains(WebDriver实例)比如mouse = ActionChains(self.driver)

行为链模拟鼠标方法 作用
.perform() 执行操作(行为链是一系列操作,最后需要使用此方法让其他方法开始执行,类似数据库事务的提交操作)
.reset_actions() 清除操作(相当于数据库中事务的回滚)
.click(点击对象) 点击左键
.click_and_hold(点击对象) 按住左键(与点击不同,这是按住不松)
.context_click(点击对象) 点击右键
.double_click(点击对象) 双击鼠标左键
.drag_and_drop(下移对象, 上移对象) 将指定元素上下移动
.drag_and_drop_by_offset(点击对象, x, y) 点击指定对象后拖动至指定xy区域
.move_by_offset(x, y) 移动鼠标到坐标xy
.move_to_element(指定对象) 将鼠标移动到指定对象上并开始悬停
.move_to_element_with_offset(指定对象, x, y) 将鼠标移动到指定对象后并偏移xy。
.release(指定对象) 在指定对象上放开鼠标,不指定对象则为当前位置放开鼠标
行为链模拟键盘方法 作用
.key_down(按键, 发送对象) 按下键盘按键,但不释放键盘(需要同时按住多键时使用),每次只能传入一个按键,如果没有发送对象,则想当前焦点发送
.send_keys(按键) 选择按键
.key_up(按键, 发送对象) 放开按键,如果没有发送对象,则想当前焦点发送
send_keys_to_element(发送对象, 按键) 点击键盘
from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.keys import Keys
# 假设需要按ctrl+c
driver = webdriver.Firefox()
ActionChains(driver).key_down(Keys.CONTROL).send_keys('c').key_up(Keys.CONTROL).perform()

保存网页快照

使用方法 作用
.get_screenshot_as_file(文件路径.png) 要将屏幕快照保存到指定路径.save_screenshot(文件路径.png)方法作用相同
.get_screenshot_as_png() 获取屏幕快照的二进制并作为返回值返回
.get_screenshot_as_base64() 获取屏幕快照的base64编码字符串并作为返回值返回,在HTML中的嵌入图像中非常有用。

对cookie的操作

使用方法 作用
.get_cookies() 用字典的方式返回获取的cookies
.get_cookie(cookie名) 按cookie寻找cookie
.delete_cookie(名称) 删除指定名称的cookie
.delete_all_cookies() 删除全部cookie
.add_cookie(cookie) 将cookie添加到当前网页

页面等待

隐式等待

隐式等待只需要使用driver.implicitly_wait(等待时间)方法即可。隐式等待一旦设置后,整个程序的生命周期中都将会启动,下列代码会让浏览器等待网页加载,但如果超过等待时间,则会出现异常(隐式等待一旦设定,整个程序中使用webdriver寻找元素都会进行等待,所以不太推荐使用隐式等待,大多数情况下可以使用显式等待)
下面有个网站,因为图片应该使用了懒加载(校点在哪,加载哪里,我们直接获取网页源码会无法得到想要的结果,所以我们就可以使用等待,等我们需要的元素出现后在保存页面)

from selenium import webdriver

class Study:
    def __init__(self):
        self.url = 'https://article.xuexi.cn/articles/index.html?art_id=15308933272663801705&item_id=15308933272663801705&study_style_id=feeds_default&showmenu=true&aid=15308933272663801705&item_type=1&recoid=12578234090040657023_1581240011&cid=&study_comment_disable=1&pid=34682268069627302&ref_read_id=8c1f7cad-22cf-473b-be16-3bf204f50468&ptype=100&source=share&share_to=wx_single&from=singlemessage'
        self.header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0'}
        self.driver = None

    def open_web(self):
        """打开浏览器"""
        driver = webdriver.Firefox()
        # 最小化浏览器
        driver.minimize_window()
        return driver

    def get_url(self):
        """访问url"""
        # 使用selenium
        driver = self.open_web()
        driver.get(self.url)
        # 隐式等待
        driver.implicitly_wait(1)
        driver.find_element_by_css_selector('img[class="xxqg-image"]')
        return driver.page_source


    def run(self):
        """启动"""
        # 保存网页源码
        html = self.get_url()
        print(html)
	

if __name__ == '__main__':
    Study().run()

显式等待

显式等待需要使用到WebDriverWaitexpected_conditions
显式等待要用到的方法我们需要先导入一下

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.implicitly_wait(3)
        driver.find_element_by_css_selector('img[class="xxqg-image"]')
		# 显示等待(内都)
        WebDriverWait(driver, 3).until(
            EC.presence_of_all_elements_located((By.CSS_SELECTOR, 'img[class="xxqg-image"]'))
        )

WebDriverWait会对until中的期望条件不断进行测试,如果在指定时间无法通过测试,那么就会返回异常(默认0.5秒一次,可以在WebDriverWait()方法第三个参数处更改默认时间,如WebDriverWait(driver, 3, 0.1))WebDriverWait()除了.until()还有.until_not()方法
相对于隐式等待,显示等待提供了很多方法可以作为等待条件expected_conditions(EC)

常用期望条件

常用期望(expected_conditions )方法 作用
.title_is(标题) 检查标题是否达到预期(匹配则返回True,否则返回false。)
.title_contains(标题) 检查标题是否包含大小写
.presence_of_element_located((By类,'期望条件')) 检查期望条件是否在页面源码中出现,不可以见元素也算(这里传入的必须是一个元组)
.presence_of_all_elements_located((By类,'期望条件')) 检查是否满足一直一个期望条件
.visibility_of_element_located((By类,'期望条件')) 检查期望条件是否在可见页面中出现,必须为可见元素,在页面中宽高大于0的为可见元素(这里传入的必须是一个元组)
.visibility_of(期望元素) 页面中是否出现期望元素
presence_of_all_emement_located() 检查页面是否所以元素都已经加装完毕
.url_contains(期望元素) 检查url是否和期望元素完全符合
.url_to_be(期望元素) .url_contains(元素) 方法作用类型,区别是url_to_be使用双等==判断是否一致url_contains使用is判断是否一致
.url_matches(期望元素) 检查url中是否包含期望元素
.url_changes(期望元素) 是否与期望元素不同
.element_to_be_cliable() 检查期望调试是否已经可用并且可以点击

期望条件有很多我并没有列举出全部,有需要的可以去selenium库中的expected_conditions.py文件中查看

打开多窗口与切换窗口

如果使用多个get,则会打开多个浏览器,如果我们想在同一个浏览器中启动做个窗口需要用到.execute_script()方法执行js命令,打开一个新窗口的js命令为window.open('网址')打开窗口需要用到

driver.get('https://www.baidu.com')
driver.execute_script('window.open("https://www.baidu.com/baidu?wd=python")')

切换窗口我们需要用到.switch_to方法切换窗口与.window_handles方法来获取窗口的句柄

.switch_to常用方法 用法
.switch_to.window(窗口句柄) 切换窗口焦点(必须拿到窗口句柄,可以通过.window_handles方法获得当前浏览器中全部窗口的句柄),如
driver.switch_to.window(driver.window_handles[0])
.switch_to.frame() 切换到嵌套页面中(HTML中<iframe></iframe>标签中的内容即为嵌套页面),可以使用多种方法找到嵌套页面,如
driver.switch_to.frame('frame_name')
driver.switch_to.frame(1)
driver.switch_to.frame(driver.find_element_by_tag_name("iframe"))
.switch_to.parent_frame() 切换到父页面中
class Study:
    def __init__(self):
        self.url = 'https://article.xuexi.cn/articles/index.html?art_id=15308933272663801705&item_id=15308933272663801705&study_style_id=feeds_default&showmenu=true&aid=15308933272663801705&item_type=1&recoid=12578234090040657023_1581240011&cid=&study_comment_disable=1&pid=34682268069627302&ref_read_id=8c1f7cad-22cf-473b-be16-3bf204f50468&ptype=100&source=share&share_to=wx_single&from=singlemessage'
        self.header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0'}
        self.driver = None

    def open_web(self):
        """打开浏览器"""
        driver = webdriver.Firefox()
        # 最小化浏览器
        driver.minimize_window()
        return driver

    def get_url(self):
        """访问url"""
        # 使用selenium
        driver = self.open_web()
        driver.get(self.url)
        # 网页逻辑太差,且图片应该为懒加载,所以需要使用此条件来判断是否加载完成,注释下方两行获取的网页将不完整
        # 显式等待
        WebDriverWait(driver, 3).until(
            EC.presence_of_all_elements_located((By.CSS_SELECTOR, 'img[class="xxqg-image"]'))
        )
        return driver.page_source


    def run(self):
        """启动"""
        # 保存网页源码
        html = self.get_url()
        print(html)
	

if __name__ == '__main__':
    Study().run()

智慧树登陆测试

from selenium import webdriver
from selenium.webdriver.common.action_chains import ActionChains
"""
测试目标(此模块只是单纯的用于selenium模块的学习使用)
自动登录智慧树(https://passport.zhihuishu.com/login)
"""


# Wisdom(智慧)
class WisdomTree:
    def __init__(self, uid, password, school='在这里写上一个学校'):
        # 手机号登陆(参数让登陆后直接跳转到个人页面)
        self.login_url = 'https://passport.zhihuishu.com/login?service=https://onlineservice.zhihuishu.com/login/gologin'
        # 学号登陆
        self.login_id_url = 'https://passport.zhihuishu.com/login?service=https://onlineservice.zhihuishu.com/login/gologin#studentID'
        self.id_name = uid
        self.password = password
        self.school_name = school
        self.driver = None

    def open_web(self):
        """打开浏览器"""
        self.driver = webdriver.Firefox()
        # 最小化浏览器
        self.driver.minimize_window()

    def login_web(self):
        """登陆网站"""
        while True:
            # 判断输入的为手机号还是学号(1开头为手机号2开头为学号,其余开头为输入错误)
            if self.id_name[0] == '1' and len(self.id_name) == 11:
                # 打开手机账号的登陆网址
                self.driver.get(self.login_url)
                # 寻找登陆名输入框并输入用户名
                login_name = self.driver.find_element_by_id('lUsername')
                login_name.send_keys(self.id_name)
                # 寻找密码栏输入框并输入密码
                login_password = self.driver.find_element_by_id('lPassword')
                login_password.send_keys(self.password)
                # 执行点击(click)事件
                login_click = self.driver.find_element_by_class_name('wall-sub-btn')
                login_click.click()
                return '正在通过电话号登录'
            elif self.id_name[0] == '2' and len(self.id_name) == 11:
                # 打开学号账号的登陆网址
                self.driver.get(self.login_id_url)
                # 寻找学校输入框后输入学校
                school = self.driver.find_element_by_class_name('school-search-ipt')
                school.send_keys(self.school_name)
                # 选择学校(填写学校后需要鼠标出发点击事件确定学校,否则无法登录)
                school_click = self.driver.find_element_by_xpath('//*[@id="schoolListCode"]/li')
                # 实例化鼠标
                mouse = ActionChains(self.driver)
                # 点击学校
                mouse.click(school_click)
                mouse.perform()
                # 寻找学号栏并输入学号
                uid = self.driver.find_element_by_id('clCode')
                uid.send_keys(self.id_name)
                # 寻找密码栏输入框并输入密码
                password = self.driver.find_element_by_id('clPassword')
                password.send_keys(self.password)
                # 执行点击(click)事件
                login_click = self.driver.find_element_by_class_name('wall-sub-btn')
                login_click.click()
                return '正在通过学号登录'
            else:
                self.id_name = input('输入有误,请重新输入学号或手机号:')

    def run(self):
        """启动代码"""
        # 打开浏览器
        self.open_web()
        # 登陆网站
        self.login_web()


if __name__ == '__main__':
    user = input('请输入学号或手机号:')
    pd = input('请输入密码:')
    WisdomTree(user, pd).run()

图片爬取测试(selenium+requests配合使用)

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
# @Author  : 寻觅
# @File    : 图片爬取.py
# @Time    : 2020/2/12 13:24
# @Software: PyCharm

import requests
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
from bs4 import BeautifulSoup


class Study:
    def __init__(self):
        self.url = 'https://article.xuexi.cn/articles/index.html?art_id=15308933272663801705&item_id=15308933272663801705&study_style_id=feeds_default&showmenu=true&aid=15308933272663801705&item_type=1&recoid=12578234090040657023_1581240011&cid=&study_comment_disable=1&pid=34682268069627302&ref_read_id=8c1f7cad-22cf-473b-be16-3bf204f50468&ptype=100&source=share&share_to=wx_single&from=singlemessage'
        self.header = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0'}
        self.driver = None

    def open_web(self):
        """打开浏览器"""
        driver = webdriver.Firefox()
        # 最小化浏览器
        driver.minimize_window()
        return driver

    def get_url(self):
        """访问url"""
        # 使用requests无法取得完整的页面源码,如下
        # html = requests.get(self.url, headers=self.header)
        # html.encoding = 'utf-8'
        # return html.content
        # 使用selenium
        driver = self.open_web()
        driver.get(self.url)
        # 网页逻辑太差,且图片应该为懒加载,所以需要使用此条件来判断是否加载完成,注释下方两行获取的网页将不完整
        # 隐式等待
        # driver.implicitly_wait(1)
        # driver.find_element_by_css_selector('img[class="xxqg-image"]')
        # 显式等待
        WebDriverWait(driver, 3).until(
            EC.presence_of_all_elements_located((By.CSS_SELECTOR, 'img[class="xxqg-image"]'))
        )
        return driver.page_source

    def write_html(self, html):
        """写入到本地"""
        with open('河南学习强国.html', 'w') as w:
            w.write(html)

    # extract(提取)
    def data_extract(self, data):
        """数据提取"""
        bs_data = BeautifulSoup(data, 'lxml')
        data = bs_data.select('img[class="xxqg-image"]')
        img_url = []
        for i in data:
            img_url.append(i['data-src'])
        return img_url

    def get_img(self, img_url):
        """将图片链接转为二进制格式的图片"""
        img_data = []
        i = 1
        print('获取的网页中首个图片为:', img_url[0])
        for url in img_url:
            print('\r共有%d张图片。正在下载第%d张图片' % (len(img_url), i), end=" ")
            img = requests.get(url, headers=self.header)
            img_data.append(img)
            i += 1
        return img_data

    def write_img(self, img_data):
        """保存图片"""
        i = 1
        for img in img_data:
            with open(f'图片/第{i}张图片.jpg', 'wb') as w:
                print('\r共有%d张图片。正在保存第%d张图片' % (len(img_data), i), end=" ")
                w.write(img.content)
            i += 1

    def run(self):
        """启动"""
        # 保存网页源码
        html = self.get_url()
        # 保存网页源码
        self.write_html(html)
        # 获取网页源码
        # html = open('河南学习强国.html').read()
        # 提取图片链接
        # print('正在获取图片链接')
        # img_url = self.data_extract(html)
        # 将链接转换为图片
        # print('正在将链接转换为图片')
        # img_data = self.get_img(img_url)
        # 保存图片
        # print('\n正在保存')
        # self.write_img(img_data)
        # print('\n程序完成')

if __name__ == '__main__':
    Study().run()



发布了78 篇原创文章 · 获赞 45 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_39611230/article/details/104229394