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的話,可以去github.com/mozilla/geck下載最新版的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的官方文檔查看:selenium-python.readthedocs.io

4.3.4 Selenium的高級操作

使用Selenium和使用瀏覽器「檢查」方法爬取動態網頁相比,Selenium因為要把整個網頁載入出來,再開始爬取內容,速度往往較慢。

因此在實際使用當中,如果使用瀏覽器「檢查」功能進行網頁的逆向工程不複雜的話,最好使用瀏覽器「檢查」功能方法。不過,也有一些方法可以用Selenium控制瀏覽器的載入的內容,從而加快Selenium的爬取速度。常用的方法有:

  1. 控制CSS的載入
  2. 控制圖片文件的顯示
  3. 控制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 爬蟲實踐:漫畫批量下載

TAG:网页爬虫 | python爬虫 | Selenium |