• 思路描述:

浏览器打开CSDN登陆页,使用浏览器的右键检查功能(F12)来监控HTTP请求,先通过错误的账号密码登陆CSDN,然后在“Network”中查看第一个请求的内容,如果是请求方式post那一般就对了,如果不是那就往下一个一个找,如图一:

python 模拟登录邮箱 python模拟用户登录验证码_模拟登录

图一

把requests URL复制出来作为登录的url(分号后面的不要),右边的上下滑动杆拉到最下面查看需要提交哪些数据,如图二:

python 模拟登录邮箱 python模拟用户登录验证码_html_02


图二

红框中的数据就是需要提交的数据,只要正确模拟出这些数据就能登录了,通过查看登录页的源码后,可以发现除了‘username’和‘password’两项需要填写外,其他的项都在网页源码中可以提取,现在登录基本没有什么问题,接下来说验证码。
通过分析可以知道csdn的验证码只在‘账号正确,密码错误一次或以上的情况下’出现,且错误五次会提示等10分钟再登录(是针对账号的,换个账号不用等),现在我们可以故意输错密码让验证码弹出来,然后分析页面源码找到验证码图片的url,下载到本地,在程序中自动打开图片,人工识别输入即可。

以上就是整个模拟登录的大概思路,具体分析细节无闲暇详述(=。=),如果刚接触模拟登录,可以看下这篇文章,Python使用requests库模拟登陆网站的方式–以豆瓣为例
它的前期分析可以学习一下。我使用requests库的session模块来维持前期获取post参数的会话,以保证多次获取url时的post表单中的一些参数(lt,execution,还有验证码图片)不变;当然,也可以使用urllib2中的opener来实现,有兴趣的话你可以试试。

在写代码时,需要注意的是,最好按照一定的格式来写,这样写出来的代码可读性佳,看起来也优雅。

代码如下:

# -*- coding: utf-8 -*-
# @Time     : 2017/12/16 8:46
# @Author   : ELI
# @IDE      : PyCharm
# @PJ_NAME  : Login_csdn

import requests
import re
import json
import os, sys
import subprocess

