目錄
XPATH語法
1 Ajax和動態HTML
如果提交表單之後,網站的頁面不需要重新刷新,那麼你訪問的網站就在用 Ajax 技術。Ajax 其實並不是一門語言,而是用來完成網絡任務(可以認爲它與網絡數據採集差不多)的一系列技術。Ajax 全稱是 Asynchronous JavaScript and XML(異步 JavaScript 和 XML),網站不需要使用單獨的頁面請求就可以和網絡服務器進行交互(收發信息)。
和 Ajax 一樣,動態 HTML(dynamic HTML,DHTML)也是一系列用於解決網絡問題的技術集合。DHTML 是用客戶端語言改變頁面的 HTML 元素。
1.1 用Selenium執行JavaScript
Selenium(http://www.seleniumhq.org/)是一個強大的網絡數據採集工具。Selenium 可以讓瀏覽器自動加載頁面,獲取需要的數據,甚至頁面截屏,或者判斷網站上某些動作是否發生。
https://pypi.org/simple/selenium/ 可以下載 Selenium 庫,可以使用 PhantomJS(http://phantomjs.org/download.html)結合在一起,就可以運行一個非常強大的網絡爬蟲。
from selenium import webdriver
import time
driver = webdriver.PhantomJS(executable_path='/phantomjs-1.9.8-macosx/bin/phantomjs')#自己程序的路徑
driver.get("http://pythonscraping.com/pages/javascript/ajaxDemo.html")
time.sleep(3)
print(driver.find_element_by_id('content').text)
driver.close()
這段代碼用 PhantomJS 庫創建了一個新的 Selenium WebDriver,首先用 WebDriver 加載頁面,然後暫停執行 3 秒鐘,再查看頁面獲取(希望已經加載完成的)內容。
1.2 Selenium 的選擇器
在這個例子中,我們用的選擇器是 find_element_by_id,下面的選擇器也可以獲取同樣的結果:
driver.find_element_by_css_selector("#content")
driver.find_element_by_tag_name("div")
選擇頁面上具有同樣特徵的多個元素,可以用 elements (換成複數)來返回一個 Python 列表:
driver.find_elements_by_css_selector("#content")
driver.find_elements_by_css_selector("div")
用 BeautifulSoup 來解析網頁內容,可以用 WebDriver 的 page_source 函數返回頁面的源代碼字符串。
pageSource = driver.page_source
bsObj = BeautifulSoup(pageSource)
print(bsObj.find(id="content").get_text())
1.3 隱式等待
隱式等待是等 DOM 中某個狀態發生後再繼續運行代碼;顯式等待明確設置了等待時間。在 Selenium庫裏面元素被觸發的期望條件(expected condition)有很多種,包括:
- 彈出一個提示框
- 一個元素被選中(比如文本框)
- 頁面的標題改變了,或者某個文字顯示在頁面上或者某個元素裏
- 一個元素在 DOM 中變成可見的,或者一個元素從 DOM 中消失了
大多數的期望條件在使用前都需要你先指定等待的目標元素。元素用定位器(locator)指定,定位器是一種抽象的查詢語言,用 By 對象表示,可以用於不同的場合,包括創建選擇器。
創建選擇器,配合 WebDriver 的 find_element 函數使用:
print(driver.find_element(By.ID, "content").text)
等同於 print(driver.find_element_by_id("content").text)
下面的程序用 id 是 loadedButton 的按鈕檢查頁面是不是已經完全加載:
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.PhantomJS(executable_path='/phantomjs-1.9.8-macosx/bin/phantomjs')
driver.get("http://pythonscraping.com/pages/javascript/ajaxDemo.html")
try:
element = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.ID, "loadedButton")))
finally:
print(driver.find_element_by_id("content").text)
driver.close()
定位器通過 By 對象進行選擇的策略:
• ID
在上面的例子裏用過;通過 HTML 的 id 屬性查找元素。
• CLASS_NAME
通過 HTML 的 class 屬性來查找元素。
• CSS_SELECTOR
通過 CSS 的 class 、 id 、 tag 屬性名來查找元素,用 #idName 、 .className 、 tagName
表示。
• LINK_TEXT
通過鏈接文字查找 HTML 的 <a> 標籤。例如,如果一個鏈接的文字是“Next”,就可以
用 (By.LINK_TEXT, "Next") 來選擇。
• PARTIAL_LINK_TEXT
與 LINK_TEXT 類似,只是通過部分鏈接文字來查找。
• NAME
通過 HTML 標籤的 name 屬性查找。這在處理 HTML 表單時非常方便。
• TAG_NAME
通過 HTML 標籤的名稱查找。
• XPATH
用 XPath 表達式(語法在下面介紹)選擇匹配的元素。
XPATH語法簡介:
XPath(XML Path,XML 路徑)是在 XML 文檔中導航和選擇元素的查詢語言。它由W3C 於 1999 年創建,在 Python、Java 和 C# 這些語言中有時會用 XPath 來處理 XML文檔。
在 XPath 語法中有四個重要概念:
- • 根節點和非根節點
♦/div 選擇 div 節點,只有當它是文檔的根節點時
♦//div 選擇文檔中所有的 div 節點(包括非根節點)
- • 通過屬性選擇節點
♦//@href 選擇帶 href 屬性的所有節點
♦//a[@href='http://google.com'] 選擇頁面中所有指向 Google 網站的鏈接
- • 通過位置選擇節點
♦//a[3] 選擇文檔中的第三個鏈接
♦//table[last()] 選擇文檔中的最後一個表
♦//a[position() < 3] 選擇文檔中的前三個鏈接
- • 星號( * )匹配任意字符或節點,可以在不同條件下使用
♦//table/tr/* 選擇所有表格行 tr 標籤的所有的子節點(這很適合選擇 th 和 td 標籤)
♦//div[@*] 選擇帶任意屬性的所有 div 標籤
1.4 處理重定向
客戶端重定向是在服務器將頁面內容發送到瀏覽器之前,由瀏覽器執行 JavaScript 完成的頁面跳轉,而不是服務器完成的跳轉。服務器端重定向一般都可以輕鬆地通過 Python 的 urllib 庫解決,不需要使用 Selenium客戶端重定向。
Selenium 可以執行 JavaScript 重定向,我們可以用一種智能的方法來檢測客戶端重定向是否完成,首先從頁面開始加載
時就“監視”DOM 中的一個元素,然後重複調用這個元素直到 Selenium 拋出一個StaleElementReferenceException 異常;也就是說,元素不在頁面的 DOM 裏了,說明這時網站已經跳轉:
from selenium import webdriver
import time
from selenium.webdriver.remote.webelement import WebElement
from selenium.common.exceptions import StaleElementReferenceException
def waitForLoad(driver):
elem = driver.find_element_by_tag_name("html")
count = 0
while True:
count += 1
if count > 20:
print("Timing out after 10 seconds and returning")
return
time.sleep(.5)
try:
elem == driver.find_element_by_tag_name("html")
except StaleElementReferenceException:
return
driver = webdriver.PhantomJS(executable_path='/phantomjs-1.9.8-macosx/bin/phantomjs')
driver.get("http://pythonscraping.com/pages/javascript/redirectDemo1.html")
waitForLoad(driver)
print(driver.page_source)