爬蟲之selenium

selenium介紹

由於requests模組不能執行js,有的頁面內容,我們在瀏覽器中可以看到,但是請求下來沒有。

selenium模組:模擬操作瀏覽器,完成人的行為。

selenium本質是通過驅動瀏覽器,完全模擬瀏覽器的操作,比如跳轉、輸入、點擊、下拉等,來拿到網頁渲染之後的結果,可支援多種瀏覽器。

模組安裝:

pip install selenium

下載驅動

驅動瀏覽器需要下載相應的驅動,Google要下Google的驅動,火狐要下火狐的驅動,並且版本要與當前瀏覽器對應。

這裡我選擇Google瀏覽器,首先查看當前Google瀏覽器的版本:

image

版本:

image

下載驅動,地址:CNPM Binaries Mirror (npmmirror.com),一定要下載對應的版本驅動,比如我Google瀏覽器版本為103.0.5060.134,就要下103.0.5060.134的驅動

image

下載對應作業系統即可。

基本使用

導入模組:

from selenium import webdriver

初始化(打開瀏覽器):

browser = webdriver.Chrome(executable_path='驅動路徑')
# 不寫路徑,要放到項目路徑下或環境變數中
browser = webdriver.Chrome()

在地址欄輸入地址:

browser.get('//www.baidu.com')

關閉標籤:

browser.close()

關閉瀏覽器:

browser.quit()

selenium用法

元素操作

操作瀏覽器頁面中的標籤。

1.搜索標籤

新版本:by=根據什麼查找,value=查找的值

  • find_element(by, value):找第一個
  • find_elements(by, value):找所有
by的參數 含義
By.ID 根據標籤id屬性查找
By.LINK_TEXT 根據a標籤的文字查找
By.PARTIAL_LINK_TEXT 根據a標籤的文字模糊匹配
By.TAG_NAME 根據標籤名查找
By.CLASS_NAME 根據標籤class屬性查找
By.NAME 根據標籤name屬性查找
By.CSS_SELECTOR 根據css選擇器查找
By.XPATH 根據xpath查找
from selenium.webdriver.common.by import By
# 查找網頁中id為'login'的標籤
tag = browser.find_element(by=By.ID, value='login')
# 查找網頁中class為'login'的所有標籤
tags = browser.find_elements(By.CLASS_NAME, value='login')
# 查找網頁div標籤中class屬性為'dd'的標籤
tag = browser.find_element(by=By.CSS_SELECTOR, value='div .dd')
# 查找網頁中a標籤文字為'登錄'的標籤
tag = browser.find_element(by=By.LINK_TEXT, value='登錄')

老版本

browser.find_element_by_id()  # 根據id
browser.find_element_by_link_text()  # 根據a標籤的文字
browser.find_element_by_partial_link_text()  # 根據a標籤的文字模糊匹配
browser.find_element_by_tag_name()  # 根據標籤名
browser.find_element_by_class_name()  # 根據類名
browser.find_element_by_name()  # 根據name屬性
browser.find_element_by_css_selector()  # css選擇器

2.點擊標籤

# 查找網頁中id為'login'的標籤
tag = browser.find_element(by=By.ID, value='login')
# 標籤點擊
tag.click()

3.向輸入框中寫內容

tag = browser.find_element(by=By.ID, value='inputTag')
tag.send_keys('內容')

4.清空

tag = browser.find_element(by=By.ID, value='inputTag')
tag.clear()

舉例:打開百度搜索’部落格園’:

from selenium import webdriver
from selenium.webdriver.common.by import By
# 打開瀏覽器
browser = webdriver.Chrome()
# 輸入網址進入
browser.get('//www.baidu.com')
# 查找百度輸入框
word = browser.find_element(By.ID, 'kw')
# 輸入框添加內容
word.send_keys('部落格園')
# 查找搜索按鈕
btn = browser.find_element(By.ID, 'su')
# 點擊搜索按鈕
btn.click()

# 等待2秒關閉瀏覽器
import time
time.sleep(2)
browser.quit()

等待元素被載入