class CSDN:
    '''
    编写主类
    '''
    def __init__(self, acount, passwd):
        '''
        初始化各种变量以供类中的其他方法调用,代入用户名和密码参数
        :param acount:
        :param passwd:
        '''
        self.url = ''
        header = {'User-Agent': 'Safari/537.36'}

        self.current_dir = os.path.join(sys.path[0], 'cookies.txt')  # 定义一个变量存放cookies的保存路径
        self.acount = acount
        self.passwd = passwd

        self.session = requests.session()  # 定义核心变量 session,用于携带headers/cookies参数访问url
        self.session.headers = header  # 添加headers

        self.cookie_exists = self.load_cookie()
        self.captchaFile = os.path.join(sys.path[0], "captcha.jpg")
        # 判断本地有无cookie文件
        if self.cookie_exists:
            print '=' * 50
            print '已检测到cookie文件,直接从本地加载cookie...'
            self.session.cookies.update(self.cookie_exists)  # 添加cookies
        else:
            print '=' * 50
            print '未检测到cookies文件存在,此次为第一次登录,登录成功后将保存cookies文件!'
            print '=' * 50

    def get_login_data(self):
        '''
        获取并返回post表单
        :return:
        '''
        # 如果存在cookie,就不运行这个方法
        self.cookie_exists = self.load_cookie()
        if self.cookie_exists:
            print '<#提示#:当前为‘已登录’状态!勿需运行get_login_data方法>\n'
            pass
        else:
            html = self.session.post(self.url).text
            # print html  #调试用
            lt = re.search('LT-\w+-\w+', html).group()  # 正则获取指定字符串,此处相对于使用bs更快更简便
            execution = re.search('execution?.*value=.(\w+)', html).group(1)  # 正则获取指定字符串
            self.lt = lt;
            self.exe = execution
            postdata = {
                'gps': '',
                'username': self.acount,
                'password': self.passwd,
                'validateCode': '',  #第一次登录时可能不需要这个参数,值可以留空
                'lt': lt,
                'execution': execution,
                '_eventId': 'submit',
            }  # 第一次登录需要提交的表单
            # print postdata   #调试用
            # print html
            return postdata

    def check_captcha(self, postdata, check_img_url, html):
        '''
        写一个方法来解决验证码
        :param postdata:
        :param check_img_url
        :return:
        '''
        # 写两个正则作为判断错误类型的依据
        check_captcha_pat = u'验证码错误'
        check_captcha = re.search(check_captcha_pat, html)

        check_pass_error_pat = u'密码不正确'
        check_pass_error = re.search(check_pass_error_pat, html)
        if check_captcha:
            print '说验证码错误?'
        elif check_pass_error:
            print '账号或密码不对,修改一下重新运行吧。'
        else:
            print '登错5次了。。等10分钟吧,要么换个账号继续。。'
            sys.exit()
        # 保存验证码到本地
        img = self.session.get(check_img_url).content
        with open(self.captchaFile, 'wb')as f:
            f.write(img)
        # 打开验证码图片,登录成功后自动删除
        subprocess.call(self.captchaFile, shell=True)
        captcha = raw_input('识别到验证码,关闭图片后输入验证码:')
        postdata['validateCode'] = captcha
        new_postdata = postdata
        os.remove(self.captchaFile)
        # 回调login方法,提交加上验证码参数
        self.login(postdata=new_postdata)

    def login(self, postdata):
        '''
        第一次登录时运行,登录成功后保存cookie
        :param postdata:
        :return:
        '''
        if os.path.exists(self.current_dir):
            print '<#提示#:当前为‘已登录’状态!勿需运行login方法>\n'
            pass
        else:
            print '正在登录中...'
            html = self.session.post(self.url, data=postdata).text
            # print html
            check_string = ['redirect = ""', 'redirected to ']  #
            for string in check_string:
                check_it = re.search(string, html)
                if check_it:  # 若登录成功,网页的response会包含check_string中的字符串,这里通过正则抓取来检测即可
                    print '登录csdn网站成功!'
                    self.savecookie()
                    break
            else:
                captcha_pat = 'src=.(.*?). id=.yanzheng.'
                check_img = re.search(captcha_pat, html)  # 正则抓取验证码的url

                check_pass_error_pat = [u'密码不正确',u'系统检测到账号异常']
                for string in check_pass_error_pat:
                    check_pass_error = re.search(string, html)  # 正则抓取关键字符来判断错误类型
                # 如果登录失败了,要么账号/密码不对,要么有验证码
                    if check_pass_error:
                        print '账号或密码不对,修改一下重新运行吧。'
                        sys.exit()
                    else:pass
                if check_img:
                    check_img_url = check_img.group(1)
                    # print html
                    self.check_captcha(postdata, check_img_url, html)
                else:
                    print '未抓取到验证码图片url,登录URL被跳转到其他页面,请调试!'  # 为以后网页结构调整做个预判
                    #print html

    def savecookie(self):
        '''
        把登陆后的cookie保存到文件
        此处将字典对象转化为字符串进行存储
        :return:
        '''
        with open(self.current_dir, 'wb') as f:
            cookies = self.session.cookies.get_dict()
            json.dump(cookies, f)
            print '=' * 50
            print '已在当前目录下生成cookie文件:%s' % self.current_dir

    def load_cookie(self):
        '''
        如果在本地存在cookies文件
        则从本地文件加载cookie,没有就返回空
        :return:
        '''
        if os.path.exists(self.current_dir):
            with open(self.current_dir) as f:
                cookie = json.load(f)
                return cookie
        else:
            return  # 返回None

    def view_blog(self):
        '''
        访问博客列表页(这个页面是需要登录才能访问的)
        :return:
        '''
        url = ''
        html = self.session.get(url).text
        check_string = u'文章列表'
        check_it = re.search(check_string, html)  # 如果访问博客列表页成功,网页内容会包含check_string,所以通过正则抓取来判断即可
        if check_it:
            print '=' * 50
            print '访问个人博客列表页成功,输出html内容:'
            print '=' * 50
            print html

        else:
            print '=' * 50
            print '未知原因导致失败,请自行调试!'
            print '=' * 50

    # 登录成功保存cookie后,运行这个方法,调用上面写好的类中的session对象,可直接访问主域名是csdn.net的其他URL
    def open_csdn_site(self, url, data=None, timeout=5):
        '''
        返回登录成功后的session
        :return:
        '''
        return self.session.get(url, data=data, timeout=timeout)


if __name__ == '__main__':  # 若在本程序自身中(非调用)运行,则运行下面的代码

    u = 'random@qq.com'  # csdn账号(邮箱/手机/账号昵称)
    p = '123456'  # csdn密码
    c = CSDN(acount=u, passwd=p)
    data = c.get_login_data()  # 用于第一次登录获取post表单,后续不用再运行
    log = c.login(data)  # 用于第一次登录获取cookie,后续不用再运行
    # blog = c.view_blog()     #登录博客主页

    # print c.open_csdn_site('').text