Ps:又到了我們的ps環(huán)節(jié),不知道上次大家嘗試的如何,這次我們將簡單介紹如何使用selenium+PhantomJS來抓取異步加載的網(wǎng)頁數(shù)據(jù)信息。當(dāng)然,selenium是一個非常強(qiáng)大的自動化工具,可以做非常多的事,有興趣的同學(xué)可以自行了解一下。
這次我們的順序稍稍變化一下,因為牽扯到配置環(huán)境。
環(huán)境配置
- selenium
pip install -U selenium
建議采用pycharm安裝,別忘了我們這個強(qiáng)大的IDE - PhantomJS
這個不同的操作系統(tǒng)有各自對應(yīng)的版本,這是官網(wǎng)的下載頁面http://phantomjs.org/download.html,去下載你對應(yīng)操作系統(tǒng)版本的phantomjs.下載完后,解壓縮可以看到在文件夾的bin目錄下有對應(yīng)的phantomjs的可執(zhí)行文件,拷貝一份放入一個環(huán)境變量可以搜索到的地方,或者直接把phantomjs的bin目錄加入環(huán)境變量即可~
簡單介紹一下PhantomJS,這是一個基于webkit的沒有界面的瀏覽器,也就是它可以像瀏覽器解析網(wǎng)頁,功能非常強(qiáng)大。但是據(jù)我測試。。解析的結(jié)果不一定和火狐或者chrome完全一樣,但是完全夠我們用。
簡單介紹一下selenium,這是一個web的自動測試工具,可以模擬人的操作。支持市面上幾乎所有的主流瀏覽器,同時也支持PhantomJS這種無界面瀏覽器。
因為selenium+Firefox或者Chrome太慢了,所以我們選用selenium+PhantomJS。
想試試selenium+Firefox的同學(xué)直接下載最新版的Firefox即可,不需要插件,但是chrome的話需要一個插件叫chromedriver,我會放到群里,閑話不多說,gogogo!
代碼預(yù)覽
#coding:utf-8
import unittest
from selenium import webdriver
from bs4 import BeautifulSoup
class seleniumTest(unittest.TestCase):
def setUp(self):
self.driver = webdriver.PhantomJS()
def testEle(self):
driver = self.driver
driver.get('http://www.douyu.com/directory/all')
soup = BeautifulSoup(driver.page_source, 'xml')
while True:
titles = soup.find_all('h3', {'class': 'ellipsis'})
nums = soup.find_all('span', {'class': 'dy-num fr'})
for title, num in zip(titles, nums):
print title.get_text(), num.get_text()
if driver.page_source.find('shark-pager-disable-next') != -1:
break
elem = driver.find_element_by_class_name('shark-pager-next')
elem.click()
soup = BeautifulSoup(driver.page_source, 'xml')
def tearDown(self):
print 'down'
if __name__ == "__main__":
unittest.main()
小伙伴們是不是驚奇的發(fā)現(xiàn)今天的代碼怎么略不一樣了~因為今天除了爬蟲相關(guān),順道教大家一些python 的小知識~
代碼剖析
import unittest
什么是unittest呢?看字面意思估計大部分人已經(jīng)懂了,就是單元測試的意思,這個庫是python自帶的給我們提供測試用的庫,很好用,今天我順道介紹一下這個庫的簡單使用。
今天的這段代碼是一個簡單的測試用例
class seleniumTest(unittest.TestCase):
繼承TestCase類,表明這是一個測試類。setup方法是初始化方法,在unittest的main方法調(diào)用之初執(zhí)行,然后tearDown是在測試完成的時候執(zhí)行,中間的每個方法必須以test開頭。這是一些簡單的規(guī)則,大概了解之后我們就可以開心的使用單元測試了~
def setUp(self):
self.dirver = webdriver.PhantomJS()
首先在我們的setup方法中初始化我們的webdriver,如果你用的是pycharm就可以看到webdriver后面會有多種方法,可以創(chuàng)建火狐的,chrome等各種瀏覽器的webdriver。
初始化完成之后開始解析,今天我們抓取斗魚tv的房間信息,用瀏覽器打開http://www.douyu.com/directory/all,然后選擇查看網(wǎng)頁源代碼,發(fā)現(xiàn)竟然可以直接在源碼中看到房間信息,心中一喜,難道不是異步的方式么?這太好抓了,直接requests請求然后分析就可以了啊。緊接著再點開第二頁,查看網(wǎng)頁源代碼。發(fā)現(xiàn)竟然和第一頁一模一樣,頓時失望,依然是采用的js異步的方式來加載數(shù)據(jù)的,只不過這次返回的不是json格式而直接是html。難道我們需要像上一章那樣么?nono,今天我們直接使用PhantomJS模擬瀏覽器的訪問過程,然后通過dirver.get方法獲取瀏覽器加載完成后的源碼。這樣子無論是否異步,我們?nèi)〉降膕ource都是和我們在瀏覽器中看到的完全一樣。是不是感覺簡便了很多呢?
dirver.get('http://www.douyu.com/directory/all')
soup = BeautifulSoup(dirver.page_source, 'xml')
這兩句想必大家也能理解了,其實就是換了一種方式去獲取到網(wǎng)頁源碼而已。這次連帶js渲染出來的網(wǎng)頁也都獲取到了。接下來
titles = soup.find_all('h3', {'class': 'ellipsis'})
nums = soup.find_all('span', {'class': 'dy-num fr'})
for title, num in zip(titles, nums):
print title.get_text(), num.get_text()
這段代碼是在源碼中取出我們想要的信息,可能同學(xué)們也發(fā)現(xiàn)了,這和我們第三節(jié)講的不太一樣,這是因為我們的解析方式換成了xml而不是原本的lxml了,lxml屬于一種更加高效的解析模式,但是我為什么要更換呢。因為在我寫這段代碼的時候發(fā)生了一個小插曲,原本我也是用lxml的方式去解析的,但是發(fā)現(xiàn)每次只能解析出來32條信息,但是很明顯斗魚每個小分頁里最少都有100多個房間,我查看了driver返回的page_source,發(fā)現(xiàn)沒有問題,于是我想可能是beautifulsoup的解析出了問題,打印soup,發(fā)現(xiàn)只有源代碼的一半,后面一半莫名奇妙丟失了。原本我以為是網(wǎng)頁中有多個</html>標(biāo)簽導(dǎo)致解析出錯,后來發(fā)現(xiàn)也不是這個問題,百度google很久也沒有結(jié)果,所以只能換一種解析模式,當(dāng)我換成xml的時候發(fā)現(xiàn)解析無誤。暫時我也不知道原因,有興趣的同學(xué)可以嘗試解決一下這個問題。
那么上面這段代碼取出了我們需要的信息,beautifulsoup的find方法要是不了解的話可以參看官方文檔。接下來:
elem = driver.find_element_by_class_name('shark-pager-next')
elem.click()
soup = BeautifulSoup(driver.page_source, 'xml')
第一行代碼是webdriver里面帶有的定位標(biāo)簽的方法,我們通過觀察發(fā)現(xiàn)斗魚頁面中下一頁這個標(biāo)簽只出現(xiàn)一次,并且是獨立樣式,所以我們直接通過class進(jìn)行定位,取到這個控件,然后執(zhí)行element的click()方法模擬鼠標(biāo)點擊,這樣,頁面就自動跳到了下一頁,接著繼續(xù)解析網(wǎng)頁源碼。
if driver.page_source.find('shark-pager-disable-next') != -1:
break
這段代碼是跳出循環(huán)用的,什么時候跳出循環(huán)呢?我們通過觀察發(fā)現(xiàn),當(dāng)?shù)阶詈笠豁摃r候斗魚下一頁的標(biāo)簽就會變灰,如圖所示:

