selenium介紹與使用
1 selenium介紹
什么是selenium?selenium是Python的一個第三方庫,對外提供的接口可以操作瀏覽器,然后讓瀏覽器完成自動化的操作。
selenium最初是一個自動化測試工具,而爬蟲中使用它主要是為了解決requests無法直接執行JavaScript代碼的問題 selenium本質是通過驅動瀏覽器,完全模擬瀏覽器的操作,比如跳轉、輸入、點擊、下拉 等,來拿到網頁渲染之后的結果,可支持多種瀏覽器.
2 下載安裝
下載驅動 http://npm.taobao.org/mirrors/chromedriver/2.35/
下載chromdriver.exe放到python安裝路徑的scripts目錄中即可,注意最新版本是2.38,並非2.9
pip install selenium
3 使用
from selenium import webdriver from time import sleep from selenium.webdriver.common.keys import Keys # 鍵盤上操作鍵的模塊 # 后面是你的瀏覽器驅動位置,記得前面加r'','r'是防止字符轉義的 driver = webdriver.Chrome() # 用get打開百度頁面 driver.get("http://www.baidu.com") # 查找頁面的“設置”選項,並進行點擊 driver.find_elements_by_link_text('設置')[0].click() sleep(2) # # 打開設置后找到“搜索設置”選項,設置為每頁顯示50條 driver.find_elements_by_link_text('搜索設置')[0].click() sleep(2) # 選中每頁顯示50條 m = driver.find_element_by_id('nr') sleep(2) m.find_element_by_xpath('//*[@id="nr"]/option[3]').click() m.find_element_by_xpath('.//option[3]').click() sleep(2) # 點擊保存設置 driver.find_elements_by_class_name("prefpanelgo")[0].click() sleep(2) # 處理彈出的警告頁面 確定accept() 和 取消dismiss() driver.switch_to.alert().accept() sleep(2) # 找到百度的輸入框,並輸入 美女 driver.find_element_by_id('kw').send_keys('美女') sleep(2) # 點擊搜索按鈕 driver.find_element_by_id('su').click() # (或者在輸入框輸入之后直接driver.send_keys(Keys.ENTER)表示按下回車鍵) sleep(2) # 在打開的頁面中找到“Selenium - 開源中國社區”,並打開這個頁面 driver.find_elements_by_link_text('美女_百度圖片')[0].click() sleep(3) # 關閉瀏覽器 driver.quit()
4 元素定位
find_element_by_id() find_element_by_name() find_element_by_class_name() find_element_by_tag_name() find_element_by_link_text() find_element_by_partial_link_text() find_element_by_xpath() find_element_by_css_selector() # 這些都有對應的 find_elements_by...相對應 # 注意: find_elements_by...拿到的是一個列表 find_element_by...拿到的是一個tag對象
5 節點交互
Selenium可以驅動瀏覽器來執行一些操作,也就是說可以讓瀏覽器模擬執行一些動作。比較常見的用法有:輸入文字時用send_keys()方法,清空文字時用clear()方法,點擊按鈕時用click()方法。
from selenium import webdriver import time browser = webdriver.Chrome() browser.get('https://www.taobao.com') input = browser.find_element_by_id('q') input.send_keys('MAC') # 輸入搜索詞條 time.sleep(1) input.clear() # 清除詞條 input.send_keys('IPhone') button = browser.find_element_by_class_name('btn-search') button.click() #點擊搜索按鈕
6 動作鏈
在上面的實例中,一些交互動作都是針對某個節點執行的。比如,對於輸入框,我們就調用它的輸入文字和清空文字方法;對於按鈕,就調用它的點擊方法。其實,還有另外一些操作,它們沒有特定的執行對象,比如鼠標拖曳、鍵盤按鍵等,這些動作用另一種方式來執行,那就是動作鏈。比如,現在實現一個節點的拖曳操作,將某個節點從一處拖曳到另外一處,可以這樣實現:
from selenium import webdriver from selenium.webdriver import ActionChains import time browser = webdriver.Chrome() url = 'http://www.runoob.com/try/try.php?filename=jqueryui-api-droppable' browser.get(url) browser.switch_to.frame('iframeResult') source = browser.find_element_by_css_selector('#draggable') target = browser.find_element_by_css_selector('#droppable') actions = ActionChains(browser) # actions.drag_and_drop(source, target) actions.click_and_hold(source).perform() # 點擊並且按住 time.sleep(1) actions.move_to_element(target).perform() # 移動到target位置 time.sleep(1) actions.move_by_offset(xoffset=50,yoffset=0).perform() # 從當前位置再沿x軸平移50個單位 actions.release()
7 執行js動作
對於某些操作,Selenium API並沒有提供。比如,下拉進度條,它可以直接模擬運行JavaScript,此時使用execute_script()方法即可實現,代碼如下:
from selenium import webdriver browser = webdriver.Chrome() browser.get('https://www.jd.com/') browser.execute_script('window.scrollTo(0, document.body.scrollHeight)') browser.execute_script('alert("123")')
如何判斷滾動條已經下拉到最后的位置了? 代碼如下:
from selenium import webdriver from time import sleep url = "https://movie.douban.com/typerank?type_name=%E6%81%90%E6%80%96&type=20&interval_id=100:90&action=" path = "./phantomjs-2.1.1-windows/bin/phantomjs.exe" height_list = [] # 用來放每次的拖動滾動條后頁面的最大高度 browser = webdriver.PhantomJS(executable_path=path) browser.get(url) height_list.append(browser.execute_script("return document.body.scrollHeight")) # 放第一次拖動滾動條后頁面的最大高度 while 1: browser.execute_script("scroll(0,1000000)") # 拖動滾動條, 取值范圍足夠大 sleep(1) check_height = browser.execute_script("return document.body.scrollHeight") # 獲取每次拖動的文檔高度 if check_height == height_list[-1]: # 判斷拖動滾動條后的最大高度與上一次的最大高度的大小,相等表明到了最底部 break height_list.append(check_height) print(height_list) # [1000, 2480, 4391, 6301, 8211, 10121, 12031, 13941, 15851, 17761, 19098]
8 延時等待
在Selenium中,get()方法會在網頁框架加載結束后結束執行,此時如果獲取page_source,可能並不是瀏覽器完全加載完成的頁面,如果某些頁面有額外的Ajax請求,我們在網頁源代碼中也不一定能成功獲取到。所以,這里需要延時等待一定時間,確保節點已經加載出來。這里等待的方式有兩種:一種是隱式等待,一種是顯式等待。
隱式等待:
當使用隱式等待執行測試的時候,如果Selenium沒有在DOM中找到節點,將繼續等待,超出設定時間后,則拋出找不到節點的異常。換句話說,當查找節點而節點並沒有立即出現的時候,隱式等待將等待一段時間再查找DOM(在這段時間之內不會查找, n秒之后再進行查找),默認的時間是0。示例如下:
from selenium import webdriver from selenium.webdriver import ActionChains from selenium.webdriver.common.by import By #按照什么方式查找,By.ID,By.CSS_SELECTOR from selenium.webdriver.common.keys import Keys #鍵盤按鍵操作 from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.wait import WebDriverWait #等待頁面加載某些元素 browser=webdriver.Chrome() #隱式等待:在查找所有元素時,如果尚未被加載,則等10秒, 10秒之后再一次查找 browser.implicitly_wait(10) browser.get('https://www.baidu.com') input_tag=browser.find_element_by_id('kw') input_tag.send_keys('美女') input_tag.send_keys(Keys.ENTER) contents=browser.find_element_by_id('content_left') #沒有等待環節而直接查找,找不到則會報錯 print(contents) browser.close()
顯示等待:
隱式等待的效果其實並沒有那么好,因為我們只規定了一個固定時間,而頁面的加載時間會受到網絡條件的影響。這里還有一種更合適的顯式等待方法,它指定要查找的節點,然后指定一個最長等待時間。如果在規定時間內加載出來了這個節點,就返回查找的節點;如果到了規定時間依然沒有加載出該節點,則拋出超時異常。
from selenium import webdriver from selenium.webdriver import ActionChains from selenium.webdriver.common.by import By #按照什么方式查找,By.ID,By.CSS_SELECTOR from selenium.webdriver.common.keys import Keys #鍵盤按鍵操作 from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.wait import WebDriverWait #等待頁面加載某些元素 browser=webdriver.Chrome() browser.get('https://www.baidu.com') input_tag=browser.find_element_by_id('kw') input_tag.send_keys('美女') input_tag.send_keys(Keys.ENTER) #顯式等待:顯式地等待某個元素被加載 wait=WebDriverWait(browser,10) wait.until(EC.presence_of_element_located((By.ID,'content_left'))) contents=browser.find_element(By.CSS_SELECTOR,'#content_left') print(contents) browser.close()
9 獲取tag節點的信息
通過page_source屬性可以獲取網頁的源代碼,接着就可以使用解析庫(如正則表達式、Beautiful Soup、pyquery等)來提取信息了。
不過,既然Selenium已經提供了選擇節點的方法,返回的是WebElement類型,那么它也有相關的方法和屬性來直接提取節點信息,如屬性、文本等。這樣的話,我們就可以不用通過解析源代碼來提取信息了,非常方便。
from selenium import webdriver from selenium.webdriver.common.by import By #按照什么方式查找,By.ID,By.CSS_SELECTOR from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.wait import WebDriverWait #等待頁面加載某些元素 browser=webdriver.Chrome() browser.get('https://www.amazon.cn/') wait=WebDriverWait(browser,10) wait.until(EC.presence_of_element_located((By.ID,'cc-lm-tcgShowImgContainer'))) tag=browser.find_element(By.CSS_SELECTOR,'#cc-lm-tcgShowImgContainer img') #獲取標簽屬性, print(tag.get_attribute('src')) #獲取標簽ID,位置,名稱,大小(了解) print(tag.id) print(tag.location) # {'x': 0, 'y': 133} print(tag.tag_name) print(tag.size) # {'height': 1453, 'width': 661} browser.close()
phantomJs介紹與使用
1 PhantomJS介紹
PhantomJS是一款無界面的瀏覽器,其自動化操作流程和上述操作谷歌瀏覽器是一致的。由於是無界面的,為了能夠展示自動化操作流程,PhantomJS為用戶提供了一個截屏的功能,使用save_screenshot("xxx.png")函數實現。
重點:selenium+phantomjs 就是爬蟲終極解決方案:有些網站上的內容信息是通過動態加載js形成的,所以使用普通爬蟲程序無法回去動態加載的js內容。例如豆瓣電影中的電影信息是通過下拉操作動態加載更多的電影信息。
from selenium import webdriver import time """ phantomjs, 完完全全模擬瀏覽器訪問url, 期間沒有可視化界面 """ time.sleep(1) bro = webdriver.PhantomJS(executable_path="./phantomjs-2.1.1-windows/bin/phantomjs.exe") bro.get("https://www.baidu.com/") time.sleep(2) text_input = bro.find_element_by_id("kw") value_btn = bro.find_element_by_id("su") text_input.send_keys("糞叉子") value_btn.click() time.sleep(2) js = "window.scrollTo(0,document.body.scrollHeight)" bro.execute_script(js) time.sleep(2) page_text = bro.page_source print(page_text) bro.quit() # 同 bro.close()
2 Google無頭瀏覽器
由於PhantomJs最近已經停止了更新和維護,所以推薦大家可以使用谷歌的無頭瀏覽器,是一款無界面的谷歌瀏覽器。
from selenium import webdriver import time from selenium.webdriver.chrome.options import Options """ 使用無頭瀏覽器的基本配置 """ chorme_options = Options() chorme_options.add_argument("--headless") chorme_options.add_argument("--disable-gpu") time.sleep(1) bro = webdriver.Chrome(executable_path="./chromedriver.exe", chrome_options=chorme_options) bro.get("https://www.baidu.com/") time.sleep(1) text_input = bro.find_element_by_id("kw") value_btn = bro.find_element_by_id("su") text_input.send_keys("瑪莎拉斯") value_btn.click() time.sleep(1) js = "window.scrollTo(0,document.body.scrollHeight)" bro.execute_script(js) time.sleep(1) page_text = bro.page_source print(page_text) bro.quit()
圖片的懶加載技術
1 圖片懶加載概念:
圖片懶加載是一種網頁優化技術。圖片作為一種網絡資源,在被請求時也與普通靜態資源一樣,將占用網絡資源,而一次性將整個頁面的所有圖片加載完,將大大增加頁面的首屏加載時間。為了解決這種問題,通過前后端配合,使圖片僅在瀏覽器當前視窗內出現時才加載該圖片,達到減少首屏圖片請求數的技術就被稱為“圖片懶加載”。
2 網站一般如何實現圖片懶加載技術呢?
在網頁源碼中,在img標簽中首先會使用一個“偽屬性”(通常使用src2,original......)去存放真正的圖片鏈接而並非是直接存放在src屬性中。當圖片出現到頁面的可視化區域中,會動態將偽屬性替換成src屬性,完成圖片的加載。
3 站長素材案例后續分析:通過細致觀察頁面的結構后發現,網頁中圖片的鏈接是存儲在了src2這個偽屬性中,代碼如下:
from bs4 import BeautifulSoup import requests # 爬取站長回家圖片鏈接 http://sc.chinaz.com/tupian/meinvtupian.html url = "http://sc.chinaz.com/tupian/meinvtupian.html" headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.109 Safari/537.36" } page = requests.get(url,headers=headers) page.encoding = "utf8" soup = BeautifulSoup(page.text,"lxml") div_box = soup.find_all("div",id="container") for pic_box in div_box: src = pic_box.find_all("img") for i in src: print(i.attrs["alt"]) print(i.attrs["src"]) # 沒有src屬性, 直接報錯 print(i.attrs["src2"]) # src2偽屬性