如何爬取中國(guó)大學(xué)MOOC上的課程信息

因?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ù)去獲取課程信息了古话。課程信息如下


image.png

一、測(cè)試代碼如下

import requests
print(requests.get("https://www.icourse163.org/category/computer").text)

很容易的就得到了該頁(yè)面的源代碼


image.png

本以為拿到了源代碼但可以隨便的提取數(shù)據(jù)了锁施,但是我在下載下來(lái)的源代碼中找了很久沒(méi)有發(fā)現(xiàn)頁(yè)面正常顯示的時(shí)的課程信息陪踩。如上面的課程截圖有C語(yǔ)言,我搜索了下C語(yǔ)言悉抵,可是沒(méi)有在源代碼中搜索到膊毁。


image.png

當(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


image.png

這兩個(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)在源代碼中便有了課程列表的信息


image.png

有了課表列表頁(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下面性置,


image.png

再展開(kāi)標(biāo)簽后便可發(fā)現(xiàn)所有的課程鏈接都位于a標(biāo)簽下拾并。


image.png

發(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é)果如下:


image.png

發(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é)果如下:


image.png

經(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ù)的信息。


image.png

經(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)簽樣式為


image.png

當(dāng)在最后一頁(yè)的時(shí)候乱豆,下一頁(yè)的標(biāo)簽樣式為
image.png

通過(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é)果如下:


image.png

通過(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脸狸、課程大綱


image.png

由于其它信息不方便截取最仑,所以就沒(méi)有截圖說(shuō)明了藐俺,經(jīng)過(guò)反復(fù)的試驗(yàn)發(fā)現(xiàn)提取這些信息還是比較容易,因?yàn)樗麄兌加袠邮矫Q泥彤,我們可以通過(guò)樣式名稱來(lái)直接定位欲芹,再提取信息就是了。


image.png
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é)果如下:


image.png

四吟吝、使用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é)果如下:


image.png

因?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é)果如下:


image.png

以上就是本次爬蟲(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ā)揮它們的作用挽封。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末已球,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子辅愿,更是在濱河造成了極大的恐慌智亮,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,888評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件点待,死亡現(xiàn)場(chǎng)離奇詭異阔蛉,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)癞埠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門状原,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)聋呢,“玉大人,你說(shuō)我怎么就攤上這事遭笋“用幔” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 168,386評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵瓦呼,是天一觀的道長(zhǎng)喂窟。 經(jīng)常有香客問(wèn)我,道長(zhǎng)央串,這世上最難降的妖魔是什么磨澡? 我笑而不...
    開(kāi)封第一講書人閱讀 59,726評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮质和,結(jié)果婚禮上稳摄,老公的妹妹穿的比我還像新娘。我一直安慰自己饲宿,他們只是感情好厦酬,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著瘫想,像睡著了一般仗阅。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上国夜,一...
    開(kāi)封第一講書人閱讀 52,337評(píng)論 1 310
  • 那天减噪,我揣著相機(jī)與錄音,去河邊找鬼车吹。 笑死筹裕,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的窄驹。 我是一名探鬼主播朝卒,決...
    沈念sama閱讀 40,902評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼乐埠!你這毒婦竟也來(lái)了扎运?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,807評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤饮戳,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后洞拨,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體扯罐,經(jīng)...
    沈念sama閱讀 46,349評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評(píng)論 3 340
  • 正文 我和宋清朗相戀三年烦衣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了歹河。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掩浙。...
    茶點(diǎn)故事閱讀 40,567評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖秸歧,靈堂內(nèi)的尸體忽然破棺而出厨姚,到底是詐尸還是另有隱情,我是刑警寧澤键菱,帶...
    沈念sama閱讀 36,242評(píng)論 5 350
  • 正文 年R本政府宣布谬墙,位于F島的核電站,受9級(jí)特大地震影響经备,放射性物質(zhì)發(fā)生泄漏拭抬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評(píng)論 3 334
  • 文/蒙蒙 一侵蒙、第九天 我趴在偏房一處隱蔽的房頂上張望造虎。 院中可真熱鬧,春花似錦纷闺、人聲如沸算凿。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 32,420評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)氓轰。三九已至,卻和暖如春波桩,著一層夾襖步出監(jiān)牢的瞬間戒努,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,531評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工镐躲, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留储玫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,995評(píng)論 3 377
  • 正文 我出身青樓萤皂,卻偏偏與公主長(zhǎng)得像撒穷,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子裆熙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理端礼,服務(wù)發(fā)現(xiàn),斷路器入录,智...
    卡卡羅2017閱讀 134,702評(píng)論 18 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,298評(píng)論 25 707
  • 有一個(gè)愛(ài)倒立的小女孩蛤奥,她叫米奇,她倒立著爬山僚稿,倒立著上樓梯凡桥,甚至倒立著吃飯。 一天蚀同,她的爸爸和媽媽覺(jué)得她倒立有點(diǎn)不...
    晨光微曉閱讀 236評(píng)論 0 0
  • 昨天晚上孩子訂正月考作文缅刽,我想看看啊掏,她不讓看,滿分50分衰猛,得37分迟蜜,還以為是考少了不讓看,當(dāng)時(shí)有點(diǎn)氣氛想搶過(guò)來(lái)看啡省,...
    幸福駕到閱讀 262評(píng)論 0 4
  • 如果說(shuō)陪伴是最長(zhǎng)情的告白那冕杠,等待一定是最極致的思念吧 五個(gè)月匆匆而過(guò)微姊,轉(zhuǎn)眼間,我們已經(jīng)分離了近半年了分预,近來(lái)兢交,天氣轉(zhuǎn)...
    Maries閱讀 316評(píng)論 0 0