區別於上篇動態網頁抓取,這里介紹另一種方法,即使用瀏覽器渲染引擎。直接用瀏覽器在顯示網頁時解析 HTML、應用 CSS 樣式並執行 JavaScript 的語句。
這個方法在爬蟲過程中會打開一個瀏覽器加載該網頁,自動操作瀏覽器瀏覽各個網頁,順便把數據抓下來。用一句簡單而通俗的話說,就是使用瀏覽器渲染方法將爬取動態網頁變成爬取靜態網頁。
我們可以用 Python 的 Selenium 庫模擬瀏覽器完成抓取。Selenium 是一個用於Web 應用程序測試的工具。Selenium 測試直接運行在瀏覽器中,瀏覽器自動按照腳本代碼做出單擊、輸入、打開、驗證等操作,就像真正的用戶在操作一樣。
通過Selenium模擬瀏覽器抓取。最常用 的是 Firefox,因此下面的講解也以 Firefox 為例,在運行之前需要安裝 Firefox 瀏 覽器。
以爬取《Python 網絡爬蟲:從入門到實踐》一書作者的個人博客評論為例。網址:http://www.santostang.com/2017/03/02/hello-world/
在運行下列代碼時,一定要留意自己網絡是否暢通,如果網絡不好造成瀏覽器不能正常打開網頁及其評論數據,就可能造成爬取失敗。
1)找到評論的HTML代碼標簽。使用Chrome打開該文章頁面,右鍵點擊頁面,打開“檢查”選項。定位到評論數據。此處定位到的評論數據即是瀏覽器渲染后的數據位置,如圖:
2)嘗試獲取一條評論數據。在原來打開頁面的代碼數據上,我們可以使用以下代碼,獲取第一條評論數據。在下面代碼中,driver.find_element_by_css_selector是用CSS選擇器查找元素,找到class為’reply-content’的div元素;find_element_by_tag_name則是通過元素的tag去尋找,意思是找到comment中的p元素。最后,再輸出p元素中的text文本。
相關代碼1:
from selenium import webdriver from selenium.webdriver.firefox.firefox_binary import FirefoxBinary caps=webdriver.DesiredCapabilities().FIREFOX caps["marionette"]=True binary=FirefoxBinary(r'E:\軟件安裝目錄\裝機必備軟件\Mozilla Firefox\firefox.exe') #把上述地址改成你電腦中Firefox程序的地址 driver=webdriver.Firefox(firefox_binary=binary,capabilities=caps) driver.get("http://www.santostang.com/2017/03/02/hello-world/") #page=driver.find_element_by_xpath(".//html") driver.switch_to.frame(driver.find_element_by_css_selector("iframe[title='livere']")) comment=driver.find_element_by_css_selector('div.reply-content-wrapper') #此處參數字段也可以是'div.reply-content',具體字段視具體網頁div包含關系而定 content=comment.find_element_by_tag_name('p') print(content.text) #driver.page_source
輸出:
在JS 里面也找不到https://api.gentie.163.com/products/ 哪位大神幫忙解答下。謝謝。
代碼解析:
1)caps=webdriver.DesiredCapabilities().FIREFOX
由此可知,將上文代碼中的caps["marionette"]=True注釋掉,代碼依舊可以正常運行。
2)binary=FirefoxBinary(r'E:\軟件安裝目錄\裝機必備軟件\Mozilla Firefox\firefox.exe')
3)driver=webdriver.Firefox(firefox_binary=binary,capabilities=caps)
構建webdriver類。
還可以構建別的類型的webdriver類。
4)driver.get("http://www.santostang.com/2017/03/02/hello-world/")
5)driver.switch_to.frame(driver.find_element_by_css_selector("iframe[title='livere']"))
6)comment=driver.find_element_by_css_selector('div.reply-content-wrapper')
7)content=comment.find_element_by_tag_name('p')
更多代碼含義和使用規則請參見官網API和Navigating:http://selenium-python.readthedocs.io/index.html
8)關於driver.switch_to.frame(driver.find_element_by_css_selector("iframe[title='livere']"))中的框架定位及title內容。
可在代碼中加入driver.page_source,並且注釋掉driver.switch_to.frame(driver.find_element_by_css_selector("iframe[title='livere']"))。可在輸出內容中找到(若輸出雜亂,不好找出相關內容,可將其復制黏貼到文本文件中,使用Notepad++打開,該軟件有前后標簽對應顯示功能):
(此處只截取了相關內容的末尾部分)
若使用了driver.switch_to.frame(driver.find_element_by_css_selector("iframe[title='livere']")),而再使用driver.page_source進行相關輸出,則發現沒有上面的iframe標簽,證明我們已經將該框架解析完畢,可以進行相關定位獲取元素了。
上面我們只是獲取了一條評論,如果要獲取所有評論,使用循環獲取所有評論。
相關代碼2:
from selenium import webdriver from selenium.webdriver.firefox.firefox_binary import FirefoxBinary caps=webdriver.DesiredCapabilities().FIREFOX caps["marionette"]=True binary=FirefoxBinary(r'E:\軟件安裝目錄\裝機必備軟件\Mozilla Firefox\firefox.exe') driver=webdriver.Firefox(firefox_binary=binary,capabilities=caps) driver.get("http://www.santostang.com/2017/03/02/hello-world/") #page=driver.find_element_by_xpath(".//html") driver.switch_to.frame(driver.find_element_by_css_selector("iframe[title='livere']")) comments=driver.find_elements_by_css_selector('div.reply-content') for eachcomment in comments: content=eachcomment.find_element_by_tag_name('p') print(content.text) #driver.page_source
輸出:
在JS 里面也找不到https://api.gentie.163.com/products/ 哪位大神幫忙解答下。謝謝。 @先生姓張 原來要按照這里的操作才行。。。 在JS 里面也找不到https://api.gentie.163.com/products/ 哪位大神幫忙解答下。謝謝。 @先生姓張 這是網易雲上面的一個連接地址,那個服務器都關閉了 在JS 里面也找不到https://api.gentie.163.com/products/ 哪位大神幫忙解答下。謝謝。 測試 為什么我用代碼打開的文章只有兩條評論,本來是有46條的,有大神知道怎么回事嗎? 菜鳥一只,求學習群 lalala1 我來試一試 我來試一試 應該點JS,然后看里面的Preview或者Response,里面響應的是Ajax的內容,然后如果去爬網站的評論的話,點開js那個請求后點Headers -->在General里面拷貝 RequestURL 就可以了
注意代碼2中將代碼1中的comment=driver.find_element_by_css_selector('div.reply-content-wrapper') 改成了comments=driver.find_elements_by_css_selector('div.reply-content')
elements加了s
以上獲取的全部評論數據均屬於正常進入該網頁,等該網頁渲染完獲取的全部評論,並未進行點擊“查看更多”來加載目前還未渲染的評論。
下面介紹一種能夠爬取到所有評論,包括點擊完“查看更多”加載的目前還未渲染的評論。
相關代碼:
from selenium import webdriver from selenium.webdriver.firefox.firefox_binary import FirefoxBinary import time caps=webdriver.DesiredCapabilities().FIREFOX caps["marionette"]=True binary=FirefoxBinary(r'E:\軟件安裝目錄\裝機必備軟件\Mozilla Firefox\firefox.exe') driver=webdriver.Firefox(firefox_binary=binary,capabilities=caps) driver.get("http://www.santostang.com/2017/03/02/hello-world/") driver.switch_to.frame(driver.find_element_by_css_selector("iframe[title='livere']")) time.sleep(60) for i in range(0,10): try: load_more=driver.find_element_by_css_selector('div.more-wrapper') load_more.click() except: pass time.sleep(5) comments=driver.find_elements_by_css_selector('div.reply-content') for eachcomment in comments: content=eachcomment.find_element_by_tag_name('p') print(content.text)
代碼解析:
1)time.sleep(60)
延時60秒執行以下代碼。
2)load_more=driver.find_element_by_css_selector('div.more-wrapper')中的參數字符串
打開目標網頁,等目標網頁整體渲染完之后,右鍵評論區的“查看更多”。
3)load_more.click()
模擬點擊“查看更多”按鈕,進行完整顯示所有評論。
4)之所以在代碼中兩次使用延時函數,是因為不同網絡狀況和不同機器環境下可能在打開網頁以及點擊“查看更多”按鈕后不能馬上顯示評論,所以代碼需要等一等,等到頁面完全渲染后,再進行評論區數據的收集工作。
因此各延時函數的延時時間長短可視具體情況靈活設置。
輸出結果:
在JS 里面也找不到https://api.gentie.163.com/products/ 哪位大神幫忙解答下。謝謝。 @先生姓張 原來要按照這里的操作才行。。。 在JS 里面也找不到https://api.gentie.163.com/products/ 哪位大神幫忙解答下。謝謝。 @先生姓張 這是網易雲上面的一個連接地址,那個服務器都關閉了 在JS 里面也找不到https://api.gentie.163.com/products/ 哪位大神幫忙解答下。謝謝。 測試 為什么我用代碼打開的文章只有兩條評論,本來是有46條的,有大神知道怎么回事嗎? 菜鳥一只,求學習群 lalala1 我來試一試 我來試一試 應該點JS,然后看里面的Preview或者Response,里面響應的是Ajax的內容,然后如果去爬網站的評論的話,點開js那個請求后點Headers -->在General里面拷貝 RequestURL 就可以了 現在死在了4.2節上,頁面評論是有的,但是XHR里沒有東西啊,這是什么情況?有解決的大神嗎? @骨犬 JS 為何靜態網頁抓取不了? 奇怪了,我按照書上的方法來操作,XHR也是空的啊 @易君召 我的也是空的 你解決問題了嗎 @思い亦深 看JS不是XHR XHR沒有顯示任何東西啊。奇怪。 找到原因了 caps["marionette"] = True 作者可以解釋一下這句話是干什么的嗎 @A cat named GitHub 改成 caps["marionette"] = False 可以運行。 目前還沒吃透代碼,先用着試試 我用的是 pycham IDE,按照作者的寫法寫的,怎么不行 @A cat named GitHub 找到原因了 caps["marionette"] = True 作者可以解釋一下這句話是干什么的嗎 對火狐版本有要求嗎 @花晨 我的也是提示火狐版本不匹配,你解決了嗎 @A cat named GitHub 重裝了geckodriver 改成caps["marionette"] = "Windows" 總之就好了 4.3.1 打開Hello World,代碼用的作者的,火狐地址我也設置了,為啥運行沒反應 from selenium import webdriver from selenium.webdriver.firefox.firefox_binary import FirefoxBinary caps = webdriver.DesiredCapabilities().FIREFOX caps["marionette"] = False binary = FirefoxBinary(r'C:\Program Files\Mozilla Firefox\firefox.exe') #把上述地址改成你電腦中Firefox程序的地址 driver = webdriver.Firefox(firefox_binary=binary, capabilities=caps) driver.get("http://www.santostang.com/2017/03/02/hello-world/") 我是番茄 為什么刷新沒有XHR數據,評論明明加載出來了 為什么刷新沒有XHR數據,評論明明加載出來了 @萌萌噠的小嘰嘰丶 書里錯誤很多,留個qq吧 為什么刷新沒有XHR數據,評論明明加載出來了 第21條測試評論 第20條測試評論 第19條測試評論 第18條測試評論 第17條測試評論 第16條測試評論 第15條測試評論 第14條測試評論 第13條測試評論 第12條測試評論 第11條測試評論 第10條測試評論 第9條測試評論 第8條測試評論 第7條測試評論 第6條測試評論 第5條測試評論 第4條測試評論 第3條測試評論 第二條測試評論 第一條測試評論
參考書目:唐松,來自《Python 網絡爬蟲:從入門到實踐》
參考書目作者關於本部分的介紹:http://www.santostang.com/2017/09/25/4-3-%E9%80%9A%E8%BF%87-selenium-%E6%A8%A1%E6%8B%9F%E6%B5%8F%E8%A7%88%E5%99%A8%E6%8A%93%E5%8F%96/