首先申明
javascript
To www.med66.com網站設計師:我只是想批量下載已花錢購買的資源罷了,沒有惡意。php
12-18html
今晚接到老姐的電話,說她已在「醫學教育網」訂購了很多視頻,要我幫她將全部的視頻都下載下來。
我看了一下,裏面有24門科目,每門科目有40多節。要我手動一個一個下,還不如讓我去死。
這種重複的事情仍是讓程序來作吧!這裏開一篇博客直播編寫的過程。java
被爬網址:http://www.med66.com/python
前幾天我剛作完一個Qihuiwang的爬蟲軟件。此次我評估了一下,此次要作的視頻下載爬蟲程序比上次又有新的挑戰:git
(1)要處理登錄的過程,上一個不須要登錄就能夠直接爬。此次必需要登錄才行。涉及到post數據表的過程json
(2)要識別JavaScript程序。我看一下,在我獲取網頁的那個按鈕上寫的是 onclick="goDownload('700914', ' ')。這個要轉換進行轉換成url地址centos
(3)下載須要記載哪些文件已經下載了,以避免每次啓動程序都從頭開始下載。這是不合理的。瀏覽器
(4)下載的文件要以課程進行目錄組織。cookie
網站路徑以下:
登錄頁面 -(登錄)-> 學員課程頁面 -(進入課程)-> 目錄頁面 -(下載中心)-> 下載頁面 --> 小節視頻
好,明天開搞,盡情關注!
12-19
今晚加了個班,很累了,何況容忍我好好休息一晚。明日再整!
12-22
週末跟一位作數據挖掘的朋友聊起這事兒。我那朋友說,驗證碼這事真很差越過。每一個網站的驗證碼都千奇百怪的,如今沒有一個統一的識別程序。不過有另外一個方法,反正我有賬號密碼,那麼人工來識別。登錄成功以後,就以登錄後的cookie與網站進行交互。
怎麼實現模擬登錄?我查了一下,網上有不少範例:
http://blog.csdn.net/lmh12506/article/details/7818306
http://www.jbxue.com/article/python/22981.html
http://www.oschina.net/code/snippet_16840_2003
12-23
今天研究了一下 www.med66.com這個網站。我用的是firefox瀏覽器,安裝了firebug插件的。
打開該網頁,找到登錄模塊所對應的網頁源碼:
發現 src="/global/login.html?t=1419344700706",除了這個就沒有別的了。這說明登錄那邊的代碼應該是在 /global/login.html 中描述的。
因而,我訪問 http://www.med66.com/global/login.html,咱們能夠看到這就是登錄窗口。
想必這就是所謂的登錄頁面了。詳細看一下這個表單,它分三個部分:學員代碼、密碼、驗證碼。
在驗證碼那裏,還隱藏了很多的隱性表單數據:
這些數據在提交表單的時候也必須一同提交。
登錄按鈕:
12-26
昨天去了趟香港,太累了,休息一晚接着整。網絡這方面,我是一個菜鳥,有大鳥漂過,不防指導指導。
上次找到了登錄界面,並獲得了源碼。咱們須要從form中提取出表單。
表單內容:
username=用戶名
passwd=密碼
randcode=驗證碼
turl=http://member.med66.com/member/loginDispose.shtm?gotoURL=http://www.med66.com/global/login.html
furl=http://www.med66.com/global/login.html?validateLogin=n
type=JS
domain=@med66.com
randid=login
cmd=ucAuth
retfield=username
發送方式:post,發送到:http://portal.cdeledu.com/auth/index.php
登錄觸發:
我查找了「登錄」按鈕執行函數"loginSm()"函數,以下爲該函數的主體內容:
function loginSm(){ var flag = checkdata(); if(flag == false){ return; } var v_username = $.trim($("#username").val()); var v_passwd = $.trim($("#passwd").val()); var v_randcode = $.trim($("#randcode").val()); $("#username").val(v_username); $("#passwd").val(v_passwd); $("#randcode").val(v_randcode); $.getJSON("http://"+portal+"/auth/index.php?cmd=ucAuth&type=JSON&randid=login&username="+v_username+"&passwd="+v_passwd+"&domain=@med66.com&randcode="+v_randcode+"&jsonpCallback=?", function(json){ var code = json.code; var sid = json.sid; if(code == 0){ document.getElementById("passwd_span").className="r"; document.getElementById("username_span").className="r"; document.getElementById("randcode_span").className="r"; $("#submit_code").val(code); $("#submit_sid").val(sid); $("#submit_form").submit(); }else{ chgverify(); document.getElementById("username_span").className="w"; document.getElementById("passwd_span").className="w"; document.getElementById("randcode_span").className=""; document.getElementById("username").focus(); document.getElementById("randcode").value=""; } }); }
我不會javascript,但看到上面的代碼也能猜到點意思:
(1)從網頁中獲取"username", "passwd", "randcode"的值,並去除先後空格後賦值給javascript變量v_username, v_passwd, v_randcode。
var v_username = $.trim($("#username").val()); var v_passwd = $.trim($("#passwd").val()); var v_randcode = $.trim($("#randcode").val()); $("#username").val(v_username); $("#passwd").val(v_passwd); $("#randcode").val(v_randcode);
(2)用.getJSON()函數訪問網址,獲得json數據。這個網址是由變量拼接出來的。
$.getJSON("http://"+portal+"/auth/index.php?cmd=ucAuth& ... &jsonpCallback=?", function(json){ ... } )
那麼問題來了:portal的值是多少?
上面的代碼,先獲得流覽器的類型到ua,
若是是safari瀏覽器,那麼portal="portal.med66.com",並修改autoSubmitForm與login_form表單的action屬性爲"http://portal.cdeledu.com/auto/index.php"
個人瀏覽器不是safari,那麼portal就應該是:portal.cdeledu.com
若是用戶名爲:cqy,密碼爲:123,驗證碼爲:4996,那麼要訪問的網址爲:
http://portal.cdeledu.com/auth/index.php?cmd=ucAuth&type=JSON&randid=login&username=cqy&passwd=123&domain=@med66.com&randcode=4996&jsonpCallback=?
我在瀏覽器的地址欄輸入上述地址,進入。獲得的返回是:
說是驗證碼不對。我這個驗證碼是從另外一個頁面獲得了。
是否是必須得在驗證碼做在同一個頁面提交才行?我試一下在剛剛驗證碼做在的頁面進行訪問上述網址。
果真是這樣的。你們看,返回的code爲0,說明成功了。並且還有sid數據。
這是爲啥呢?難不成,在提交的時候,不一樣的頁面還有一個id碼?
因而我想研究一下驗證碼,看到網頁:http://www.cnd8.com/news/news/13778.htm
說驗證碼都是存放在cookie裏面的。因而我打開 http://www.med66.com/global/login.html 並複製一個一樣的頁面。這兩個頁網除了驗證碼不同,其它都同樣。而後我打開firebug,查看它們之間的cookie的差異。我反覆比較,沒有發現有任何的區別。得出的結論那就是驗證碼相關的信息並無存放在cookie裏。
那除了cookie外,那就是別的什麼識別碼了。
後來經反覆驗證,原來是以前我網址寫錯了或是驗證碼失效了。其實沒有限制,只要是用firefox打開的登錄界面,但用firefox去訪問那個網址,都能成功,不論是不是同個頁面,或同個窗口。同一種瀏覽器是能夠的,但不一樣的不行。好比說:我從firefox上獲得的驗證碼,把網址放在konqueror中去訪問,就會報驗證碼錯誤。反之在firefox上去訪問就OK。konqueror得驗證碼,firefox訪問也不行。(好像是cookie引發的問題)
既然這樣,那就沒什麼大的問題。
(3)將json數據交給最後的回調函數進行分析處理。
var code = json.code; var sid = json.sid; if(code == 0){ document.getElementById("passwd_span").className="r"; document.getElementById("username_span").className="r"; document.getElementById("randcode_span").className="r"; $("#submit_code").val(code); $("#submit_sid").val(sid); $("#submit_form").submit();
若是成功,json.code應該爲0。而後將code, sid填寫到網頁的submit_code, 與submit_sid元素的value中去。並觸發表單的提交事件。
如上就是第二次要提交的表單,看起來很是簡單。用python很好模擬的。
好傢伙!原來提交過程還要與網站進行了次交互才行。
12-29
驗證碼是怎麼獲得了?
每次訪問 http://portal.cdeledu.com/auth/randcode.php?id=login 都能獲得一個新的碼證碼圖片。而這個驗證碼能夠用來獲取json上面數據的(這個我驗證過)。能夠經過wget獲取這個圖片。
好,既然瞭解得差很少了。下面咱們就來用python開始模擬這個登錄過程。
import urllib import urllib2 import cookielib
#登錄網站,並返回登錄後的頁面 def login(): username = 'hevake' password = 'abc123' #配置opener,要使用到cookie install_opener() #獲取驗證碼圖片,展現並讓用戶輸入驗證碼 randcode = get_randcode() if randcode != None: #獲取json數據,提取code, sid json_data = get_json(username, password, randcode) if json_data != None: if json_data['code'] == '0': #用code, sid提交submit_form表單,返回登錄後的頁面 page = submit_form(json_data['code'], json_data['sid']) print('登錄成功') return page else: print(json_data['msg']) else: print('獲取json失敗') else: print('獲取驗證碼失敗') pass if __name__ == '__main__': page = login() if page != None: f = open('login.html', 'w') f.write(page) f.close()
很少解釋,上面只是粗略的登錄步驟,每步還須要實現。
第一步:install_opener()
因爲登錄須要用到cookie,因此要使用帶cookie的opener,以下爲 install_opener()函數的實現:
#配置urllib2的opener def install_opener(): cj = cookielib.CookieJar() processor = urllib2.HTTPCookieProcessor(cj) opener = urllib2.build_opener(processor) urllib2.install_opener(opener) pass
配置了opener以後,後面用urllib2.urlopen()就是用的新設置的opener進行操做了。
相關文章:urllib2,cookielib
第二步:get_randcode()
前面提到了,獲取驗證碼其實就是訪問網址 http://portal.cdeledu.com/auth/randcode.php?id=login,那麼如今要作的是用python從這個url上獲取圖片,並展現出來。
相關的文章:python批量下載圖片,python下載網頁圖片
今天太晚了,明日接着搞!
2015 1-3
獲取驗證碼流程大概以下:
def get_randcode(): #TODO # 獲取隨機驗證碼 # http://portal.cdeledu.com/auth/randcode.php?id=login # 將驗證碼展示出來,讓用戶輸入 randcode = raw_input('請輸入驗證碼:') return randcode pass
本人如今還不知道怎麼讓計算機自動識別驗證碼,那就人工輸入吧!反正又不是搶火車票。
用一個專門的函數專門用於下載驗證碼圖片:
def get_randcode_jpeg(): randcode_url = 'http://portal.cdeledu.com/auth/randcode.php?id=login' respond = urllib2.urlopen(randcode) return respond.read() pass
這個函數的功能就是訪問驗證碼的url,獲取圖片。
而後,該怎麼顯示出來呢?
(1)保存到本地圖片文件,而後調用系統應用打開。
(2)用什麼別的圖形組件打開。
python的GUI界面,我知道的有PyQt4, Tkinter。PyQt4功能是強大,可是安裝很糟心!Tkinter相對比較簡單。那就選用Tkinter吧。
驗證碼的文件是jpeg,而Tkinter顯不出來,而網上相關的資源很少。那仍是用別的看看,wxPython。
算了,蠻拆騰的,就爲了顯示個圖片還得去學wxPython,Tkinter。仍是採用方案(2)吧!查了一下,centos的圖片瀏覽器命令是 eog,那就用它算了。
def show_randcode_jpeg(): jpeg_context = get_randcode_jpeg() jpeg_file = 'randcode.jpeg' f = open(jpeg_file, 'w') f.write(jpeg_context) f.close() os.system('eog "%s"' % jpeg_file)
上面這個函數能用圖片瀏覽器達到顯示驗證碼的效果。就這樣吧!之後有時間再研究wxPython。
那麼對於的 get_randcode() 函數要寫成這樣:
def get_randcode(): #TODO # 獲取隨機驗證碼 # http://portal.cdeledu.com/auth/randcode.php?id=login # 將驗證碼展示出來,讓用戶輸入 show_randcode_jpeg() randcode = raw_input('請輸入驗證碼:') return randcode pass
好了!如今驗證碼有了。下一步就是:get_json()
第三步:get_json()
上面,咱們研究過,get_json()這個過程實際上是合成一個url,並從這個url獲取json數據。
#合成網址,獲取json數據並提取sid碼 def get_json(username, password, randcode): url_template = 'http://portal.cdeledu.com/auth/index.php?cmd=ucAuth&type=JSON&randid=login&username=%s&passwd=%s&domain=@med66.com&randcode=%s&jsonpCallback=?' #合成完整的url url_full = url_template % (username, password, randcode) respond = urllib2.urlopen(url_full) context = respond.read() print(context[2:-1]) json_data = json.loads(context) code = json_data.code sid = json_data.sid return code, sid
上面的代碼可以成功獲取json數據,並能成功得到sid碼。可是在用json提取數據的時候出錯。
查得緣由是 python json 庫不支持json中的key沒有雙引號括起的狀況。如:{aa:123},必須得是:{"aa":123}。還有,python json不能處理單引號。
解決辦法之一就是用正則式re中的sub進行替換。
>>> print ss {code:'0',sid:'vfuuf6emf80gpoet52k23pigm6'} >>> sd = regex.sub(r'(\w+):', r'"\1":', ss) >>> print sd {"code":'0',"sid":'vfuuf6emf80gpoet52k23pigm6'}
那再修改一下 get_json() 函數:
def parser_json(json_context): #給json的key加雙引號,否則python json解釋會報錯 json_context = re.sub(r'(\w+):', r'"\1":', json_context) #將單引號轉換成雙引號 json_context = re.sub(r"\'", r'"', json_context) json_data = json.loads(json_context) return json_data #合成網址,獲取json數據並提取sid碼 def get_json(username, password, randcode): url_template = 'http://portal.cdeledu.com/auth/index.php?cmd=ucAuth&type=JSON&randid=login&username=%s&passwd=%s&domain=@med66.com&randcode=%s&jsonpCallback=?' #合成完整的url url_full = url_template % (username, password, randcode) respond = urllib2.urlopen(url_full) context = respond.read()[2:-1] #去除開頭的?(與後面的) json_data = parser_json(context) return json_data
好了,經過屢次調試,經過了登錄這個過程。
第四步:提交 submit_form表單
前面研究了,submit_form表單的提交就是:
將 code, sid 數據 POST 到 http://member.med66.com/member/loginDispose.shtm
關於表單的提交網上有太多博文了,如: http://developer.51cto.com/art/201003/186364.htm
OK,let us go !
#提交submit_form表單到 http://member.med66.com/member/loginDispose.shtm def submit_form(code, sid): url = 'http://member.med66.com/member/loginDispose.shtm' url_data = urllib.urlencode({'code':code, 'sid':sid}) print(url_data) request = urllib2.Request(url, data=url_data) respond = urllib2.urlopen(request) page = respond.read() return page
第五步:測試
上面,咱們將各個步驟都一一實現了。如今咱們來驗證一下是否成功登錄。測試結果是將最後提交 submit_form 獲得的頁面保存在 login.html 文件中。
咱們用firefox打開這個文件:
這說明,登錄已經成功了!
完整的代碼已提交到 osc 代碼庫了:http://git.oschina.net/hevake_lcj/Med66VideoDownloader