V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
anhkgg
V2EX  ›  Python

pylogin 系列之搞定百度统计

  •  
  •   anhkgg ·
    anhkgg · 2017-08-22 00:07:48 +08:00 · 2542 次点击
    这是一个创建于 2432 天前的主题,其中的信息可能已经有所发展或是发生改变。

    概述

    这次分析的百度统计登录接口,算是这几个中最简单的了。

    但是学到了一个新东西,叫做 js 模板,搞 web 的同学应该知道,我这种 web 半吊子第一次见,非常有意思。

    工具:

    1. chrome/firefox
    2. f12,network
    3. python:requests、re
    

    登录接口

    打开百度统计首页https://tongji.baidu.com/web/welcome/login,点开登录框,f12。尝试输入之后,查看发送的数据。

    Request URL:https://cas.baidu.com/?action=login
    Request Method:POST
    Status Code:200 OK
    
    appscope[]:6
    appscope[]:7
    appscope[]:12
    appid:12
    entered_login:anhkgg //名字
    entered_password:1111111111111111 //密码
    entered_imagecode:9mxm //验证码
    charset:utf-8
    fromu:https://tongji.baidu.com/web/welcome/loginback
    selfu:https://tongji.baidu.com/web/welcome/login
    senderr:1
    

    除了上面注释的需要输入的三个字段,其他字段意义都不明确,偷点懒,多次尝试后发现其他字段不会变化,那么就用固定值了。

    点击验证码,看到网络,拿到获取验证码的请求,key 使用 10 位时间戳。

    GET https://cas.baidu.com/?action=image&key=1503151305
    

    所以登录接口就出来了,vcode需要人工输入。

    url = 'https://cas.baidu.com/?action=image&key=' + time_stamp(10)
    r = self.s.get(url)
    
    payload = {
        'appscope[]':6,
        'appscope[]':7,
        'appscope[]':12,
        'appid':12,
        'entered_login':name,
        'entered_password':pwd,
        'entered_imagecode':vcode,
        'charset':'utf-8',
        'fromu':'https://tongji.baidu.com/web/welcome/loginback',
        'selfu':'https://tongji.baidu.com/web/welcome/login',
        'senderr':1,
        }        
    url = 'https://cas.baidu.com/?action=login'
    r = self.s.post(url, data = payload)
    

    接着看看登录返回状态,如果失败了,返回数据中包含如下数据:

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta http-equiv="ReFresh" content="0; url=https://tongji.baidu.com/web/welcome/login?fromu=https%3A%2F%2Ftongji.baidu.com%2Fweb%2Fwelcome%2Floginback&e=%E7%94%A8%E6%88%B7%E5%90%8D%E5%AF%86%E7%A0%81%E9%94%99%E8%AF%AF&un=anhkgg&aid=12&errno=132" />
    <title>正在处理...</title>
    </head>
    <body>
        <script>                
        var url="https://tongji.baidu.com/web/welcome/login?fromu=https%3A%2F%2Ftongji.baidu.com%2Fweb%2Fwelcome%2Floginback&e=%E7%94%A8%E6%88%B7%E5%90%8D%E5%AF%86%E7%A0%81%E9%94%99%E8%AF%AF&un=anhkgg&aid=12&errno=132";
        location.href=url;
        </script>
    
    </body>
    </html>
    

    然后浏览器加载该 url,显示错误提示信息

    Request URL:https://tongji.baidu.com/web/welcome/login?fromu=https%3A%2F%2Ftongji.baidu.com%2Fweb%2Fwelcome%2Floginback&e=%E7%94%A8%E6%88%B7%E5%90%8D%E5%AF%86%E7%A0%81%E9%94%99%E8%AF%AF&un=anhkgg&aid=12&errno=132
    Request Method:GET
    
    fromu:https://tongji.baidu.com/web/welcome/loginback
    e:用户名密码错误
    un:anhkgg
    aid:12
    errno:132
    

    其中e是错误提示信息,errno 是错误号。

    登录成功返回数据如下,没有e错误信息。

    <script>                
    var url="http://cas.baidu.com/?action=check&appid=12&u=https%3A%2F%2Ftongji.baidu.com%2Fweb%2Fwelcome%2Floginback%3Fcastk%3Dc4086gh7e82166251d451&fromLogin=1";
    location.href=url;
    </script>
    

    那么就可以先通过正则拿到 url,通过搜索 url 是否有e判断是否登录成功,并且拿到提示信息。成功则继续访问该 url 跳转到成功页面,获取其他需要的信息。

    pattern = re.compile(r'var url="(.*?)";')
    cont = re.search(pattern, r.content)
    url = cont.group(1)
    pattern = re.compile(r'e=(.*?)&un=')
    cont = re.search(pattern, url)
    if cont != None:
        r = urllib.unquote(cont.group(1)) #失败
        return utf2gbk(r)        
    
    r = self.s.get(url) # 成功
    

    js 模板

    这里比较意思的是使用的 js 模板来生成登录表单。

    具体 js 模板使用看这里

    <script id="LoginTemplate" type="text/template">
    
        <div id="LoginContainer" class="login-dialog">
            <div id="TopTmp">&nbsp;</div>
    
            if (this.isIco == 1) {
            <div id="LoginMain" class="ico-login clearfix">
                <div class="visitor-login-tab" id="LoginTab">请输入查看密码</div>
                <div id="LoginInput" class="login-input">
                    if (this.errMsg) {
                    <div id="ErrorTip" class="error">#{this.errMsg}</div>
                    }
                    ...
                </div>
            </div>
            }
            else {
            <div id="LoginMain" class="login-main">
                
                    <form method="post" action="#{this.loginAction}">
                        <input type="hidden" value="12" id="Appid" name="appid">
                        ...
                        <input type="hidden" value="#{this.selfUrl}" name="selfu" />
                        <input type="hidden" value="1" name="senderr" />
                    </form>
                </div>
            </div>
            }
        </div>
        <div class="dialog-bottom-bg"></div>
    </script>
    

    从上面代码中可以看到,某些标签的值使用了#{this.xxx}这样的语法,不是直接填入的具体内容,更加灵活,扩展更容易。

    然后在点击登录按钮之后,通过函数格式化一个全局定义的变量来生成的登录表单。具体如下:

    //全局数据,用于替换表单中的 this.xxx
    <script type="text/javascript">
    VAR = {
        webMasterRegister: "https://tongji.baidu.com/web/register",
        customRegister: "https://u.baidu.com/ucweb/?module=Reguser&controller=reg&action=index&appid=3",
        union_forget: "http://union.baidu.com/findPassword!input.action",
        shifen_forget: "https://aq.baidu.com/new/#/findpwd",
        uc_forget: "https://aq.baidu.com/new/#/findpwd",
        waiting_img_src: "/web/img/loadingImage.gif",
        app_id: "0",
        errMsg: "",
        loginUrl: "/web/welcome/login",
        loginAction: "https://cas.baidu.com/?action=login",
        userName: "",
        authCode: "https://cas.baidu.com/?action=image&key=1503151305",
        registerUrl: "/web/register",
        fromUrl: "https://tongji.baidu.com/web/welcome/loginback",
        selfUrl: "https://tongji.baidu.com/web/welcome/login",
        isIco: "0",
        webmasterUserNum: "2097176",
        customerUserNum: "2270927",
        mtjUserNum: "2262130"
    };
    </script>
    

    然后在 login.js 中,通过下面的函数来初始化表单,并且显示。

    其中n.format("LoginTemplate", VAR)用于格式化 VAR 定义的数据到表单的数据中。

    , h = function() {
        var e = t(".login-trigger").eq(0);
        e.on("click", function() {
            s || (s = new i({
                width: 345,
                isModal: !0,
                titleText: "",
                isSingle: !0,
                content: n.format("LoginTemplate", VAR) //初始化登录表单数据
            }),
            loginController.init()),
            s.show()
        });
    

    而在 format 具体如何替换的,就随意实现了,这里就不在具体分析,有兴趣跟着分析的同学可以去看看 common.js 中的代码。

    总结

    百度统计接口非常简单,密码未做变换,使用 https。

    登录之后具体做什么也不在分析。

    预告下次做百度主站的登录分析,简单看了下,非常...复杂!

    博客原文:https://anhkgg.github.io/pylogin-baidutongji-login-analyze/

    1 条回复    2017-08-22 15:51:41 +08:00
    hemoely
        1
    hemoely  
       2017-08-22 15:51:41 +08:00
    楼主 666,实践才能巩固学习
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   5347 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 08:55 · PVG 16:55 · LAX 01:55 · JFK 04:55
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.