程式操作頁面非常快,所以在取每個標籤的時候,標籤可能沒有載入好,取標籤時就會報錯,所以需要設置等待時間。

如果標籤找不到,就會等待,還找不到就會報錯:

from selenium import webdriver
from selenium.webdriver.common.by import By
# 打開瀏覽器
browser = webdriver.Chrome()
# 找不到標籤就等待2秒
browser.implicitly_wait(2)
# 輸入網址進入
browser.get('//www.baidu.com')
# 查找百度輸入框
word = browser.find_element(By.ID, 'kw')
# 輸入框添加內容
word.send_keys('部落格園')
# 查找搜索按鈕
btn = browser.find_element(By.ID, 'su')
# 點擊搜索按鈕
btn.click()
# 找到能進入部落格園的a標籤
cnblog = browser.find_element(By.LINK_TEXT, '部落格園')
# 進入部落格園
cnblog.click()

# 等待2秒關閉瀏覽器
import time
time.sleep(2)
browser.quit()

元素各項屬性

標籤對象調用 含義
標籤對象.location 標籤所在位置
標籤對象.size 標籤大小(高寬)
標籤對象.id 標籤id號(隨機給的),不是id屬性
標籤對象.tag_name 標籤名
標籤對象.get_attribute(‘屬性’) 標籤屬性值
from selenium import webdriver
from selenium.webdriver.common.by import By
# 打開瀏覽器
browser = webdriver.Chrome()
# 輸入網址進入
browser.get('//www.baidu.com')
# 查找百度輸入框
word = browser.find_element(By.ID, 'kw')

print(word.location)  # 標籤所在位置,{'x': 298, 'y': 188}
print(word.size)  # 標籤大小,{'height': 44, 'width': 550}
print(word.id)  # 標籤id號,不是id屬性
print(word.tag_name)  # 標籤名字,input
print(word.get_attribute('class'))  # 獲取標籤的class屬性值,s_ipt

browser.quit()

執行js程式碼

執行js用途:

  1. 普通滑屏,打開新標籤
  2. 可以執行js程式碼,別人網站的變數,函數,都可以拿到並執行

滾動條到最底部

from selenium import webdriver
# 打開瀏覽器
browser = webdriver.Chrome()
# 輸入網址進入
browser.get('//www.cnblogs.com/')
# 滾動條到最底部
browser.execute_script('scrollTo(0,document.body.scrollHeight)')

import time
time.sleep(3)
browser.quit()

打開新標籤

from selenium import webdriver
# 打開瀏覽器
browser = webdriver.Chrome()
# 輸入網址進入
browser.get('//www.cnblogs.com/')
# 打開新標籤
browser.execute_script('window.open()')

import time
time.sleep(3)
browser.quit()

切換選項卡

瀏覽器打開了多個選項卡,需要切換時:

browser.switch_to.window(browser.window_handles[1])
# 已棄用的方法
browser.switch_to_window(browser.window_handles[1])

browser.window_handles[0]代表第一個選項卡
browser.window_handles[1]代表第二個選項卡

from selenium import webdriver
import time
# 打開瀏覽器
browser = webdriver.Chrome()
# 進入部落格園
browser.get('//www.cnblogs.com/')
time.sleep(1)
# 打開新標籤
browser.execute_script('window.open()')
# 切換到新標籤
browser.switch_to.window(browser.window_handles[1])
# 新標籤進入百度
browser.get('//www.baidu.com/')
time.sleep(1)
# 切換回部落格園
browser.switch_to.window(browser.window_handles[0])

time.sleep(2)
browser.quit()

瀏覽器前進後退

瀏覽器前進:

browser.forward()

瀏覽器後退:

browser.back()

無介面瀏覽器

不顯示的打開瀏覽器的圖形化介面,還能獲取數據

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

chrome_options = Options()

