爬蟲之selenium
selenium介紹
由於requests模組不能執行js,有的頁面內容,我們在瀏覽器中可以看到,但是請求下來沒有。
selenium模組:模擬操作瀏覽器,完成人的行為。
selenium本質是通過驅動瀏覽器,完全模擬瀏覽器的操作,比如跳轉、輸入、點擊、下拉等,來拿到網頁渲染之後的結果,可支援多種瀏覽器。
模組安裝:
pip install selenium
下載驅動
驅動瀏覽器需要下載相應的驅動,Google要下Google的驅動,火狐要下火狐的驅動,並且版本要與當前瀏覽器對應。
這裡我選擇Google瀏覽器,首先查看當前Google瀏覽器的版本:
版本:
下載驅動,地址:CNPM Binaries Mirror (npmmirror.com),一定要下載對應的版本驅動,比如我Google瀏覽器版本為103.0.5060.134,就要下103.0.5060.134的驅動
下載對應作業系統即可。
基本使用
導入模組:
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用途:
- 普通滑屏,打開新標籤
- 可以執行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)
異常處理
由於有時候程式操作瀏覽器過快,有些標籤會找不到等一些情況會出現報錯,這時候可以用異常處理:
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選擇器:
通過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()