本文的所有程式碼都在GitHub上託管,想要程式碼的同學請點選這裡?
序:由於自己想要實現一個課程推薦系統,需要在各大視訊網站上爬取所有視訊課程,從而為後續的推薦工作提供大量資料,在此篇部落格中我分別爬取了MOOC、網易雲課堂、騰訊課堂、學堂線上共約15萬條資料。
執行環境:
-
mysqlclient
~=1.4.6 -
requests
~=2.22.0 -
bs4
~=0.0.1 -
beautifulsoup4
~=4.8.2
MOOC
首先進入網站,在這裡我們分析他的API設計,先要找到他是從哪一個API獲得相應課程的,經過分析之後我們發現是https://www.icourse163.org/web/j/courseBean.getCoursePanelListByFrontCategory.rpc
這個API,其返回內容如下:
然後我們隨意點選頁面上的一個課程,找到其課程url的規律,開啟溝通心理學這門課程,其URL是https://www.icourse163.org/course/HIT-1001515007
,而溝通心理學這門課程返回的資訊是:
// 在這裡我只保留了我需要的一些資料
{
name: "溝通心理學",
id: 1001515007,
schoolPanel: {id: 9005, name: "哈爾濱工業大學", shortName: "HIT"}
}
我們可以發現課程的URL就是學校的簡稱-id,這樣就可以組成課程URL,現在我們得知課程URL如何得知,那麼這些課程資料需要傳什麼引數呢,如下:
{
categoryId: -1, // 類別id,因為我這裡選的全部,所以是-1
type: 30,
orderBy: 0,
pageIndex: 1, // 第幾頁
pageSize: 20 // 每頁多少條資料
}
到這裡就新產生了一個問題,categoryId
是怎麼來的,我們繼續看網頁請求的api列表,找到這樣一個APIhttps://www.icourse163.org/web/j/mocCourseCategoryBean.getCategByType.rpc
,其返回結果如下:
資料結構大致如下:
我們想要得到的課程分類特別細緻的話就需要一直向下找json的children,直到children為空,演算法的話就採用遞迴演算法就可以。
到現在為止我們已經知道了如何獲取類別id,如果由類別id獲得課程資料,接下來我們就需要把獲取到的資料儲存到資料庫中,我的資料庫包含類別、課程名稱、課程圖片URL、課程URL、課程來源這四個欄位,儲存程式碼如下:
# 儲存到資料庫
def save_to_mysql(data, category_name):
sql = "insert into webCourses (category, name, site, imgUrl, resource) values ('{0}', '{1}', '{2}', '{3}', '{4}')".format(
category_name, data["name"],
'https://www.icourse163.org/course/' + str(data["schoolPanel"]["shortName"]) + "-" + str(data["id"]),
data["imgUrl"], "慕課")
print(sql)
execute(sql)
要注意這裡的execute
函式是我封裝的一個函式,具體的作用就是執行sql語句,全部程式碼請到我的GitHub檢視。
騰訊課堂
其實如果你看過了上面MOOC的獲取所有課程的API設計,其他課程網站的API設計也是大致相同的,首先我們要獲得類別id,然後再根據類別id去請求資料,與mooc不同的是騰訊課堂請求課程資料是通過beautifulsoup4
解析html
內容實現的。下面就來簡單說一下:
獲取課程類別的API:https://ke.qq.com/cgi-bin/get_cat_info
根據類別id獲得資料的網頁url: https://ke.qq.com/course/list?mt=1001&st=2001&tt=3001&page=2
,這裡的mt、st、tt分別是三個類別,st是mt的一個子類,tt是st的一個子類,page就是頁數了。
得到的網站如下:
在這裡我們需要的是每一個課程,其實思路很簡單,按F12
開啟開發者工具,找到課程對應的dom塊,用beautifulsoup4
解析html
內容,得到課程陣列就可以了,程式碼如下:
# 獲取課程資料
def get_course_data(mt, st, tt, page, category):
url = "https://ke.qq.com/course/list?mt=" + str(mt) + "&st=" + str(st) + "&tt=" + str(tt) + "&page=" + str(page)
response = requests.request("GET", url).text
bs = BeautifulSoup(response)
course_blocks = bs.find_all(name='li', attrs={"class": "course-card-item--v3 js-course-card-item"})
# print(course_blocks)
if len(course_blocks) != 0:
for i in range(len(course_blocks)):
bs = course_blocks[i]
img = bs.find(name="img", attrs={"class", "item-img"})
a = bs.find(name="a", attrs={"class", "item-img-link"})
save_to_mysql(img.attrs["alt"], a.attrs["href"], img.attrs["src"], category)
return True # 這裡是返回該類別的下一頁是否還有資料
else:
return False
得到資料之後再將這些資料存入到資料庫中就可以了。
網易雲課堂
其實網易雲課堂就和MOOC的API設計非常類似了,畢竟都是網易公司的程式設計師寫的,規範也都差不多,看懂MOOC的api設計的同學直接去我的github看程式碼就可以了。
學堂線上
學堂線上的API設計就比較簡單,直接通過一個API就可以獲得所有的資料,https://next.xuetangx.com/api/v1/lms/get_product_list/?page=1
,返回的資料格式如下:
在這裡一個API裡面課程名稱、分類、課程封面URL,課程id可以看的非常請求,下面我們就需要得到課程資訊與課程url之間的關係,我們隨意點開一個課程,分析他的URL,我們就可以發現,課程URL就是https://next.xuetangx.com/course/
加上課程的course_sign
組成的。
到這裡就分析結束,儲存到資料庫即可。
小結
從上述的分析我們可以看出,各大課程網站的api設計都是類似的,並且他們都沒有做api請求限制,所以我在爬取過程中沒有遇到過被封IP的情況,也算是省了不少事?。在這裡把程式碼與思路分享給大家,希望能夠給到大家一些幫助。所有程式碼請點選這裡?