python模拟登录练习(二)

花了4个晚上终于把模拟登录新浪微博学习完了,相对于知乎迷你登录,微博登录的过程确实难度大了很多,好多知识点都不懂,所以虽然把代码都码了一遍,但很多都是照猫画虎,其实还有很多地方不是十分清楚。代码中写了很多注解(对向我一样的初学者来说应该很有必要),所以看以来不是很简洁,其实源码查不到150行。

希望本文能对向我一样的初学者能起到一点借鉴作用

写以下我这几天自学的过程:
一、第一天直接看xchaoinfo的源码,完全看不懂,好不好。看了15分钟直接放弃,转百度看图文教程,还好网上的教程不算少(其实方法都一样,代码也基本没多少差别,应该起初都出自同一作者),
网上从抓包介绍,好吧又不会,那就学吧~~~
这样一天就过去了,感觉什么都没学到
二、第二天继续抓包,还是没什么进展,期间换了各种抓包工具,都不太会用,感觉网上介绍的看方法用这些抓包工具都没发抓到,这样一天快要过去的时候,想想还是用浏览器的F12吧,这样就用了火狐浏览器(因为之前一致用chrome的F12看源码,也抓不到js文件),下载了firebug,用起来还是一头雾水,晚上想想还是睡觉吧,睡觉前又百度了firebug的用法,本来也没抱希望有什么用,可是突然奇迹发生了,多次尝试了之后找到了教程中的js文件,太晚了,睡觉!!!
自学真的很痛苦,可能很简单的问题会难住好几天。
三、第三天,继续看js文件,抓post的各种属性,看着教程试着理解密码加密的方法,差不多的时候继续回去啃源码,中间碰到不会的模块google和自己用idle一点点测试,理解个大概继续往下啃,啃到一般就睡觉了。
四、第四天的继续下面的代码,后面的表单登录相对简单,因为之前有相关的学习经验,进过两个小时,终于测试成功了。
下面把源码贴出来给自己留个纪念,也给需要的书友提供一点思路。

"""
新浪微博模拟登录练习,本教程参考了很多网上的教程,很多教程的源码都差不多,本文的代码主要修改自author : "xchaoinfo" github的源码
新浪微博的模拟登录对新手来说比较困难(我就是初学者,以前没有任何编程基础,因为对现在的工作状态不满意,2017年开始自学python)


author : xcaojianhong
qq:1254798548
date:2017.02.21
"""

import time
import base64   #加密模块
import rsa  #加密模块
import binascii #二进制模块
import requests #是的,本次练习还是用的requests库,本意是学习scrapy爬虫,可是找不到模拟登录的详细的教程,只能先搁置了
import re   #正则模块,不太会用
import random   #我不是太理解为甚要rondom模块
import http.cookiejar
from PIL import Image #这个只用到简单的方法,高级的本人也不会
from urllib.parse import quote_plus #涉及编码问题,需要用到该模块


'''
如果没有开启登录保护,不用输入验证码就可以登录
如果开启登录保护,需要输入验证码,PIL库就是用了打开验证码的图片用的,就只是打开,还是需要手动输入的,不是完成自动验证
'''


# 构造 Request headers,这个不解释了,很多基础教程中都会有的,一般网站都有反爬虫的机制,最常见的就是识别是不是浏览器访问的,这个就是用来模拟浏览器的
user_agent = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36'
headers = {
    'User-Agent': user_agent
}

session = requests.session()    #实例化一个session类,session类能够自动处理cookies
session.cookies = http.cookiejar.LWPCookieJar(filename = 'cookies') #用于保存cookies,对爬取后需爬去有用
index_url = "http://weibo.com/login.php" #这个是微博登录的原始的url

# 访问初始页面带上 cookies
try:
    session.get(index_url,headers=headers,timeout=2)        #为什么用延时参数不太清楚,我自己写的话就只写excpet后面的一句
except:
    session.get(index_url,headers=headers)

try:
    input = raw_input #这个自行百度两个input的差异,因为我也不太清楚
except:
    pass