chrome_options.add_argument('window-size=1920,3000')  # 指定瀏覽器解析度
chrome_options.add_argument('--disable-gpu')  # Google文檔提到需要加上這個屬性來規避bug
chrome_options.add_argument('--hide-scrollbars')  # 隱藏滾動條, 應對一些特殊頁面
chrome_options.add_argument('blink-settings=imagesEnabled=false')  # 不載入圖片, 提升速度
chrome_options.add_argument('--headless')  # 瀏覽器不提供可視化頁面. linux下如果系統不支援可視化不加這條會啟動失敗

browser = webdriver.Chrome(options=chrome_options)
browser.get('//www.cnblogs.com/')
print(browser.page_source)  # 當前頁面的內容(html內容)
browser.quit()

xpath的使用

簡單介紹

XPath 是一門在 XML 文檔中查找資訊的語言。

表達式 描述
nodename 選取此標籤的所有子標籤。如:div,選取div標籤下的子標籤
/ 找當前路徑下的標籤
// 找當前路徑子子孫孫下的標籤
. 表示當前路徑
.. 表示上一次
@ 選取屬性。

舉例:

表達式 描述
//* 所有標籤
//head 所有head標籤
//div/a 所有div標籤下的a標籤
//a[@class=”a1″] 所有class屬性為’a1’的a標籤
/div 所有最外層的div標籤
//head/text() 所有head標籤的文本內容
//div/a[1] div標籤下的第一個a標籤
//div/a[contains(@class,”li”)] 多個屬性要用contains

selenium中使用

網頁中打開F12,找到標籤,右鍵–>複製–>複製 Xpath,把值用在程式碼中即可。

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

browser = webdriver.Chrome()
browser.get('//www.cnblogs.com/')
res = browser.find_element(By.XPATH, '//*[@id="post_list"]/article[1]/section/div')
print(res.text)

image

異常處理

由於有時候程式操作瀏覽器過快,有些標籤會找不到等一些情況會出現報錯,這時候可以用異常處理:

from selenium.common.exceptions import TimeoutException, NoSuchElementException, NoSuchFrameException

from selenium import webdriver
from selenium.webdriver.common.by import By
# 打開瀏覽器
browser = webdriver.Chrome()
try:
    # 輸入網址進入
    browser.get('//www.baidu.com')
    # 查找百度輸入框
    word = browser.find_element(By.ID, 'kw')
    # 輸入框添加內容
    word.send_keys('部落格園')
    # 查找搜索按鈕
    btn = browser.find_element(By.ID, 'su')
    # 點擊搜索按鈕
    btn.click()
    # 找到能進入部落格園的a標籤
    cnblog = browser.find_element(By.LINK_TEXT, '部落格園')
except Exception as e:
    print(e)
finally:  # 不管報不報錯最後都關閉瀏覽器
    # 等待2秒關閉瀏覽器
    import time
    time.sleep(2)
    browser.quit()

登錄獲取cookie保存

獲取瀏覽器所有cookie:

cookies = browser.get_cookies()
print(cookies)

登錄部落格園並保存cookie:

from selenium import webdriver
from selenium.webdriver.common.by import By
import time
# 打開瀏覽器
browser = webdriver.Chrome()
browser.implicitly_wait(2)
try:
    # 進入部落格園
    browser.get('//www.cnblogs.com/')
    # 查找登錄按鈕
    login_btn = browser.find_element(By.LINK_TEXT, '登錄')
    login_btn.click()
    # 查找用戶名和密碼輸入框
    username = browser.find_element(By.ID, 'mat-input-0')
    password = browser.find_element(By.ID, 'mat-input-1')
    # 輸入用戶名密碼
    username.send_keys('xx')
    password.send_keys('123')
    btn = browser.find_element(By.CSS_SELECTOR, 'body > app-root > app-sign-in-layout > div > div > app-sign-in > app-content-container > div > div > div > form > div > button')
    btn.click()
    # 可能要驗證,睡眠一下手動過驗證
    time.sleep(10)
    # 保存cookie
    import json
    cookies = browser.get_cookies()
    with open('cnblog.json', 'w', encoding='utf-8') as f:
        json.dump(cookies, f)

except Exception as e:
    print(e)
finally:  # 不管報不報錯最後都關閉瀏覽器
    # 等待2秒關閉瀏覽器
    time.sleep(2)
    browser.quit()

