python爬取js动态网页_Python 从零开始爬虫(八)——动态爬取解决方案 之 selenium
selenium——自動化測試工具,專門為Web應用程序編寫的一個驗收測試工具,測試其兼容性,功能什么的。然而讓蟲師們垂涎的并不是以上的種種,而是其通過驅動瀏覽器獲得的解析JavaScript的能力。所以說這貨在動態爬取方面簡直是掛逼級別的存在,相較于手動分析更簡單易用,節省分析打碼時間。
雖然selenium因其“超能力”被不少人吹上天了,但是認清利弊,根據需求來選擇爬蟲工具,還是挺重要的,所以這里簡單說下以供參考:
selenium無腦解決動態難題
selenium更耐網頁變動
selenium極大提升開發效率,但極大降低爬取效率(規模一大就明顯了)。
selenium更占用資源(cpu,內存,網絡)
理由:打開一個/多個瀏覽器,瀏覽器里還有n個窗口.....
下面不多說,進入正題
前置
本體直接pip安裝pip install selenium
驅動按需選擇:
chrome (內核版本要對應,詳看其中的notes.txt)
firefox (相信各位的英文實力)
IE (很少用吧)
下載好后把解壓得到的驅動放到設置了環境變量的路徑下就行了,如果你的python設置了環境變量,應該丟到python目錄下就行了。
簡單嘗試
照例貼上文檔,2.53舊版中文,想學更多就給我啃
選好瀏覽器就創建實例,之后的操作都在這個實例之上,并介紹一些有用的option,本文以Chrome為例。
from selenium import webdriver
#其他瀏覽器把Chrome換名就行
#option = webdriver.ChromeOptions()
#option.set_headless() 設置無頭瀏覽器,就是隱藏界面后臺運行
driver = webdriver.Chrome() #創建driver實例
#driver = webdriver.Chrome(chrome_options=option) 創建實例并載入option
url = '**********'
driver.get(url)
#driver.maximize_window() 最大化窗口
#driver.set_window_size(width,height) 設置窗口大小
print(driver.page_source) #打印網頁源碼
driver.quit() # 關閉瀏覽器
對付低級的動態網頁(ajax較少)就是這么簡單,基本不用考慮動態問題,徒手擼動態的人表示羨慕。然而更多時候并不像這樣簡單,直接套用的話很容易導致源碼所見非所得,而且很多爬蟲并沒有簡單到拿個源碼就走的程度,它還要交互,模擬點擊滾動等等。所以還請往下看,不要看到這就投入實戰啦
定位
selenium提供多種方法對元素進行定位,返回WebElement對象,而上面提到的driver就相當于最大的WebElement對象
#以下都是單次定位,返回第一個定位到的。如果想多次定位,給element加個s就行,返回的是符合元素的列表
element = driver.find_element_by_id() # 通過標簽的id定位,接收id屬性值
driver.find_element_by_name() # 通過標簽的name定位,接收name屬性值
driver.find_element_by_xpath() # 通過xpath定位,接收xpath表達式
driver.find_element_by_link_text() # 通過標簽的完全文本定位,接收完整的文本
driver.find_element_by_partial_link_text() # 通過標簽的部分文本定位,接收部分文本
driver.find_element_by_tag_name() # 通過標簽名定位,接收標簽名
driver.find_element_by_class_name() # 通過標簽的class定位,接收class屬性值
driver.find_element_by_css_selector() # 通過css選擇器定位,接收其語法
#返回的WebElement對象可以繼續往下定位
xpath和css就不展開講了,能實現精確定位,一定要學其中一個,不知道的小伙伴們上網自學吧,不難
除了上面這些公有的方法,還有2個私有的方法來幫助頁面對象的定位。 這兩個方法就是 find_element 和 find_elements,需要導入By類輔助,接收一個By類屬性及其對應語法/值
from selenium.webdriver.common.by import By
driver.find_element(By.XPATH,'//a[text()="dark"]')
driver.find_elements(By.XPATH,'//a[text()="dark"]')
'''By類有以下屬性
ID = "id"
XPATH = "xpath"
LINK_TEXT = "link text"
PARTIAL_LINK_TEXT = "partial link text"
NAME = "name"
TAG_NAME = "tag_name"
CLASS_NAME = "class name"
CSS_SELECTOR = "css selector"
'''
frame切換
很多人在用selenium會遇到所見非所得,或者定位頁面元素的時候會定位不到的問題,這種情況很有可能是frame在搞鬼,必須切換到相應frame中再進行定位。如果遇到以上問題,第一時間F12看下你所要的信息是否在frame標簽里面。
frame標簽有frameset、frame、iframe三種,frameset跟其他普通標簽沒有區別,不會影響到正常的定位,而frame與iframe對selenium定位而言是一樣的。
selenium提供了4種方法定位iframe并切換進去:
#對于.....
driver.switch_to_frame(0) # 用frame的index來定位,第一個是0,以此類推
driver.switch_to_frame("frame1") # 用id來定位
driver.switch_to_frame("dark") # 用name來定位
driver.switch_to_frame(driver.find_element_by_tag_name("iframe")) # 用WebElement對象來定位
通常通過id和name就能實現,無此屬性時可以通過WebElement對象,即用find_element系列方法所取得的對象來定位。如果你確定每個目標frame都是固定第幾個,那也可以用index定位
切到frame中之后,就不能繼續操作主文檔的元素了,這時如果想操作主文檔內容,則需切回主文檔。
driver.switch_to_default_content()
嵌套frame的切換
如果frame里包著frame而你要的frame是后者,那么需要一層一層切換進去,切換方法四選一
driver.switch_to_frame("frame1")
driver.switch_to_frame("frame2")
如果想回去上一個父frame,用driver.switch_to.parent_frame()
window 切換
有時候點開一個鏈接就會彈出一個新窗口,如果要對其操作就要切換過去,方法和frame的切換差不多,但只接收window_handle(相當于窗口的名字)來進行切換。driver.switch_to_window("windowName")
切換前最好保存之前的handle和所有的handles以便于來回切換。
current_window = driver.current_window_handle # 獲取當前窗口handle name
all_windows = driver.window_handles # 獲取所有窗口handle name
# 如果window不是當前window,則切換到該window,即切換到新窗口
for window in all_windows:
if window != current_window:
driver.switch_to_window(window)
#....
driver.switch_to_window(current_window) # 返回之前的窗口
driver.close() # 窗口的關閉用close()
頁面交互
常用的頁面交互有點擊,輸入文本等。交互的原則是先定位,后交互,比如你F12找到了某個文本框或某個可點擊項的標簽,那就先定位到那,再用以下的交互方法
from selenium.webdriver.common.keys import Keys
# 首先定位到某個文本框或某個可點擊項(如超鏈接,按鈕),
element = driver.find_element_by_xpath('//a[text()="dark"]')
element.send_keys("some text") # 往文本框輸入文本
#element.send_keys("some text", Keys.ARROW_DOWN) 可以使用 Keys 類來模擬輸入方向鍵
element.clear() # 清空文本框
#element.click() 如果此元素可點擊,可用click()方法
要注意的一點是,不是定位到就必定能交互,有時候目標會被網頁彈出來的東西覆蓋,導致無法交互,所以要確保頁面干凈無覆蓋
上下拉滾動
selenium可以執行js,下拉滾動可以通過此實現,因此就算不懂js也可以記一些有用的js代碼
#driver.execute_script('js_str')
driver.execute_script('window.scrollTo(0,10000)') # 移動到指定坐標
driver.execute_script('window.scrollBy(0,10000)') # 相對當前坐標移動
driver.execute_script('window.scrollTo(0, document.body.scrollHeight)') # 相對當前坐標移動
等待
現在不少web都都有使用ajax技術,即異步加載,有時候你要的東西都還沒加載到你就去定位了,就會拋出異常。為了避免此等血淋淋的慘狀,必須要待其加載一段時間后再進行后面的操作。這里付一張不等待就獲取page_source的后果
最粗暴的方法就是使用time.sleep(),這很笨,因為你還要設置合適的時間,而不同網站加載的速度有異,容易造成時間的浪費。所以最好還是使用selenium提供的兩種等待方法
顯式Wiats
顯式Wiats允許你設置一個加載時間的上限和一個條件,每隔0.5s就判斷一下所設條件,條件成立就繼續執行下面的代碼,如果過了時間上限還是沒有成立,默認拋出NoSuchElementException 異常。這種相對智能的等待方法能最大化地節省時間,應該優先選擇使用
selenium提供了多種expected_conditions(EC)來設置條件,但是要注意有定位時EC的方法接收的是定位信息的元組(locator_tuple)而不是兩個參數,正確用法如EC.presence_of_element_located((By.ID,"dark"))
這個條件檢測是否有id='dark'的元素
selenium提供的EC有以下方法,意思如其名,這里注釋一些易搞錯的,選擇使用
title_is(str)
title_contains(str)
presence_of_element_located(locator_tuple)
visibility_of_element_located(locator_tuple) # 判斷某個元素是否可見
visibility_of(WebElement) # 同上,傳參不同
presence_of_all_elements_located(locator_tuple)
text_to_be_present_in_element(str) # 判斷某個元素中的text是否包含了指定字符串
text_to_be_present_in_element_value(str) # 判斷某個元素中的value屬性是否包含了預期的字符串
frame_to_be_available_and_switch_to_it
invisibility_of_element_located(locator_tuple)
element_to_be_clickable(locator_tuple)
staleness_of(WebElement) # 等待某個元素從的移除
element_to_be_selected(WebElement)
element_located_to_be_selected(locator_tuple)
element_selection_state_to_be(WebElement)
element_located_selection_state_to_be(locator_tuple)
alert_is_present() # 判斷頁面上是否存在alert
使用上通常搭配try語句
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait # 導入顯式等待
from selenium.webdriver.support import expected_conditions as EC # 導入EC
from selenium.common.exceptions import NoSuchElementException
# WebDriverWait(driver,time).until(EC) 顯式waits
try:
element = WebDriverWait(driver,10).until(EC.presence_of_element_located((By.ID,"myDynamicElement")))
except NoSuchElementException:
#.....
finally:
driver.quit()
隱式等待
隱式等待是在嘗試定位某個元素的時候,如果沒能成功,就等待固定長度的時間,默認0秒。一旦設置了隱式等待時間,它的作用范圍就是Webdriver對象實例的整個生命周期。driver.implicitly_wait(10)
最后補充
這是用selenium幾分鐘弄出來的網易云音樂單曲評論爬蟲,而且還模擬了評論翻頁,還截了圖。沒提取信息,提取的時候可以用BeautifulSoup或正則提取,
from selenium import webdriver
import time
from selenium.common.exceptions import NoSuchElementException, TimeoutException
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
driver = webdriver.Chrome()
driver.maximize_window()
driver.get("http://music.163.com/#/song?id=31877470")
driver.switch_to_frame("contentFrame")
time.sleep(5)
driver.execute_script('window.scrollTo(0, document.body.scrollHeight)')
driver.save_screenshot('E:/python3/gg.png') # 截圖
b = driver.find_element_by_xpath("//a[starts-with(@class,'zbtn znxt')]")
b.click()
try:
WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH,"//a[@data-type='reply']")))
print(driver.page_source)
except NoSuchElementException:
print('OMG')
finally:
driver.quit()
短短二十行就能做到這種程度(而且不少還是import的),足以見得selenium強大。如果要手動分析,你還要分析js加密算法,寫上n倍的代碼。兩者相比起來selenium真的很吸引人,簡直是懶人救星。大家權衡利弊按需選擇吧
總結
以上是生活随笔為你收集整理的python爬取js动态网页_Python 从零开始爬虫(八)——动态爬取解决方案 之 selenium的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 速领,我给大家做了程序员专属红包封面~
- 下一篇: python管理系统web版_【程序源代