# 下面开始困难了,我光抓包就花了两个晚上才看懂网上讲的方法是怎么回事,期间换了很多抓包的工具,最后还是觉得firebug好用
def get_su(username):
    """
    1.输入账号,会先在js中通过encodeURIComponent进行编码,对应python的urllib.parse.quote_plus方法,问我怎么知道的,是因为源码中的注释说的
    密码的加密方法在ssologin.js文件中,为我为什么知道,网上教程说的,自己分析的话我估计2天都不一定能找到。
    在js里面有这样的代码:
    username = sinaSSOEncoder.base64.encode(urlencode(username))
    urlencode函数中用了encodeURIComponent编码,具体代码就不贴了,自己仔细看能找到。这里能看到用户名用了base64进行加密

    2.其实我也不知道怎么解释,为什么要先对username_quote进行utf-8编码,然后才base64加密,照着写就行了,我在python中测试不编码加密会报错,具体原因不清楚
    """
    username_quote = quote_plus(username)   #详细解释看1
    username_base64 = base64.b64encode(username_quote.encode('utf-8'))  #详细解释看2
    # print(username_base64.decode('utf-8')) 测试用
    return username_base64.decode('utf-8') #解码成utf-8格式字符串


# 预登陆获得 servertime, nonce, pubkey, rsakv 这些属性在后面密码加密过程中需要用到
def server_data(su):
    """
    3.通过抓包分析我们看到,https://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack
    &su=ODg4ODg4OA==&rsakt=mod&checkpin=1&client=ssologin.js(v1.4.18)&_=1487602221124这个url的response是一个json格式的文本,其中包含
    servertime, nonce, pubkey, rsakv这些属性,这正是我们需要的,&su=ODg4ODg4OA==,这个su并不是get_su的返回值,但是相近,直接用get_su的返回值不影响
    另外这里用到了time模块,用了生成时间戳,1487602221124这个就是时间戳

    """

    #get_su()解释了那么多,就是为了获得su,虽然在这里su不那么重要(因为实际预登录url中su并不是完全正确的),但是最后的post表单中需要用到su
    pre_url = "http://login.sina.com.cn/sso/prelogin.php?entry=weibo&callback=sinaSSOController.preloginCallBack&su="\
    + su + "&rsakt=mod&checkpin=1&client=ssologin.js(v1.4.18)&_=" + str(int(time.time() * 1000))    #为什么是这个url,详细请看3
    pre_data_res = session.get(pre_url, headers=headers)

    sever_data = eval(pre_data_res.content.decode("utf-8").replace("sinaSSOController.preloginCallBack", ''))   #这里用repalce替换
    # print(sever_data) 测试用
    return sever_data

# 这个是本次登录中最难的地方,需要用到上面的参数
def get_password(password,servertime,nonce,pubkey):
    """
    具体为什么需要这些参数,还是要仔细分析之前提到的js文件,里面有密码加密的过程
    request.servertime = me.servertime;
            request.nonce = me.nonce;
            request.pwencode = "rsa2";
            request.rsakv = me.rsakv;
            var RSAKey = new sinaSSOEncoder.RSAKey();
            RSAKey.setPublic(me.rsaPubkey, "10001");    #rsaPubkey就是pubkey,js的代码就不贴了
            password = RSAKey.encrypt([me.servertime, me.nonce].join("\t") + "\n" + password)
    """
    rsapublickey = int(pubkey,16)   #如果是16进制,则转化为10进制
    key = rsa.PublicKey(rsapublickey,65537) # '10001'转化为10进制就是65537
    message = str(servertime) + '\t' + str(nonce) + '\n' + str(password)  # 仔细对着js代码慢慢看
    message = message.encode("utf-8")
    #print(message) 测试用
    passwd = rsa.encrypt(message, key)  # 加密,为什么要用rsa,我也不知道,源代码就用的,我也不懂这个方法的具体的用途,反正看着和js的代码相似的,当成是python版本的翻译看
    #print(passwd) 测试用
    passwd = binascii.b2a_hex(passwd)  # 将加密信息转换为16进制,post需要16进制ps,猜测的,对编码不太了解。
    # print(passwd) 测试用
    return passwd

# 获得验证码,并下载后用PIL模块自动打开,没有安装的话手动去爬虫文件的目录中手动打开图片
def get_cha(pic):
    """

    我没有详细分析需要验证码的收获,因为我登录新浪微博基本没有碰到需要验证码的时候
    下面用到了随机数,我不是太理解,既然能用随机数,为什么不能用固定的数字,反正都是我们自己构造的一个数字
    """
    cha_url = "http://login.sina.com.cn/cgi/pin.php?r="
    cha_url = cha_url + str(int(random.random() * 100000000)) + "&s=0&p="
    cha_url = cha_url + pcid
    cha_page = session.get(cha_url, headers=headers)
    with open("cha.jpg", 'wb') as f:
        f.write(cha_page.content)
        f.close()
    try:
        im = Image.open("cha.jpg")
        im.show()
        im.close()
    except:
        print(u"请到当前目录下,找到验证码后输入")

