醫學教育網批量資源下載程序之——登錄

首先申明
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

相關文章
相關標籤/搜索