如果標籤不好找出來,可以打開F12,找到標籤位置,複製css選擇器:

image

通過cookie達成登錄效果

from selenium import webdriver
import time
# 打開瀏覽器
browser = webdriver.Chrome()
browser.implicitly_wait(2)
try:
    # 進入部落格園
    browser.get('//www.cnblogs.com/')
    # 此時還不是登錄後的狀態
    time.sleep(2)
    # 拿出cookie
    import json
    with open('cnblog.json', 'r', encoding='utf-8') as f:
        cookies = json.load(f)
    # cookie保存時是列表套字典,但寫入cookie需要用字典格式,所以用循環
    for cookie in cookies:
        browser.add_cookie(cookie)
    # cookie全寫入後刷新頁面
    browser.refresh()
    # 此時是登錄後的狀態

except Exception as e:
    print(e)
finally:  # 不管報不報錯最後都關閉瀏覽器
    # 等待2秒關閉瀏覽器
    time.sleep(2)
    browser.quit()

動作鏈

模擬按住滑鼠拖動的效果,或者是在某個標籤上的某個位置點擊的效果,主要用來做驗證碼的破解(滑動驗證碼)。

導入:

from selenium.webdriver import ActionChains

初始化:得到動作鏈對象

actions = ActionChains(瀏覽器對象) 

添加動作的方法:

# 按住標籤的動作,按住source
actions.click_and_hold(sourse)
# 移動標籤的動作,source移到target
actions.drag_and_drop(sourse, target)
# 移動標籤的動作,按住source移到target並偏移
actions.click_and_hold(sourse).drag_and_drop_by_offset(target, xoffset, yoffset)
# 移動標籤的動作,按住source並移動(x, y)
actions.click_and_hold(sourse).move_by_offset(xoffset, yoffset)
# 鬆開動作
actions.release()

執行動作:上述方法需要語句執行動作

# 執行動作
actions.perform()

實戰:

方式一:基於同一個動作鏈串列執行

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