def login(username,password):
    """
    激动人心的时候快要到了,上面作的一些准备工作就是为了获得post的属性
    """
    su = get_su(username)   #获得加密的su
    sever_data = server_data(su)    #获得server_data函数返回的字典
    servertime = sever_data['servertime']
    nonce = sever_data['nonce']
    rsakv = sever_data["rsakv"]
    pubkey = sever_data["pubkey"]
    showpin = sever_data["showpin"] #这个参数的值关系到是否需要输入验证码,0表示不需要,1表示需要
    password_secret = get_password(password,servertime,nonce,pubkey) #获得加密的sp
    # su,sp,servertime,nonce,rsakv,sp属性是变化的,其他的都可以写死
    postdata = {
        'entry':'weibo',
        'gateway':'1',
        'from':'',
        'savestate':'7',
        'useticket':'1',
        'pagerefer':"http://login.sina.com.cn/sso/logout.php?entry=miniblog&r=http%3A%2F%2Fweibo.com%2Flogout.php%3Fbackurl",
        'wsseretry':'servertime_error',
        'vsnf':'1',
        'su':su,
        'service':'miniblog',
        'servertime':servertime,
        'nonce':nonce,
        'pwencode':'rsa2',
        'rsakv':rsakv,
        'sp':password_secret,
        'sr':'1536*864',
        'encoding':'UTF-8',
        'prelt':'105',
        'url':'http://weibo.com/ajaxlogin.php?framelogin=1&callback=parent.sinaSSOController.feedBackUrlCallBack',
        'returntype':'META'
        }

    login_url = 'http://login.sina.com.cn/sso/login.php?client=ssologin.js(v1.4.18)'    #用于登录的url,psot表单提交的url
    if showpin == 0:
        login_page = session.post(login_url, data=postdata, headers=headers)
    else:
        pcid = sever_data["pcid"]
        get_cha(pcid)
        postdata['door'] = input(u"请输入验证码")
        login_page = session.post(login_url, data=postdata, headers=headers)
    login_loop = (login_page.content.decode("GBK")) #看网络抓包,知道是gbk编码
    #print(login_loop)
    pa = r'location\.replace\([\'"](.*?)[\'"]\)' #这里的正则查相关的教程
    loop_url = re.findall(pa, login_loop)[0]
    #print(loop_url)
    # 正常访问login_page就结束了,但是微博丧心病狂的还需要一部跳转,请继续往下看,我自己写的都累了,需要进一步访问login_loop这个url

    login_index = session.get(loop_url, headers=headers)
    uuid = login_index.text
    print(login_index.status_code)  #测试登录是否成功
    session.cookies.save() #保存cookies

    # ↑↑↑↑↑↑到上面其实已经完成了登录了

    response = session.get('http://weibo.com/',headers=headers)
    # print(response.text) #测试用,可以打印出来看看是否与正常登录看到的微博首页的源码一样了


    # 以下是原文作者用来登录微博个人首页获得使用者微博账号,并打印出来的代码,我没有详细解析
    uuid_pa = r'"uniqueid":"(.*?)"'
    uuid_res = re.findall(uuid_pa, uuid)[0]
    web_weibo_url = "http://weibo.com/%s/profile?topnav=1&wvr=6&is_all=1" % uuid_res
    weibo_page = session.get(web_weibo_url, headers=headers)
    weibo_pa = r'(.*?)'
    # print(weibo_page.content.decode("utf-8"))
    userID = re.findall(weibo_pa, weibo_page.content.decode("utf-8", 'ignore'), re.S)[0]    #不加re.S参数好像没什么不同,上文我没加
    print(u"欢迎你 %s, 你在正在使用 xcaojianhong 写的爬虫模拟登录微博" % userID)
    print(input(u'输入任何键继续'))
    print(u'好吧其实大部分代码都是参考xchaoinfo的文章')


if __name__ == "__main__":
    username = input(u'用户名:')
    password = input(u'密码:')
    login(username, password)


测试结果:

python模拟登录练习(二)_第1张图片
@P`MXHW5]5`%MP)~2LMWU50.png

你可能感兴趣的:(python模拟登录练习(二))