4.3 通過selenium 模擬瀏覽器抓取
4.3 通過selenium 模擬瀏覽器抓取
在上述的例子中,使用Chrome「檢查」功能找到源地址還十分容易。但是有一些網站非常複雜,例如前面的天貓產品評論,使用「檢查」功能很難找到調用的網頁地址。除此之外,有一些數據真實地址的URL也十分冗長和複雜,有些網站為了規避這些抓取會對地址進行加密,造成其中的一些變數讓人摸不著頭腦。
因此,這裡介紹第二種方法,使用瀏覽器渲染引擎。直接用瀏覽器在顯示網頁時解析HTML,應用CSS樣式並執行JavaScript的語句。
這方法在爬蟲過程中會打開一個瀏覽器,載入該網頁,自動操作瀏覽器瀏覽各個網頁,順便把數據抓下來。用一句簡單而通俗的話說,使用瀏覽器渲染方法,爬取動態網頁變成了爬取靜態網頁。
我們可以用Python的selenium庫模擬瀏覽器完成抓取。Selenium是一個用於Web應用程序測試的工具。Selenium測試直接運行在瀏覽器中,瀏覽器自動按照腳本代碼做出點擊,輸入,打開,驗證等操作,就像真正的用戶在操作一樣。
4.3.1 selenium 的安裝與基本介紹
selenium的安裝非常簡單,和其他的Python 庫一樣,我們可以用pip 安裝。
pip install seleniumn
selenium的腳本可以控制瀏覽器進行操作,可以實現多個瀏覽器的調用,包括IE(7, 8, 9, 10, 11),Firefox,Safari,Google Chrome,Opera等。最常用的是 Firefox,因此下面的講解也以Firefox為例,在執行之前你需要安裝Firefox瀏覽器。
首先,我們打開使用 selenium 打開瀏覽器和一個網頁,以下是代碼:
from selenium import webdriverndriver = webdriver.Firefox()ndriver.get("http://www.santostang.com/2017/03/02/hello-world/")n
運行之後,發現程序報錯,錯誤為:
selenium.common.exceptions.WebDriverException:Message: geckodriver executable needs to be in PATH.
在selenium之前的版本中,這樣做是不會報錯的,但是selenium的新版卻無法正常運行。於是,我們需要從網上下載一個geckodriver,並放在環境變數的PATH中。如果是Windows的話,可以去https://github.com/mozilla/geckodriver/releases下載最新版的geckodriver,下載是一個zip文件,可以解壓後放在Anaconda的安裝地址中,例如「C:ProgramDataAnaconda3Scripts」。然後在環境變數的PATH中,我們加入這個geckodriver的地址。除了下載geckodriver,我們還需要在代碼中指定Firefox程序的地址,因此,最後的代碼如下:
from selenium import webdrivernfrom selenium.webdriver.firefox.firefox_binary import FirefoxBinarynnncaps = webdriver.DesiredCapabilities().FIREFOXncaps["marionette"] = Truenbinary = FirefoxBinary(rD:Program FilesMozilla Firefoxfirefox.exe)n#把上述地址改成你電腦中Firefox程序的地址ndriver = webdriver.Firefox(firefox_binary=binary, capabilities=caps)ndriver.get("http://www.santostang.com/2017/03/02/hello-world/")n
運行後,代碼會打開「Hello World」這篇文章的頁面。如下圖所示:
4.3.2 selenium的實戰案例
為了演示Selenium是怎麼工作的,我們這裡把前面用Chrome「檢查」方法獲取網頁真實地址,然後爬取博客文章評論的網頁爬蟲方法,寫成Selenium的版本。
由於Selenium使用瀏覽器渲染,因此,那些評論數據已經渲染到了HTML代碼中。我們可以使用Chrome「檢查」方法,定位到元素位置。
步驟一:找到評論的HTML代碼標籤。使用Chrome打開該文章頁面,右鍵點擊頁面,打開「檢查」選項。按照第二章的方法,定位到評論數據。如下圖所示:可以看到該數據的標籤為<div class="reply-content"><p> 第21條測試評論 </p> </div>。
步驟二:嘗試獲取一條評論數據。在原來打開頁面的代碼數據上,我們可以使用以下代碼,獲取第一條評論數據。在下面代碼中,driver.find_element_by_css_selector是用CSS選擇器查找元素,找到class為』reply-content』的div元素;find_element_by_tag_name則是通過元素的tag去尋找,意思是找到comment中的p元素。最後,再輸出p元素中的text文本。
comment = driver.find_element_by_css_selector(div.reply-content)ncontent = comment.find_element_by_tag_name(p)nprint (content.text)n
運行上述代碼,我們得到的結果是錯誤:「Message: Unable to locate element:
div.reply-content」。這究竟是為什麼呢?步驟二:我們可以在 jupyter 中鍵入driver.page_source
找到為什麼沒有定位到評論元素,通過排查我們發現,原來代碼中的 JavaScript 解析成了一個 iframe,<iframe title="livere" scrolling="no"…>也就是說,所有的評論都裝在這個框架之中,裡面的評論並沒有解析出來,所以我們才找不到div.reply-content元素。這時,我們需要加上對 iframe 的解析。
driver.switch_to.frame(driver.find_element_by_css_selector("iframe[title=livere]"))ncomment = driver.find_element_by_css_selector(div.reply-content)ncontent = comment.find_element_by_tag_name(p)nprint (content.text)n
運行上述代碼,我們可以得到最上面的一條評論:「第21條測試評論」。
4.3.3 selenium獲取文章的所有評論
上一節我們只是獲取了一條評論,如果要獲取所有評論,使用循環獲取所有評論。具體代碼如下:
from selenium import webdrivernfrom selenium.webdriver.firefox.firefox_binary nimport FirefoxBinarynimport timenncaps = webdriver.DesiredCapabilities().FIREFOXncaps["marionette"] = Truenbinary = FirefoxBinary(rD:Program FilesMozilla Firefoxfirefox.exe)n#把上述地址改成你電腦中Firefox程序的地址nndriver = webdriver.Firefox(firefox_binary=binary, capabilities=caps)ndriver.get("http://www.santostang.com/2017/03/02/hello-world/")ndriver.switch_to.frame(driver.find_element_by_css_selector("iframe[title=livere]"))nnncomments = driver.find_elements_by_css_selector(div.reply-content)nfor eachcomment in comments:n content = eachcomment.find_element_by_tag_name(p)n print (content.text)n
代碼的前端部分和之前一樣,用來打開該文章頁面。這裡,我們用的是driver.find_elements_by_css_selector(div.reply-content),在 element 後加了 s,找到所有的評論。然後再用find_element_by_tag_name(p)得到評論中的內容。
運行完成後,列印出來的結果是:
其實,selenium選擇元素的方法有很多,具體如下所示:
- find_element_by_id:通過元素的id選擇,例如:driver.find_element_by_id(loginForm)
- find_element_by_name:通過元素的name選擇,driver.find_element_by_name(password)
- find_element_by_xpath:通過xpath選擇,driver.find_element_by_xpath("//form[1]")
- find_element_by_link_text:通過鏈接地址選擇
- find_element_by_partial_link_text:通過鏈接的部分地址選擇
- find_element_by_tag_name:通過元素的名稱選擇
- find_element_by_class_name:通過元素的id選擇
- find_element_by_css_selector:通過css選擇器選擇
有時候,我們需要查找多個元素。在上述例子中,我們就查找了所有的評論。因此,也有對應的元素選擇方法,就是在上述的element後加上s,變成elements。
- find_elements_by_name
- find_elements_by_xpath
- find_elements_by_link_text
- find_elements_by_partial_link_text
- find_elements_by_tag_name
- find_elements_by_class_name
- find_elements_by_css_selector
其中xpath和css_selector是比較好的方法,一方面比較清晰,另一方面相對其他方法定位元素比較準確。
除此之外,我們還可以使用selenium操作元素方法實現自動操作網頁。常見的操作元素方法如下:
- clear 清除元素的內容
- send_keys 模擬按鍵輸入
- click 點擊元素
- submit 提交表單
user = driver.find_element_by_name("username") #找到用戶名輸入框nuser.clear #清除用戶名輸入框內容nuser.send_keys("1234567") #在框中輸入用戶名npwd = driver.find_element_by_name("password") #找到密碼輸入框npwd.clear #清除密碼輸入框內容npwd.send_keys("******") #在框中輸入密碼ndriver.find_element_by_id("loginBtn").click() #點擊登錄n
上述代碼中,是一個自動登錄程序中截取的一部分。從代碼中可以看到,我們可以用selenium操作元素的方法,對瀏覽器中的網頁進行各種操作,包括登錄。
除此之外,selenium除了滑鼠簡單的操作,還可以實現複雜的雙擊,拖拽等操作。此外,它還可以獲得網頁中各個元素的大小,甚至還可以進行模擬鍵盤的操作。由於篇幅有限,有興趣的讀者,可以到selenium的官方文檔查看:http://selenium-python.readthedocs.io/index.html
4.3.4 Selenium的高級操作
使用Selenium和使用瀏覽器「檢查」方法爬取動態網頁相比,Selenium因為要把整個網頁載入出來,再開始爬取內容,速度往往較慢。
因此在實際使用當中,如果使用瀏覽器「檢查」功能進行網頁的逆向工程不複雜的話,最好使用瀏覽器「檢查」功能方法。不過,也有一些方法可以用Selenium控制瀏覽器的載入的內容,從而加快Selenium的爬取速度。常用的方法有:
- 控制CSS的載入
- 控制圖片文件的顯示
- 控制JavaScript的執行
1. 控制CSS。因為抓取過程中我們僅僅抓取頁面的內容,CSS樣式文件是用來控制頁面的外觀和元素放置位置的,對內容並沒有影響。因此我們可以限制網頁載入CSS,從而減少抓取時間。代碼如下所示:
from selenium import webdrivernfrom selenium.webdriver.firefox.firefox_binary import FirefoxBinarynncaps = webdriver.DesiredCapabilities().FIREFOXncaps["marionette"] = Truennbinary = FirefoxBinary(rD:Program FilesMozilla Firefoxfirefox.exe)n#把上述地址改成你電腦中Firefox程序的地址nfp = webdriver.FirefoxProfile()nfp.set_preference("permissions.default.stylesheet",2)nndriver = webdriver.Firefox(firefox_binary=binary, firefox_profile=fp, capabilities=caps)ndriver.get("http://www.santostang.com/2017/03/02/hello-world/")n
運行上述代碼,得到的頁面如下所示:
2. 限制圖片的載入。如果我們不需要抓取網頁上的圖片的話,最好可以禁止圖片載入,限制圖片的載入可以幫助我們極大地提高網路爬蟲的效率。因為網頁上的圖片往往較多,而且圖片文件相對於文字,CSS,JavaScript等其他文件都比較大,所以載入需要較長時間。
from selenium import webdrivernfrom selenium.webdriver.firefox.firefox_binary import FirefoxBinarynncaps = webdriver.DesiredCapabilities().FIREFOXncaps["marionette"] = Falsenbinary = FirefoxBinary(rD:Program FilesMozilla Firefoxfirefox.exe)n#把上述地址改成你電腦中Firefox程序的地址nfp = webdriver.FirefoxProfile()nfp.set_preference("permissions.default.image",2)ndriver = webdriver.Firefox(firefox_binary=binary, firefox_profile = fp, capabilities=caps)ndriver.get("http://www.santostang.com/2017/03/02/hello-world/")n
運行上述代碼,得到的頁面如下所示:
3. 限制JavaScript的執行。如果我們需要抓取的內容不是通過JavaScript動態載入得到的,我們可以通過禁止JavaScript的執行來提高抓取的效率。因為大多數網頁都會利用JavaScript非同步地載入很多內容,這些內容不僅是我們不需要的,它們的載入還浪費了時間。
from selenium import webdrivernfrom selenium.webdriver.firefox.firefox_binary import FirefoxBinarynncaps = webdriver.DesiredCapabilities().FIREFOXncaps["marionette"] = Truennbinary = FirefoxBinary(rD:Program FilesMozilla Firefoxfirefox.exe)n#把上述地址改成你電腦中Firefox程序的地址nfp = webdriver.FirefoxProfile()nfp.set_preference("javascript.enabled", False)ndriver = webdriver.Firefox(firefox_binary=binary, firefox_profile = fp, capabilities=caps)ndriver.get("http://www.santostang.com/2017/03/02/hello-world/")n
那麼這三種方法,哪一種最節省時間呢?通過對上述三種方法的測試,嘗試載入博客的主頁50次,並對於載入時間取平均值。我們發現三種方法各自載入所需的時間,如下表所示:
通過上述結果,我們發現 3 種限制方法都能使爬蟲載入網頁的速度有所加快,其中全部限制對於載入速度的提升效果最好。由於 3 種方法的時間相差並不是很多,再加上網路環境和隨機變數的原因,因此我們並不能肯定哪種方法更好。具體的載入速度提升還得看相應的網頁,若網頁的圖片比較多,則限制圖片的載入肯定效果很好。 如果能夠限制,那麼最好限制多種載入,這樣的效果最好。
第四章其他章節請查看
第四章:動態網頁抓取 (解析真實地址 + selenium)
4.2 解析真實地址抓取
4.3 通過selenium 模擬瀏覽器抓取
推薦閱讀:
※mac下怎麼搭建selenium python環境?
※python selenium 如何查看網頁的源代碼 ?
※Pycharm—FileNotFoundError: [WinError 2] 系統找不到指定的文件?
※爬蟲雜談(二)使用Selenium抓取動態網站
※從零開始寫Python爬蟲 --- 3.3 爬蟲實踐:漫畫批量下載