browser = webdriver.Chrome()
browser.implicitly_wait(5)
browser.get('//www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
# 頁面有一個iframe標籤,需要切換進去
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()

time.sleep(2)
browser.quit()
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains
import time

browser = webdriver.Chrome()
browser.implicitly_wait(5)
browser.get('//www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
browser.switch_to.frame('iframeResult')

source = browser.find_element(By.ID, 'draggable')
target = browser.find_element(By.ID, 'droppable')
# 拿到動作鏈對象
actions = ActionChains(browser).click_and_hold(source)
actions.drag_and_drop_by_offset(target, 10, 10)
# 執行動作
actions.perform()

time.sleep(2)
browser.quit()

方式二:不同的動作鏈,每次移動的位移都不同

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

browser = webdriver.Chrome()
browser.implicitly_wait(5)
browser.get('//www.runoob.com/try/try.php?filename=jqueryui-api-droppable')
browser.switch_to.frame('iframeResult')

source = browser.find_element(By.ID, 'draggable')
target = browser.find_element(By.ID, 'droppable')

ActionChains(browser).click_and_hold(source).perform()

distance = target.location['x'] - source.location['x']  # 兩個控制項的x軸的距離
track = 0
while track < distance:
    ActionChains(browser).move_by_offset(xoffset=20, yoffset=0).perform()
    track += 20
ActionChains(browser).release().perform()

time.sleep(2)
browser.quit()

打碼平台使用(驗證碼破解)

簡單的數字字母組合可以使用影像識別(python 現成模組),但是成功率不高,所以可以使用第三方打碼平台(破解驗證碼平台),花錢,把驗證碼圖片給它,它給你識別完,返回給你。

超級鷹驗證碼識別-專業的驗證碼雲端識別服務,讓驗證碼識別更快速、更準確、更強大 (chaojiying.com)

超級鷹開發文檔:

import requests
from hashlib import md5

class ChaojiyingClient:
    def __init__(self, username, password, soft_id):
        self.username = username
        password = password.encode('utf8')
        self.password = md5(password).hexdigest()
        self.soft_id = soft_id
        self.base_params = {
            'user': self.username,
            'pass2': self.password,
            'softid': self.soft_id,
        }
        self.headers = {
            'Connection': 'Keep-Alive',
            'User-Agent': 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0)',
        }
    def PostPic(self, im, codetype):
        """
        im: 圖片位元組
        codetype: 題目類型 參考 //www.chaojiying.com/price.html
        """
        params = {
            'codetype': codetype,
        }
        params.update(self.base_params)
        files = {'userfile': ('ccc.jpg', im)}
        r = requests.post('//upload.chaojiying.net/Upload/Processing.php', data=params, files=files,
                          headers=self.headers)
        return r.json()
    def PostPic_base64(self, base64_str, codetype):
        """
        im: 圖片位元組
        codetype: 題目類型 參考 //www.chaojiying.com/price.html
        """
        params = {
            'codetype': codetype,
            'file_base64': base64_str
        }
        params.update(self.base_params)
        r = requests.post('//upload.chaojiying.net/Upload/Processing.php', data=params, headers=self.headers)
        return r.json()
    def ReportError(self, im_id):
        """
        im_id:報錯題目的圖片ID
        """
        params = {
            'id': im_id,
        }
        params.update(self.base_params)
        r = requests.post('//upload.chaojiying.net/Upload/ReportError.php', data=params, headers=self.headers)
        return r.json()

if __name__ == '__main__':
    chaojiying = ChaojiyingClient('超級鷹用戶名', '超級鷹用戶名的密碼', '96001')  # 用戶中心>>軟體ID 生成一個替換 96001
    im = open('a.jpg', 'rb').read()  # 本地圖片文件路徑 來替換 a.jpg 有時WIN系統須要//
    print(chaojiying.PostPic(im, 1902))  # 1902 驗證碼類型  官方網站>>價格體系 3.4+版 print 後要加()
    # print chaojiying.PostPic(base64_str, 1902)  #此處為傳入 base64程式碼

用超級鷹破解超級鷹驗證碼並登錄:

from selenium import webdriver
from selenium.webdriver.common.by import By
import time
from PIL import Image

# 驗證碼獲取
def get_code():
    browser.save_screenshot('main.png')  # 把當前頁面截圖
    img = browser.find_element(By.XPATH, '/html/body/div[3]/div/div[3]/div[1]/form/div/img')  # 獲取圖片標籤
    location = img.location  # 圖片位置
    size = img.size  # 圖片寬高

    # 使用pillow扣除大圖中的驗證碼,pip install pillow
    img_tu = (
        int(location['x']) * 1.25,
        int(location['y']) * 1.25,
        int(location['x'] + size['width']) * 1.25,
        int(location['y'] + size['height']) * 1.25
    )
    # 打開當前頁面截圖
    img_main = Image.open('./main.png')
    # 摳出驗證碼圖片
    img_code = img_main.crop(img_tu)
    # 保存驗證碼圖片
    img_code.save('code.png')
    # 破解亞驗證碼
    from chaojiying import ChaojiyingClient

    chaojiying = ChaojiyingClient('zbh332525', '332525', '937257')
    # chaojiying = ChaojiyingClient('用戶名', '密碼', '軟體id')
    im = open('code.png', 'rb').read()
    code = chaojiying.PostPic(im, 1902)['pic_str']
    return code

browser = webdriver.Chrome()
browser.implicitly_wait(5)
browser.get('//www.chaojiying.com/user/login/')
browser.maximize_window()  # 瀏覽器全螢幕,免得有偏差
# 寫入用戶名、密碼、驗證碼
username = browser.find_element(By.NAME, 'user').send_keys('zbh332525')
password = browser.find_element(By.NAME, 'pass').send_keys('332525')
code = browser.find_element(By.NAME, 'imgtxt').send_keys(get_code())
time.sleep(2)
# 登錄按鈕點擊
browser.find_element(By.XPATH, '/html/body/div[3]/div/div[3]/div[1]/form/p[4]/input').click()

time.sleep(2)
browser.quit()
Tags: