逼瘋反扒的爬蟲利器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()



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章