于是,我們發(fā)現(xiàn)了有個唯一的shark-pager-disable-next樣式,那么我們只需要在源碼中找這個樣式,如果有則說明已經(jīng)解析到最后一頁,直接跳出循環(huán)結(jié)束程序就好~
至此,今天的代碼就講解完畢了。有沒有覺得比上次的觀察法方便了很多呢?附帶一提,有興趣的通許可以吧今天代碼中self.dirver = webdriver.PhantomJS()換成self.dirver = webdriver.Firefox(),看看會發(fā)生什么有趣的事情,我就不劇透了~
寫在最后
慣例了,demo已經(jīng)給你們了,也講解完畢,現(xiàn)在需要你們自己實踐了,去嘗試抓取一下其他TV吧,推薦抓一下戰(zhàn)旗,因為加載方式又和斗魚完全不一樣了,有挑戰(zhàn)性點~下一節(jié)我們講解如何在爬蟲中使用多進(jìn)程!
附加部分
一些seleium的小互動操作
- 填入表單數(shù)據(jù)
#coding:utf-8
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
driver = webdriver.Firefox()
driver.get('https://www.baidu.com/')
elem = driver.find_element_by_id('kw')
elem.send_keys(u'爬蟲')
elem.send_keys(Keys.RETURN)
print(driver.page_source)
- 滾動致頁面最下方
#coding:utf-8
from selenium import webdriver
import time
driver = webdriver.Firefox()
driver.get('http://www.itdecent.cn/collections')
time.sleep(1)
for i in range(10):
dirver.execute_script('window.scrollTo(0,document.body.scrollHeight)')
time.sleep(1)
使用selenium模擬人的操作行為,這次換成了火狐瀏覽器,可以看得更加清楚~
有興趣的同學(xué)可以加群498945822一起交流學(xué)習(xí)哦~~
發(fā)現(xiàn)問題的同學(xué)歡迎指正,直接說就行,不用留面子,博主臉皮厚