医学教育网批量资源下载程序之——登陆

首先申明

To www.med66.com网站设计师:我只是想批量下载已花钱购买的资源罢了,没有恶意。



12-18

今晚接到老姐的电话,说她已在“医学教育网”订购了不少视频,要我帮她将所有的视频都下载下来。
我看了一下,里面有24门科目,每门科目有40多节。要我手动一个一个下,还不如让我去死。
这种重复的事情还是让程序来做吧!这里开一篇博客直播编写的过程。

被爬网址:http://www.med66.com/

前几天我刚做完一个Qihuiwang的爬虫软件。这次我评估了一下,这次要做的视频下载爬虫程序比上次又有新的挑战:

(1)要处理登陆的过程,上一个不需要登陆就可以直接爬。这次必须要登陆才行。涉及到post数据表的过程

(2)要识别JavaScript程序。我看一下,在我获取网页的那个按钮上写的是 onclick="goDownload('700914', ' ')。这个要转换进行转换成url地址

(3)下载需要记载哪些文件已经下载了,以免每次启动程序都从头开始下载。这是不合理的。

(4)下载的文件要以课程进行目录组织。

网站路径如下:

登陆页面 -(登陆)-> 学员课程页面 -(进入课程)-> 目录页面 -(下载中心)-> 下载页面 --> 小节视频

好,明天开搞,尽情关注!

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

时间: 2024-08-01 10:47:48

医学教育网批量资源下载程序之——登陆的相关文章

医学教育网批量资源下载程序之——获取下载列表

从网站上下载资源可分为两步: (1)通过遍历网站的方试,生成资源列表. (2)根据列表一一下载资源. 资源列表是一个树状结构: 前面已完成了login()函数,现在我们可以调用这个函数成功登陆并获得课程列表页面. 可以从这个页面获取课程名称与课程ID. 捕获课程名称与课程ID可以用正则式进行. 为了方便调试正则式,我们直接用我们在login.py中下载下来的login.html进行分析.写如下脚本来调试正则式: #!/usr/bin/env python #encoding=utf-8 impo

PHP多线程批量采集下载美女图片的实现代码

使用curl的多线程,另外curl可以设置请求时间,遇到很慢的url资源,可以果断的放弃,这样没有阻塞,另外有多线程请求,效率应该比较高 下面是代码实现: /**        * curl 多线程        * @author http://www.lai18.com       * @param array $array 并行网址        * @param int $timeout 超时时间       * @return mix        */   public functi

让迅雷7支持批量离线下载功能

    迅雷离线下载为我们提供了高速和稳定的下载体验,属于迅雷会员的"杀手级应用".不过每次去迅雷中单击"离线下载"按钮实在是太麻烦,只要稍作变通,就可以在迅雷7中享受到批量离线下载功能.     一.复制下载地址     首先从it下载网上下载并安装最新版本的迅雷7,然后单击左侧的"正在下载"按钮,在右侧窗格中配合Ctrl或Shift键选中多个资源,右击,选择"复制网址到剪切板"命令将这些资源的下载地址复制到剪贴板中(如图1

androd资源下载-andord下载资源存放可写路径和sd卡路径问题

问题描述 andord下载资源存放可写路径和sd卡路径问题 我的app使用到过程中需要从网上下载一些图片,起初我是放在app的可写路径下,后来发现资源稍微大一点该路径就无法放下了,大概10单20m的图片压缩包,在新机器上可写全部放得下,但是旧一点的机子经常下载不完,但是内部存储空间尚有盈余的!后来我把资源下载到sd卡上完全没问题,但是放到sd卡上有个弊端,打开相册可以把我的资源图片一览无余,这样用户很可能把程序所依赖的资源给删除掉!我资源放在的sd卡目录是:/storage/emulated/0

与派网络专业提供Exchange产品开发和技术服务|技术文章|资源下载|源码提供www.yupai.net

问题描述 与派办公技术支持网技术支持网http://www.yupai.net(WebMAIL-RTC-MEET-OA-EIP邮局-实时通讯-视频会议-办公自动化-信息门户)基于WindowsServer.ActiveDirectory.ExchangeServer.SharepointServer.CommunicationsServer.LiveMeeting技术,致力于办公自动化系统OA.实时通讯RTC.视频会议MEET.工作流WORKFLOW.企业邮局MAIL.企业信息门户EIP.内容管

实用又好玩 同步推iOS资源下载工具使用体验

硅谷网12月19日讯 前不久小编经同事推荐,安装了"同步推"这款iOS平台的资源下载工具.虽说现在类似工具非常多, 不过同步推海量的iOS资源储备.实用又好玩的功能配备,还是给小编留下了深刻的印象.好工具就是要和大家一起分享,下面就随小编一起来体验下吧!     下载"同步推",多种方法任你选     同步推需要下载到我们的iPhone/iPad中才能使用,因为其是 一款客户端iOS资源下载工具.在软件官网(http://tui.tongbu.com/),我们可以看

Win8系统下载程序失败,提示签名无效该怎么办

  1.打开IE浏览器,点击右上角的工具(齿轮状)--Internet选项; 2.在打开的Internet选项窗口中,切换至"高级"项,取消勾选设置下面的"检查所下载程序的签名",点击确定保存设置.

批量造数据程序

批量造数据程序,适用于压力测试,测试数据准备,SQL查询语句优化(需要大量数据)   可一定之进程数,每个进程中开启线程数,已经进程处理的数据量等     Mr. Neo Chen (netkiller), 陈景峰(BG7NYT) <openunix@163.com> 版权 2011, 2012 http://netkiller.github.com 摘要   下面是我多年积累下来的经验总结,整理成文档供大家参考:   Netkiller Architect 手札 Netkiller Linu

8086 下载-8086(以及类似的intel芯片)如何下载程序

问题描述 8086(以及类似的intel芯片)如何下载程序 我们知道8086用汇编编写的程序是exe文件,那这exe文件是下载到8086内部flash运行还是扩展的flash运行,程序如何下载到flash(通过什么软件,什么接口,什么下载器?),我在网上找了好久也没找到答案,这个问题换一种说法就是咱们的PC的底层BIOS程序是如何下载到芯片里去的?热切盼望您的回答,谢谢!