因?yàn)樽罱枰鲆粋€(gè)關(guān)于課程類的項(xiàng)目善延,但苦于沒(méi)有相關(guān)課程的信息及簡(jiǎn)介。當(dāng)我在看MOOC上面的Python爬蟲(chóng)課程時(shí),突然想到MOOC上面的課程信息如此完善,我為何不利用下MOOC上面的課程信息呢鹦赎,說(shuō)干就干,我隨便在MOOC上找到了一頁(yè)課程信息后误堡,便決定使用Python的requests庫(kù)去獲取課程信息了古话。課程信息如下
一、測(cè)試代碼如下
import requests
print(requests.get("https://www.icourse163.org/category/computer").text)
很容易的就得到了該頁(yè)面的源代碼
本以為拿到了源代碼但可以隨便的提取數(shù)據(jù)了锁施,但是我在下載下來(lái)的源代碼中找了很久沒(méi)有發(fā)現(xiàn)頁(yè)面正常顯示的時(shí)的課程信息陪踩。如上面的課程截圖有C語(yǔ)言,我搜索了下C語(yǔ)言悉抵,可是沒(méi)有在源代碼中搜索到膊毁。
當(dāng)我仔細(xì)看的查看課程列表頁(yè)面的源代碼的時(shí)候我才發(fā)現(xiàn)原來(lái)MOOC上面的課程列表信息是通過(guò)js加載的數(shù)據(jù),js需要瀏覽器才能加載基跑,普通的請(qǐng)求只能拿到渲染前的源代碼,所以在源代碼里面沒(méi)有找到相關(guān)的課程信息描焰。發(fā)現(xiàn)問(wèn)題之后就好辦了媳否,既然需要瀏覽器加載js來(lái)渲染數(shù)據(jù),那我們就給它一個(gè)瀏覽器然他渲染之后再去拿數(shù)據(jù)就是了荆秦。在python當(dāng)中篱竭,可以使用selenium去模擬各種各樣的瀏覽器,如chrome,safari,firefox步绸。甚至是手機(jī)上的瀏覽器掺逼。selenium+phantomjs便是一個(gè)無(wú)頭瀏覽器。它倆的結(jié)合便可以達(dá)到更好的數(shù)據(jù)采集效率瓤介。不過(guò)在python中需要安裝下selenium吕喘,至于phantomjs到官網(wǎng)去下載一個(gè)就是了。
安裝 selenium
pip3 install selenium
下載phantomjs
這兩個(gè)工具安裝好之后刑桑,便可以直接使用了氯质,現(xiàn)在來(lái)試試去獲取課程列表的那個(gè)頁(yè)面,看看能不能加載出數(shù)據(jù)祠斧。
# -*- coding: UTF-8 -*-
from selenium import webdriver
chrome=webdriver.PhantomJS(executable_path="E:\phantomJs\phantomJs.exe")
chrome.get("https://www.icourse163.org/category/computer")
print(chrome.page_source)
# executable_path為你下載phantomjs的地址
# page_source為當(dāng)前頁(yè)面的源代碼
于是現(xiàn)在源代碼中便有了課程列表的信息
有了課表列表頁(yè)面的源代碼后闻察,我們就可以提取課程的Url地址,提取出Url地址后,就可以進(jìn)行課表的詳情頁(yè)面辕漂,然后就可以隨便拿課程的詳細(xì)信息了呢灶。
二、解析課程列表頁(yè)面的課表url地址
在python中有許多的解析網(wǎng)頁(yè)源代碼的庫(kù)钉嘹,如正則表達(dá)式鸯乃、beautifulsoup、pyquery等隧期,通過(guò)運(yùn)行這些解析庫(kù)飒责,可以讓我們更加方便的提取我們自己想要的數(shù)據(jù)。本次將使用pyquery庫(kù)仆潮,pyquery庫(kù)同樣需要安裝宏蛉。
安裝pyquery庫(kù)
pip3 install pyquery
安裝好之后我們就可以解析源代碼中的數(shù)據(jù)了。通過(guò)檢查元素發(fā)現(xiàn)所有課程簡(jiǎn)介信息都位于id為j-courseCardListBox為的div下面性置,
再展開(kāi)標(biāo)簽后便可發(fā)現(xiàn)所有的課程鏈接都位于a標(biāo)簽下拾并。
發(fā)現(xiàn)這個(gè)規(guī)律后,我們便可以使用pyquery定位到id為j-courseCardListBox為的div中鹏浅,再在該div中找出所有a標(biāo)簽并取其href的值嗅义,便可以拿到本頁(yè)面的所有課程的詳細(xì)信息的鏈接地址了。有了思路后隐砸,代碼就比較好寫了之碗。
# -*- coding: UTF-8 -*-
from selenium import webdriver
from pyquery import PyQuery as pq
chrome=webdriver.PhantomJS(executable_path="E:\phantomJs\phantomJs.exe")
chrome.get("https://www.icourse163.org/category/computer")
code=pq(chrome.page_source)
href = code("#j-courseCardListBox a")
for i in href:
print(str(code(i).attr("href")))
結(jié)果如下:
發(fā)現(xiàn)結(jié)果中包含一些其它的信息的,經(jīng)測(cè)試發(fā)現(xiàn)類似于"//www.icourse163.org/course/XJTU-46006"這樣的地址信息季希,才是課程的地址信息褪那。所以在上面的代碼中加了一些判斷邏輯來(lái)去除其它的信息。
# -*- coding: UTF-8 -*-
from selenium import webdriver
from pyquery import PyQuery as pq
chrome=webdriver.PhantomJS(executable_path="E:\phantomJs\phantomJs.exe")
chrome.get("https://www.icourse163.org/category/computer")
code=pq(chrome.page_source)
href = code("#j-courseCardListBox a")
urlList=[]
for i in href:
temp = "http:" +str(code(i).attr("href"))
if temp.__contains__("www") and not temp.__contains__("https"):
print(temp)
urlList.append(temp)
urlList=list(set(urlList)) # 去除重復(fù)的url地址
結(jié)果如下:
經(jīng)過(guò)簡(jiǎn)單的處理之后便提取出了當(dāng)前頁(yè)面的課程詳細(xì)信息頁(yè)面的url鏈接地址式塌,是不是很容易呢博敬,果然是,很容易峰尝。提取一個(gè)頁(yè)面的課程詳細(xì)信息頁(yè)面的url鏈接地址并不是我想要的最終結(jié)果偏窝,我的最終目標(biāo)是拿到所有課程的詳細(xì)信息,所以就需要提取出所有課程詳細(xì)信息的url地址武学。要拿到所有課程的鏈接祭往,只需要實(shí)現(xiàn)翻頁(yè)就可以了,翻一頁(yè)拿一頁(yè)的課程詳細(xì)信息的鏈接地址劳淆。要實(shí)現(xiàn)翻頁(yè)爬取链沼,只需要知道當(dāng)前的課程的信息共有多少頁(yè),就可以使用一個(gè)循環(huán)沛鸵,每循環(huán)一次翻頁(yè)一次就可以了括勺。但是發(fā)現(xiàn)總頁(yè)數(shù)和其分頁(yè)的樣式都差不多一樣缆八,不是很方便提取總頁(yè)數(shù)的信息。
經(jīng)過(guò)我反復(fù)的實(shí)驗(yàn)后發(fā)現(xiàn)當(dāng)頁(yè)面位于最后一頁(yè)時(shí)疾捍,下一頁(yè)面的標(biāo)簽樣式會(huì)有變化奈辰,當(dāng)在非最后一頁(yè)的時(shí)候,下一頁(yè)的標(biāo)簽樣式為
當(dāng)在最后一頁(yè)的時(shí)候乱豆,下一頁(yè)的標(biāo)簽樣式為
通過(guò)對(duì)比發(fā)現(xiàn)這兩個(gè)標(biāo)簽的樣式不一樣奖恰,那我是不是就可以通過(guò)標(biāo)簽的樣式來(lái)判斷當(dāng)前頁(yè)面是否為最后一頁(yè)了,有了翻頁(yè)的思路之后就可以代碼來(lái)實(shí)現(xiàn)了宛裕,用一個(gè)while循環(huán)來(lái)翻頁(yè)瑟啃,在循環(huán)內(nèi)中判斷當(dāng)前頁(yè)面的下一頁(yè)的標(biāo)簽樣式是否為當(dāng)當(dāng)前頁(yè)面為最后一頁(yè)的標(biāo)簽樣式。如果是可以跳出循環(huán)了揩尸,至于提取單頁(yè)面的課程詳細(xì)信息的鏈接前面就已經(jīng)實(shí)現(xiàn)了蛹屿,下面是具體的代碼實(shí)現(xiàn)
chrome = webdriver.PhantomJS(executable_path="E:\phantomJs\phantomJs.exe")
webdriver.PhantomJS()
chrome.get(pageSourceUrl) # 打開(kāi)課程列表頁(yè)面
allUrl=[] # 用于存放獲取到的課程詳細(xì)信息的url地址
while (True):
allUrl=chain(allUrl,getPageUrl(chrome.page_source)) # chain是為了將多個(gè)列表轉(zhuǎn)換為一個(gè)列表,getPageUrl()為前面實(shí)現(xiàn)的獲取單頁(yè)面的課程詳細(xì)信息的url地址
chrome.find_element_by_link_text("下一頁(yè)").click() # 單擊下一頁(yè)標(biāo)簽實(shí)現(xiàn)翻頁(yè)
time.sleep(3) # 讓程序等待3秒鐘岩榆,因?yàn)闊o(wú)頭瀏覽器加載頁(yè)面需要一點(diǎn)時(shí)間错负,為了保險(xiǎn)起見(jiàn),所以設(shè)置的比較長(zhǎng)
if (chrome.find_element_by_class_name("ux-pager_btn__next").get_attribute("class") == "ux-pager_btn ux-pager_btn__next z-dis"):
allUrl = chain(allUrl, getPageUrl(chrome.page_source))
break
# if是判斷當(dāng)前頁(yè)面是否為最后一頁(yè)勇边,如果是則獲取最后一頁(yè)的課程詳細(xì)信息的url地址瞧捌,再跳出循環(huán)
chrome.quit()
部分結(jié)果如下:
通過(guò)以上的兩個(gè)步驟就基本可以拿到課程詳細(xì)信息的頁(yè)面的url地址了碱茁,拿到這些地址之后我們就可以爬去每個(gè)課程的課程詳細(xì)信息了棕叫。
三耿焊、拿到課程的詳細(xì)信息
要拿到課程的詳細(xì)信息無(wú)非就三個(gè)步驟,1奕坟、拿到其詳細(xì)頁(yè)面的url地址谊囚,2、下載其頁(yè)面的源代碼执赡,3、提取想要的數(shù)據(jù)函筋。第一步已經(jīng)做到了沙合,我們就可以直接開(kāi)始做第二、三步了跌帐,下面隨機(jī)選了一個(gè)課程詳細(xì)信息的頁(yè)面來(lái)做測(cè)試首懈。我們想從該頁(yè)面中提取以下信息,
1谨敛、課程名稱
2究履、課程概述
3、授課教師
4脸狸、課程大綱
由于其它信息不方便截取最仑,所以就沒(méi)有截圖說(shuō)明了藐俺,經(jīng)過(guò)反復(fù)的試驗(yàn)發(fā)現(xiàn)提取這些信息還是比較容易,因?yàn)樗麄兌加袠邮矫Q泥彤,我們可以通過(guò)樣式名稱來(lái)直接定位欲芹,再提取信息就是了。
import requests
from pyquery import PyQuery as pq
info=pq(requests.get("https://www.icourse163.org/course/TONGJI-89002").text)
print(info(".course-title.f-ib.f-vam").text()) # 課程名稱
t = info(".f-richEditorText")
print(info(".cnt.f-fl").text().replace("\n", " ")) # 教師信息
print(info(t[0]).text()) # 課程簡(jiǎn)介
print(info(t[1]).text()) # 課程大綱
print(info(t[2]).text()) # 先修課程
結(jié)果如下:
四吟吝、使用redis存取拿到的所有課程詳細(xì)信息
需要到redis的官網(wǎng)下載一個(gè)redis并安裝菱父,再下載一個(gè)redis可視化管理工具,并可以對(duì)redis進(jìn)行管理了剑逃,當(dāng)然也可以直接使用命令行浙宜。
在python中使用redis需要安裝redis庫(kù)才行,
安裝redis庫(kù)
pip3 install redis
安裝好之后就可以直接使用redis了蛹磺。
import redis
r=redis.from_url("http://127.0.0.1:6379")
r.set("test","123")
print(r.get("test"))
結(jié)果如下:
因?yàn)榕廊〉恼n程信息是由許多的數(shù)據(jù)項(xiàng)構(gòu)成粟瞬,所以我們?cè)诖鎯?chǔ)的時(shí)候可以使用hashMap。在python中即r.hset(),r.hget()称开。
我將上面的所有步驟都分別寫成了具體的函數(shù)亩钟,下面是本次爬蟲(chóng)中的所有代碼。
# -*- coding: UTF-8 -*-
from selenium import webdriver
from pyquery import PyQuery as pq
import time
import redis
import chardet
import requests
from itertools import chain
r = redis.from_url("http://127.0.0.1:6379")
def getPageUrl(pageSource):
code = pq(pageSource)
href = code("#j-courseCardListBox a")
urlList = []
for i in href:
temp = str(code(i).attr("href"))
if temp.__contains__("www") and not temp.__contains__("https"):
urlList.append("http:" + temp)
urlList = list(set(urlList))
return urlList
def getAllUrl(pageSourceUrl):
chrome = webdriver.PhantomJS(executable_path="E:\phantomJs\phantomJs.exe")
webdriver.PhantomJS()
chrome.get(pageSourceUrl)
allUrl=[]
count=1
while (True):
allUrl=chain(allUrl,getPageUrl(chrome.page_source))
print(count)
print(chrome.find_element_by_class_name("ux-pager_btn__next").get_attribute("class"))
chrome.find_element_by_link_text("下一頁(yè)").click()
time.sleep(3)
if (chrome.find_element_by_class_name("ux-pager_btn__next").get_attribute("class") == "ux-pager_btn ux-pager_btn__next z-dis"):
allUrl = chain(allUrl, getPageUrl(chrome.page_source))
print(count)
break
count+=1
chrome.quit()
return allUrl
def saveCourseInfoes(courseUrlList=[]):
count,index=0,0
errorList=[]
while(count<courseUrlList.__len__()):
info = pq(requests.get(courseUrlList[count]).text)
print(info(".course-title.f-ib.f-vam").text())
t = info(".f-richEditorText")
if (len(t) >= 3):
print(info(".cnt.f-fl").text().replace("\n", " "))
r.hset("CourseInfo", index, {
"courseName": info(".course-title.f-ib.f-vam").text(),
"teacherInfo": info(".cnt.f-fl").text().replace("\n", " "),
"anOverviewOfTheCourse": info(t[0]).text(),
"teachingObjectives": info(t[1]).text(),
"syllabus": info(t[2]).text()
})
index+=1
else:
errorList.append(courseUrlList[count])
count+=1
return errorList
timeStart=time.time()
allUrl=getAllUrl("https://www.icourse163.org/category/all")
errorList=saveCourseInfoes(list(allUrl))
print("\n\n")
for i in errorList:
print(i)
print("共耗時(shí):",end=" ")
print(time.time()-timeStart)
結(jié)果如下:
以上就是本次爬蟲(chóng)的所有內(nèi)容了鳖轰,總爬到了1111門課程信息清酥,不知道是否有重復(fù)的內(nèi)容存在≡搪拢總結(jié)一下本次爬蟲(chóng)用到的一些庫(kù)和工具焰轻。
1、python
2昆雀、selenium+phantomjs
3辱志、requests
4、pyquery
5狞膘、redis
這些庫(kù)和工具的確比較方便揩懒,不過(guò)有時(shí)候需要具體問(wèn)題具體分析,這樣才能更好的發(fā)揮它們的作用挽封。