摘要:至此登錄功能初步實(shí)現(xiàn)。為了方便我們快速定位到每節(jié)課的所有資源,可以把一節(jié)課的所有資源文件均命名為課名文件類型??梢钥匆幌乱粋€(gè)測(cè)試?yán)又械奈募?,部分?nèi)容如下到這里為止,我們已經(jīng)成功完成爬取課程資源的目標(biāo),具體的代碼放在上。
原文地址
有時(shí)候我們需要把一些經(jīng)典的東西收藏起來(lái),時(shí)時(shí)回味,而Coursera上的一些課程無(wú)疑就是經(jīng)典之作。Coursera中的大部分完結(jié)課程都提供了完整的配套教學(xué)資源,包括ppt,視頻以及字幕等,離線下來(lái)后會(huì)非常便于學(xué)習(xí)。很明顯,我們不會(huì)去一個(gè)文件一個(gè)文件的下載,只有傻子才那么干,程序員都是聰明人!
那我們聰明人準(zhǔn)備怎么辦呢?當(dāng)然是寫一個(gè)腳本來(lái)批量下載了。首先我們需要分析一下手工下載的流程:登錄自己的Coursera賬戶(有的課程需要我們登錄并選課后才能看到相應(yīng)的資源),在課程資源頁(yè)面里,找到相應(yīng)的文件鏈接,然后用喜歡的工具下載。
很簡(jiǎn)單是吧?我們可以用程序來(lái)模仿以上的步驟,這樣就可以解放雙手了。整個(gè)程序分為三個(gè)部分就可以了:
登錄Coursera;
在課程資源頁(yè)面里面找到資源鏈接;
根據(jù)資源鏈接選擇合適的工具下載資源。
下面就來(lái)具體的實(shí)現(xiàn)以下吧!
登錄剛開始時(shí)自己并沒(méi)有添加登錄模塊,以為訪客就可以下載相應(yīng)的課程資源,后來(lái)在測(cè)試comnetworks-002這門課程時(shí)發(fā)現(xiàn)訪客訪問(wèn)資源頁(yè)面時(shí)會(huì)自動(dòng)跳轉(zhuǎn)到登錄界面,下圖是chrome在隱身模式訪問(wèn)該課程資源頁(yè)面時(shí)的情況。
要想模擬登錄,我們先找到登錄的頁(yè)面,然后利用google的Developer Tools分析賬號(hào)密碼是如何上傳到服務(wù)器的。
我們?cè)诘卿涰?yè)面的表單中填入賬號(hào)密碼,然后點(diǎn)擊登錄。與此同時(shí),我們需要雙眼緊盯Developer Tools——Network,找到提交賬號(hào)信息的url。一般情況下,如果要向服務(wù)器提交信息,一般都用post方法,這里我們只需要先找到Method為post的url。悲劇的是,每次登錄賬號(hào)時(shí),Network里面都找不到提交賬戶信息的地址。猜測(cè)登錄成功后,直接跳轉(zhuǎn)到登錄成功后的頁(yè)面,想要找的內(nèi)容一閃而過(guò)了。
于是就隨便輸入了一組賬號(hào)密碼,故意登錄失敗,果真找到了post的頁(yè)面地址,如下圖:
地址為:https://accounts.coursera.org/api/v1/login。為了知道向服務(wù)器提交了哪些內(nèi)容,進(jìn)一步觀察post頁(yè)面中表單中內(nèi)容,如下圖:
我們看到一共有三個(gè)字段:
email:賬號(hào)的注冊(cè)郵箱
password:賬號(hào)密碼
webrequest:附加的字段,值為true。
接下來(lái)就動(dòng)手寫吧,我選擇用python的Requests庫(kù)來(lái)模擬登錄,關(guān)于Requests官網(wǎng)是這樣介紹的。
Requests is an elegant and simple HTTP library for Python, built for human beings.
事實(shí)上requests用起來(lái)確實(shí)簡(jiǎn)單方便,不虧是專門為人類設(shè)計(jì)的http庫(kù)。requests提供了Session對(duì)象,可以用來(lái)在不同的請(qǐng)求中傳遞一些相同的數(shù)據(jù),比如在每次請(qǐng)求中都攜帶cookie。
初步的代碼如下:
signin_url = "https://accounts.coursera.org/api/v1/login" logininfo = {"email": "...", "password": "...", "webrequest": "true" } user_agent = ("Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_4) " "AppleWebKit/537.36 (KHTML, like Gecko) " "Chrome/36.0.1985.143 Safari/537.36") post_headers = {"User-Agent": user_agent, "Referer": "https://accounts.coursera.org/signin" } coursera_session = requests.Session() login_res = coursera_session.post(signin_url, data=logininfo, headers=post_headers, ) if login_res.status_code == 200: print "Login Successfully!" else: print login_res.text
將表單中提交的內(nèi)容存放在字典中,然后作為data參數(shù)傳遞給Session.post函數(shù)。一般情況下,最好是加上請(qǐng)求User-Agent,Referer等請(qǐng)求頭部,User-Agent用來(lái)模擬瀏覽器請(qǐng)求,Referer用來(lái)告訴服務(wù)器我是從referer頁(yè)面跳轉(zhuǎn)到請(qǐng)求頁(yè)面的,有時(shí)候服務(wù)器會(huì)檢查請(qǐng)求的Referer字段來(lái)保證是從固定地址跳到當(dāng)前請(qǐng)求頁(yè)的。
上面片段的運(yùn)行結(jié)果很奇怪,顯示如下信息:Invalid CSRF Token。后來(lái)在github上面搜索到一個(gè)Coursera的批量下載腳本,發(fā)現(xiàn)人家發(fā)送頁(yè)面請(qǐng)求時(shí)headers多了XCSRF2Cookie, XCSRF2Token, XCSRFToken, cookie4個(gè)字段。于是又重新看了一下post頁(yè)面的請(qǐng)求頭部,發(fā)現(xiàn)確實(shí)有這幾個(gè)字段,估計(jì)是服務(wù)器端用來(lái)做一些限制的。
用瀏覽器登錄了幾次,發(fā)現(xiàn)XCSRF2Token, XCSRFToken是長(zhǎng)度為24的隨機(jī)字符串,XCSRF2Cookie為"csrf2_token_"加上長(zhǎng)度為8的隨機(jī)字符串。不過(guò)一直沒(méi)搞明白Cookie是怎么求出來(lái)的,不過(guò)看github上面代碼,Cookie似乎只是"csrftoken"和其他三個(gè)的組合,試了一下竟然可以。
在原來(lái)的代碼上添加以下部分就足夠了。
def randomString(length): return "".join(random.choice(string.letters + string.digits) for i in xrange(length)) XCSRF2Cookie = "csrf2_token_%s" % "".join(randomString(8)) XCSRF2Token = "".join(randomString(24)) XCSRFToken = "".join(randomString(24)) cookie = "csrftoken=%s; %s=%s" % (XCSRFToken, XCSRF2Cookie, XCSRF2Token) post_headers = {"User-Agent": user_agent, "Referer": "https://accounts.coursera.org/signin", "X-Requested-With": "XMLHttpRequest", "X-CSRF2-Cookie": XCSRF2Cookie, "X-CSRF2-Token": XCSRF2Token, "X-CSRFToken": XCSRFToken, "Cookie": cookie }
至此登錄功能初步實(shí)現(xiàn)。
分析資源鏈接登錄成功后,我們只需要get到資源頁(yè)面的內(nèi)容,然后過(guò)濾出自己需要的資源鏈接就行了。資源頁(yè)面的地址很簡(jiǎn)單,為https://class.coursera.org/name/lecture,其中name為課程名稱。比如對(duì)于課程comnetworks-002,資源頁(yè)面地址為https://class.coursera.org/comnetworks-002/lecture。
抓取到頁(yè)面資源后,我們需要分析html文件,這里選擇使用BeautifulSoup。BeautifulSoup是一個(gè)可以從HTML或XML文件中提取數(shù)據(jù)的Python庫(kù),相當(dāng)強(qiáng)大。具體使用官網(wǎng)上有很詳細(xì)的文檔,這里不再贅述。在使用BeautifulSoup前,我們還得找出資源鏈接的規(guī)律,方便我們過(guò)濾。
其中課程每周的總題目為class=course-item-list-header的div標(biāo)簽下,每周的課程均在class=course-item-list-section-list的ul標(biāo)簽下,每節(jié)課程在一個(gè)li標(biāo)簽中,課程資源則在li標(biāo)簽中的div標(biāo)簽中。
查看了幾門課程之后,發(fā)現(xiàn)過(guò)濾資源鏈接的方法很簡(jiǎn)單,如下:
ppt和ppt資源:用正則表達(dá)式匹配鏈接;
字幕資源:找到title="Subtitles (srt)"的標(biāo)簽,取其href屬性;
視頻資源:找到title="Video (MP4)"的標(biāo)簽,取其href屬性即可。
字幕和視頻也可以用正則表達(dá)式過(guò)濾,不過(guò)用BeautifulSoup根據(jù)title屬性來(lái)匹配,有更好的易讀性。而ppt和pdf資源,沒(méi)有固定的title屬性,只好利用正則表達(dá)式來(lái)匹配。
具體代碼如下:
soup = BeautifulSoup(content) chapter_list = soup.find_all("div", class_="course-item-list-header") lecture_resource_list = soup.find_all("ul", class_="course-item-list-section-list") ppt_pattern = re.compile(r"https://[^"]*.ppt[x]?") pdf_pattern = re.compile(r"https://[^"]*.pdf") for lecture_item, chapter_item in zip(lecture_resource_list, chapter_list): # weekly title chapter = chapter_item.h3.text.lstrip() for lecture in lecture_item: lecture_name = lecture.a.string.lstrip() # get resource link ppt_tag = lecture.find(href=ppt_pattern) pdf_tag = lecture.find(href=pdf_pattern) srt_tag = lecture.find(title="Subtitles (srt)") mp4_tag = lecture.find(title="Video (MP4)") print ppt_tag["href"], pdf_tag["href"] print srt_tag["href"], mp4_tag["href"]下載資源
既然已經(jīng)得到了資源鏈接,下載部分就很容易了,這里我選擇使用curl來(lái)下載。具體思路很簡(jiǎn)單,就是輸出curl resource_link -o file_name到一個(gè)種子文件中去,比如到feed.sh中。這樣只需要給種子文件執(zhí)行權(quán)限,然后運(yùn)行種子文件即可。
為了便于歸類課程資源,可以為課程每周的標(biāo)題建立一個(gè)文件夾,之后該周的所有課程均下載在該目錄下。為了方便我們快速定位到每節(jié)課的所有資源,可以把一節(jié)課的所有資源文件均命名為課名.文件類型。具體的實(shí)現(xiàn)比較簡(jiǎn)單,這里不再給出具體程序了??梢钥匆幌乱粋€(gè)測(cè)試?yán)又械膄eed.sh文件,部分內(nèi)容如下:
mkdir "Week 1: Introduction, Protocols, and Layering" cd "Week 1: Introduction, Protocols, and Layering" curl https://d396qusza40orc.cloudfront.net/comnetworks/lect/1-readings.pdf -o "1-1 Goals and Motivation (15:46).pdf" curl https://class.coursera.org/comnetworks-002/lecture/subtitles?q=25_en&format=srt -o "1-1 Goals and Motivation (15:46).srt" curl https://class.coursera.org/comnetworks-002/lecture/download.mp4?lecture_id=25 -o "1-1 Goals and Motivation (15:46).mp4" curl https://d396qusza40orc.cloudfront.net/comnetworks/lect/1-readings.pdf -o "1-2 Uses of Networks (17:12).pdf" curl https://class.coursera.org/comnetworks-002/lecture/subtitles?q=11_en&format=srt -o "1-2 Uses of Networks (17:12).srt" curl https://class.coursera.org/comnetworks-002/lecture/download.mp4?lecture_id=11 -o "1-2 Uses of Networks (17:12).mp4"
到這里為止,我們已經(jīng)成功完成爬取Coursera課程資源的目標(biāo),具體的代碼放在gist上。使用時(shí),我們只需要運(yùn)行程序,并把課程名稱作為參數(shù)傳遞給程序就可以了(這里的課程名稱并不是整個(gè)課程的完整名字,而是在課程介紹頁(yè)面地址中的縮略名字,比如Computer Networks這門課,課程名稱是comnetworks-002)。
其實(shí),這個(gè)程序可以看做一個(gè)簡(jiǎn)單的小爬蟲程序了,下面粗略介紹下爬蟲的概念。
一點(diǎn)都不簡(jiǎn)單的爬蟲關(guān)于什么是爬蟲,wiki上是這樣說(shuō)的
A Web crawler is an Internet bot that systematically browses the World Wide Web, typically for the purpose of Web indexing.
爬蟲的總體架構(gòu)圖如下(圖片來(lái)自wiki):
簡(jiǎn)單來(lái)說(shuō),爬蟲從Scheduler中獲取初始的urls,下載相應(yīng)的頁(yè)面,存儲(chǔ)有用的數(shù)據(jù),同時(shí)分析該頁(yè)面中的鏈接,如果已經(jīng)訪問(wèn)就pass,沒(méi)訪問(wèn)的話加入到Scheduler中等待抓取頁(yè)面。
當(dāng)然有一些協(xié)議來(lái)約束爬蟲的行為規(guī)范,比如許多網(wǎng)站都有一個(gè)robots.txt文件來(lái)規(guī)定網(wǎng)站哪些內(nèi)容可以被爬取,哪些不可以。
每個(gè)搜索引擎背后都有一個(gè)強(qiáng)大的爬蟲程序,把觸角伸到網(wǎng)絡(luò)中的所有角落,不斷去收集有用信息,并建立索引。這種搜索引擎級(jí)別的爬蟲實(shí)現(xiàn)起來(lái)非常復(fù)雜,因?yàn)榫W(wǎng)絡(luò)上的頁(yè)面數(shù)量太過(guò)龐大,只是遍歷他們就已經(jīng)很困難了,更不要說(shuō)去分析頁(yè)面信息,并建立索引了。
實(shí)際應(yīng)用中,我們只需要爬取特定站點(diǎn),抓取少量的資源,這樣實(shí)現(xiàn)起來(lái)簡(jiǎn)單很多。不過(guò)仍然有許多讓人頭疼的問(wèn)題,比如許多頁(yè)面元素是javascript生成的,這時(shí)候我們需要一個(gè)javascript引擎,渲染出整個(gè)頁(yè)面,再加以過(guò)濾。
更糟糕的是,許多站點(diǎn)都會(huì)用一些措施來(lái)阻止爬蟲爬取資源,比如限定同一IP一段時(shí)間的訪問(wèn)次數(shù),或者是限制兩次操作的時(shí)間間隔,加入驗(yàn)證碼等等。絕大多數(shù)情況下,我們不知道服務(wù)器端是如何防止爬蟲的,所以要想讓爬蟲工作起來(lái)確實(shí)挺難的。
參考:
github:coursera-dl/coursera
github:coursera-downloader
python爬取頁(yè)面元素失敗
Wiki: Web crawler
Python 爬蟲如何入門學(xué)習(xí)?
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/37388.html
摘要:該課程旨在面向有抱負(fù)的工程師,從人工智能的基本概念入門到掌握為人工智能解決方案構(gòu)建深度學(xué)習(xí)模型所需技能。 showImg(https://segmentfault.com/img/bVbkP5z?w=800&h=664); 作者 | Jo Stichbury翻譯 | Mika本文為 CDA 數(shù)據(jù)分析師原創(chuàng)作品,轉(zhuǎn)載需授權(quán) 前言 如今人工智能備受追捧,由于傳統(tǒng)軟件團(tuán)隊(duì)缺乏AI技能,常常會(huì)...
摘要:是你學(xué)習(xí)從入門到專家必備的學(xué)習(xí)路線和優(yōu)質(zhì)學(xué)習(xí)資源。的數(shù)學(xué)基礎(chǔ)最主要是高等數(shù)學(xué)線性代數(shù)概率論與數(shù)理統(tǒng)計(jì)三門課程,這三門課程是本科必修的。其作為機(jī)器學(xué)習(xí)的入門和進(jìn)階資料非常適合。書籍介紹深度學(xué)習(xí)通常又被稱為花書,深度學(xué)習(xí)領(lǐng)域最經(jīng)典的暢銷書。 showImg(https://segmentfault.com/img/remote/1460000019011569); 【導(dǎo)讀】本文由知名開源平...
摘要:普通程序員,如何轉(zhuǎn)向人工智能方向,是知乎上的一個(gè)問(wèn)題。領(lǐng)域簡(jiǎn)介,也就是人工智能,并不僅僅包括機(jī)器學(xué)習(xí)。但是,人工智能并不等同于機(jī)器學(xué)習(xí),這點(diǎn)在進(jìn)入這個(gè)領(lǐng)域時(shí)一定要認(rèn)識(shí)清楚。 人工智能已經(jīng)成為越來(lái)越火的一個(gè)方向。普通程序員,如何轉(zhuǎn)向人工智能方向,是知乎上的一個(gè)問(wèn)題。本文是對(duì)此問(wèn)題的一個(gè)回答的歸檔版。相比原回答有所內(nèi)容增加。 目的 本文的目的是給出一個(gè)簡(jiǎn)單的,平滑的,易于實(shí)現(xiàn)的學(xué)習(xí)方法,幫...
閱讀 2544·2021-11-18 10:02
閱讀 2027·2021-11-09 09:45
閱讀 2510·2021-09-26 09:47
閱讀 1070·2021-07-23 10:26
閱讀 1110·2019-08-30 15:47
閱讀 3390·2019-08-30 15:44
閱讀 1008·2019-08-30 15:43
閱讀 915·2019-08-29 13:50