特別聲明:
- 供交流學習使用,不得用作商業用途。
- 如有違規侵權,請聯系刪除。
一、Selenium框架介紹
Selenium 是一個用于瀏覽器自動化測試的框架,可以用來爬取任何網頁上看到的數據。
簡單地說,就是模擬一個人所有的網頁操作的行為
Selenium的下載與安裝:
- 安裝:在終端輸入 pip install selenium
- 下載:下載Chromedriver,解壓后放在…\Google\Chrome\Application\(右擊Chrome圖標,打開文件所在文件夾)
- 環境變量:將該目錄添加至環境變量:右鍵點擊我的電腦----->屬性--->高級系統設置---->環境變量------>在path路徑下添加上文中…\Google\Chrome\Application\路徑
使用代碼測試:
Selenium的簡單使用:
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
driver = webdriver.Chrome()
driver.get('http://www.baidu.com')
elem = driver.find_element_by_xpath('//*[@id = "kw"]') #查找輸入框
elem.send_keys('Python Selenium',Keys.ENTER) #模擬點擊回車
print(driver.page_source)
- 遇到的問題:報錯顯示缺少參數"Python3 Selenium自動化測試賦值出現:WebDriverException: Message: unknown error: call function result missing 'value'",發現的Chromedriver版本要跟當前Chrome版本一致才可以
Selenium的優缺點:
- 優點:Selenium可以爬取任何網頁的任何內容,因為它是通過瀏覽器訪問的方式進行數據的爬取,沒有網站會拒絕瀏覽器的訪問。 但是淘寶和知乎會對Selenium有反爬機制,需要進行偽裝。
- 缺點:時間以及內存消耗太大,可以開啟無頭模式headless或者PhantomJS webdriver緩解這個問題
二、Selenium的使用
跟人操作網頁一樣,都是需要查找和操作網頁元素
查找元素
XXX表示用CSS、id、name、identifier、XPath定位、 超鏈接、DOM、 CSS selector進行定位。更多定位方式
PS: 對于網頁的定位路徑,只需要在谷歌瀏覽器中,鼠標放到對應元素--右鍵--檢查--在右邊的網頁源碼相應位置--右鍵--復制--選擇相應的路徑定位方法
- 1.直接用webdriver對象的查找
driver.find_element_by_XXX()
查找符合條件的單個元素
driver.find_elements_by_XXX()
查找符合條件的一組元素
此方法通常需要前面編寫time.sleep()一定秒數,讓網頁加載完畢,否則可能會找不到元素
-
WebDriverWait對象(更推薦)
此方法可以顯式等待網頁加載
顯式等待可以自定義等待的條件,用于更加復雜的頁面等待條件
-
WebDriverWait對象(更推薦)
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 = webdriver.Chrome()
driver.get('填入網站的url')
wait = WebDriverWait(driver, 10) 構建WebDriverWait對象,10秒加載時間
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, 'CSS選擇器定位路徑'))) # 很多有經驗的selenium用戶推薦CSS定位方式,因為它比XPath更快。而且可以在HTML文件中找到更復雜的對象。
- 使用pyquery庫
類似JQuery
定位元素
此方法適合用作返回網頁元素進行下一步處理
- 使用pyquery庫
from selenium import webdriver
from pyquery import PyQuery as pq
driver = webdriver.Chrome()
driver.get('填入網站的url')
html = driver.page_source()
doc = pq(html)
#pyquery(browser.page_source)就相當于requests.get獲取的內容
items = doc('CSS選擇器路徑').items()
操作元素
相應的,我們有:
- 直接用webdriver對象的操作
from selenium import webdriver
account = browser.find_element_by_xpath('//*[@id="TPL_username_1"]')
account.clear()
account.send_keys("用戶名")
passwd = browser.find_element_by_xpath('//*[@id="TPL_password_1"]')
passwd.send_keys("密碼")
submit = browser.find_element_by_xpath('//*[@id="J_SubmitStatic"]')
submit.click() #點擊登錄按鈕
-
WebDriverWait對象的操作(更推薦)
也是可以顯示等待加載時間
-
WebDriverWait對象的操作(更推薦)
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
input = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.form > input')))
submit = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.form > span.btn.J_Submit')))
input.clear() # 清除輸入框內容
input.send_keys(page_num) # 輸入輸入框內容
submit.click() # 點擊確定按鈕
三、Selenium項目實戰(爬取淘寶商品)
思路:
#! /usr/bin/env python
# -*- coding:utf-8 -*-
# Use for get infomation from taobao by ChromeDriver
# Author:Robin; Created in 20190831
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 selenium.common.exceptions import TimeoutException #檢查網絡請求是否超時
#from selenium.webdriver import ChromeOptions
#from selenium.webdriver.common.keys import Keys
#from selenium.webdriver import ActionChains
from pyquery import PyQuery as pq
from pymongo import MongoClient
#import random
import time
import re
browser = webdriver.Chrome() #創建webdriver對象
wait = WebDriverWait(browser, 10)
#進入淘寶網,輸入商品名稱,返回頁面
def search(good):
try:
browser.get('https://www.taobao.com/')
input = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#q'))) # 搜索輸入框
submit = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#J_TSearchForm > div.search-button > button'))) # 搜索點擊按
keys = '{}'.format(good)
input.send_keys(keys) #輸入商品名
submit.click() #點擊搜索
total = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.total')))
get_products(good)
return total.text #總頁數
except TimeoutException:
#login(browser)
search(good)
#跳轉到下一頁
def next_page(page_num, good):
print('正在爬取第{}頁'.format(page_num))
try:
input = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.form > input'))) #頁碼輸入框
submit = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > div.form > span.btn.J_Submit'))) #頁碼跳轉確定按鈕
input.clear() #清空頁碼框
input.send_keys(page_num) #輸入頁碼
submit.click() # 點擊確定
time.sleep(10) #防止爬取過快
wait.until(EC.text_to_be_present_in_element((By.CSS_SELECTOR, '#mainsrp-pager > div > div > div > ul > li.item.active > span'), str(page_num))) #檢查是否跳轉到正確頁數
get_products(good)
except TimeoutException:
next_page(page_num, good)
#得到淘寶商品信息
def get_products(good):
wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-itemlist .items .item')))
html = browser.page_source
doc = pq(html) # pyquery(browser.page_source)就相當于requests.get獲取的內容, 構建PyQuery對象
items = doc('#mainsrp-itemlist .items .item').items() #獲取所有的‘#mainsrp-itemlist .items .item’選擇出來的內容下的各項
for item in items: # 每個item變量都是一個PyQuery對象,然后再調用它的find()方法,傳入CSS選擇器,就可以獲取單個商品的特定內容了。
products = {
'image': item.find('.pic .img').attr('src'), #獲取圖片鏈接
'price': item.find('.price').text(),
'deal': item.find('.deal-cnt').text()[:-3],
'title': item.find('.title').text(),
'shop': item.find('.shop').text(),
'location': item.find('.location').text(),
}
#print(products)
save_to_mongo(products, good)
# 保存數據到MongoDB
def save_to_mongo(result, good):
client = MongoClient('mongodb://localhost:27017')
db = client.taobao
set_name = '{}'.format(good)
goods_set = db[set_name] # 創建以商品名命名的數據集
try:
if goods_set.insert(result): # 插入到MongoDB數據庫相應數據集
print("存儲到MONGODB成功",result)
except Exception:
print("存儲到MONGODB失敗",result)
def main(good):
print('正在爬取第1頁...')
total = search(good)
total = int(re.compile('(\d+)').search(total).group(1))
for i in range(2, total+1):
next_page(i, good)
browser.close() #把瀏覽器關掉
if __name__ == '__main__':
main('樹莓派')
運行結果:
問題
- 淘寶模擬登錄:
- 一開始我以為淘寶點擊搜索商品是都會自動跳轉到登錄頁面的,然后就想用Selenium模擬人的登錄,然后再利用selenium.webdriver的ActionChains的click_and_hold(slider).perform() 和 click_and_hold(slider).perform() 方法,模擬人操作時候先快后慢的特點
- 后面才發現淘寶識別的是瀏覽器發送的一些參數判定是Selenium還是普通瀏覽器
- 但是感覺這個代碼挺有意思的,或許以后也有用,也拿出來分享下
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 selenium.common.exceptions import TimeoutException #檢查網絡請求是否超時
from selenium.webdriver import ChromeOptions
from selenium.webdriver.common.keys import Keys
from selenium.webdriver import ActionChains
from pyquery import PyQuery as pq
from pymongo import MongoClient
import random
import time
import re
##選用開發者模式,創建一個瀏覽器對象,可避免被淘寶檢測到是selenium模擬瀏覽器
option = ChromeOptions()
option.add_argument('--proxy-serve=127.0.0.1:8080')
option.add_experimental_option('excludeSwitches',['enable-automation'])
#chrome_options.add_experimental_option('excludeSwitches', ['enable-logging'])#禁止打印日志 跟上面只能選一個
browser = webdriver.Chrome(options=option)
browser = webdriver.Chrome()
wait = WebDriverWait(browser, 10)
# 滑塊移動軌跡計算:使得滑塊先快后慢
def get_track(distance):
track = []
current = 0
mid = distance * (1/2)
t = 2
v = 0
a = 4
a1 = -4
while current < distance:
#滑條前1/2的部分加速滑動
if current < mid:
move = v * t + 1/2 * a *t * t #物理上勻加速運動計算某一時間內的位移: s = v*t + (1/2)a*t^2
current += move
track.append(round(move))
v = v + a*t # 加速運動物體某一時刻的瞬時速度
else:
#滑條后1/2的部分加速度減慢
move = v * t + 1/2 * a1 *t *t
current += move
track.append(round(move))
v = v + a1*t
return track
def drag(length, xpath):
if browser.find_element_by_xpath("{}".format(xpath)):
slider = browser.find_element_by_xpath("{}".format(xpath)) #找到滑動按鈕
track = get_track(length) #模擬運動軌跡,速度先快后慢
ActionChains(browser).click_and_hold(slider).perform() # 按住滑塊滑動
for x in track:
ActionChains(browser).drag_and_drop_by_offset(slider, xoffset=x, yoffset=random.randint(1,3)).perform()
ActionChains(browser).release().perform()
else:
pass
# Selenium控制的情況下搜索商品,淘寶會自動跳轉到登錄界面
def login(browser):
try:
button = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#J_Quick2Static')))
button.click()
account = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#TPL_username_1')))
passwd = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#TPL_password_1')))
submit = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '#J_SubmitStatic')))
account.clear()
account.send_keys('用戶名')
passwd.send_keys('密碼')
submit.click()
try:
change_login = browser.find_element_by_id('J_Quick2Static') #點擊密碼登錄按鈕,選擇用密碼方式登錄
time.sleep(3)
change_login.click()
except Exception:
pass
time.sleep(3)
account = browser.find_element_by_xpath('//*[@id="TPL_username_1"]')
account.clear()
account.send_keys('用戶名’)
passwd = browser.find_element_by_xpath('//*[@id="TPL_password_1"]')
passwd.send_keys('密碼')
try:
drag(700, "http://*[@id='nc_1_n1z']")
except Exception:
pass
submit = browser.find_element_by_xpath('//*[@id="J_SubmitStatic"]')
submit.click() #點擊登錄按鈕
try:
index_page = wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '//*[@id="J_SiteNavHome"]/div/a/span')))
index_page.click()
except Exception:
login(browser)
except TimeoutException:
login(browser)
if __name__ == '__main__':
login(browser)
- 有時候用正則表達式匹配淘寶的頁碼,total = int(re.compile('(\d+)').search(total).group(1))會匹配失敗,不知道究竟是加載不完全還是淘寶的一些反爬措施,有空再研究下
- 當爬取到一定頁數的時候,淘寶會檢查到頁面的異常,然后就會卡在驗證頁面,出現手動也無法通過驗證的情況,所以應該要多準備幾個代理和IP,使用scrapy框架來爬取會更加方便。
改進
- 由于淘寶對Selenium的反爬做得比較好,所以登錄需要手動掃碼登錄,如果需要模擬登錄請參考以下鏈接:
- 如果需要更新MongoDB數據庫,請參考:
- MongoDB 更新文檔
- Mongodb數據更新命令(update、save)
- python 更新 MongoDB
- python與mongodb的交互 增刪改差
- 賬號和密碼可以存儲到json或者pickle打包的配置文件里面,需要時回調,方便代碼維護和信息安全
更多Selenium技巧擴展閱讀:
可以用于掩飾Selenium,防止被識別出
設置headless和關閉圖片緩存,可以減少機器負荷,提高性能
- chrome配置
設置無頭瀏覽器PhantomJS和關閉圖片緩存
- Windows下PhantomJS的安裝和使用
- Python3網絡爬蟲開發實戰 1.2.5-PhantomJS的安裝
- Python爬蟲利器四之PhantomJS的用法
Selenium更多定位用法:
- 菜鳥學自動化測試(五)-----selenium命令之定位頁面元素
更多CSS選擇器用法:(PS:這又是一個大坑)
- 【Selenium專題】元素定位之CssSelector
- Selectors Level 3官方文檔
其他參考教程:
- 網絡爬蟲(python項目)
- 爬蟲實踐---Selenium-抓取淘寶搜索商品信息
- (九)使用Selenium+Chrome/PhantomJS(模擬瀏覽器)抓取淘寶商品美食信息|Python3網絡爬蟲開發實戰