崔庆才flask+scrapy维护cookies过程及代码讲解

源码请访问其github,https://github.com/Python3WebSpider/CookiesPool

下面开始一步一步来讲解代码:

首先看run.py看一下程序的入口

from cookiespool.scheduler import Scheduler

def main():
    s = Scheduler()
    s.run()

if __name__ == '__main__':
    main()

程序入口十分简单,看来是调用了Scheduler()的run函数。

    def run(self):
        if API_PROCESS:#如果config.py里API接口服务是True意味着要开启API接口服务
            api_process = Process(target=Scheduler.api)#Process是开启一个进程
            api_process.start()
        
        if GENERATOR_PROCESS:#同理 产生器
            generate_process = Process(target=Scheduler.generate_cookie)
            generate_process.start()
        
        if VALID_PROCESS:#同理 验证器
            valid_process = Process(target=Scheduler.valid_cookie)
            valid_process.start()

看来,崔神是根据config.py里对于API_PROCESS 等三个变量的设置进行了相应的操作。顾名思义,产生器是抽取cookies的,验证器是检验cookies是否可用的。先看第二个,也就是产生器吧。

@staticmethod
    #产生器
    def generate_cookie(cycle=CYCLE):
        while True:
            print('Cookies生成进程开始运行')
            try:
                for website, cls in GENERATOR_MAP.items():
                    generator = eval(cls + '(website="' + website + '")')#即WeiboCookiesGenerator(website="weibo")
                    generator.run()
                    print('Cookies生成完成')
                    generator.close()
                    time.sleep(cycle)
            except Exception as e:
                print(e.args)

看来是读取了静态配置数据,然后调用了相应的函数。我们看下是啥。

# 产生器类,如扩展其他站点,请在此配置
GENERATOR_MAP = {
    'weibo': 'WeiboCookiesGenerator'
}

原来是调用了WeiboCookiesGenerator(website="weibo")这个函数。我们去generator.py看一下他干了嘛。

class WeiboCookiesGenerator(CookiesGenerator):
    def __init__(self, website='weibo'):
        """
        初始化操作
        :param website: 站点名称
        :param browser: 使用的浏览器
        """
        CookiesGenerator.__init__(self, website)
        self.website = website
    
    def new_cookies(self, username, password):
        """
        生成Cookies
        :param username: 用户名
        :param password: 密码
        :return: 用户名和Cookies
        """
        return WeiboCookies(username, password, self.browser).main()

 

CookiesGenerator.__init__(self, website)

我们看上面这一句,找到相应代码  

class CookiesGenerator(object):
    def __init__(self, website='default'):
        """
        父类, 初始化一些对象
        :param website: 名称
        :param browser: 浏览器, 若不使用浏览器则可设置为 None
        """
        self.website = website
        self.cookies_db = RedisClient('cookies', self.website)#type=cookies,website=weibo self.name={'cookies':'weibo'}
        self.accounts_db = RedisClient('accounts', self.website)#type=cookies,website=weibo self.name={'accounts':'weibo'}
        self.init_browser()

    def __del__(self):
        self.close()
    
    def init_browser(self):
        """
        通过browser参数初始化全局浏览器供模拟登录使用
        :return:
        """
        if BROWSER_TYPE == 'PhantomJS':
            caps = DesiredCapabilities.PHANTOMJS
            caps[
                "phantomjs.page.settings.userAgent"] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36'
            self.browser = webdriver.PhantomJS(desired_capabilities=caps)
            self.browser.set_window_size(1400, 500)
        elif BROWSER_TYPE == 'Chrome':
            self.browser = webdriver.Chrome()
……………………………………该类未完

根据传过来的website="weibo"从而 初始化了self.cookies和accounts_db,这两个都是数据库操作文件,没什么好看的,想看就自己看db.py吧,这里不解释了。

继续看init_browse函数,就是用selenium初始化了浏览器而已,因为用了phantomjs所以电脑不会以窗口形式打开浏览器,而是后台模拟。

到这里generator = eval(cls + '(website="' + website + '")')#即WeiboCookiesGenerator(website="weibo")这句话走完了,在继续看。

generator.run()
    def run(self):
        """
        运行, 得到所有账户, 然后顺次模拟登录
        :return:
        """
        accounts_usernames = self.accounts_db.usernames() #获取name对应的hash中所有键值对的key  self.name={'accounts':'weibo'}
        cookies_usernames = self.cookies_db.usernames()#获取name对应的hash中所有键值对的key  self.name={'cookies':'weibo'}
        
        for username in accounts_usernames:
            if not username in cookies_usernames:
                password = self.accounts_db.get(username)#在name对应的hash中获取根据key获取value
                print('正在生成Cookies', '账号', username, '密码', password)
                result = self.new_cookies(username, password)
                # 成功获取
                if result.get('status') == 1:
                    cookies = self.process_cookies(result.get('content'))
                    print('成功获取到Cookies', cookies)
                    if self.cookies_db.set(username, json.dumps(cookies)):
                        print('成功保存Cookies')
                # 密码错误,移除账号
                elif result.get('status') == 2:
                    print(result.get('content'))
                    if self.accounts_db.delete(username):
                        print('成功删除账号')
                else:
                    print(result.get('content'))
        else:
            print('所有账号都已经成功获取Cookies')

不断从redis数据库获得账号密码来模拟登录从而获取cookies,我们看result = self.new_cookies(username, password),对应函数代码如下:

    def new_cookies(self, username, password):
        """
        生成Cookies
        :param username: 用户名
        :param password: 密码
        :return: 用户名和Cookies
        """
        return WeiboCookies(username, password, self.browser).main()

接着找weiboCookies,锁定到了cookies.py,咱们看一下这个文件是干嘛的,首先,参数初始化

class WeiboCookies():
    def __init__(self, username, password, browser):
        self.url = 'https://passport.weibo.cn/signin/login?entry=mweibo&r=https://m.weibo.cn/'
        self.browser = browser
        self.wait = WebDriverWait(self.browser, 20)
        self.username = username
        self.password = password

然后执行main()

    def main(self):
        """
        破解入口
        :return:
        """
        self.open()
        if self.password_error():
            return {
                'status': 2,
                'content': '用户名或密码错误'
            }
        # 如果不需要验证码直接登录成功
        if self.login_successfully():
            cookies = self.get_cookies()
            return {
                'status': 1,
                'content': cookies
            }
        # 获取验证码图片
        image = self.get_image('captcha.png')
        numbers = self.detect_image(image)
        self.move(numbers)
        if self.login_successfully():
            cookies = self.get_cookies()
            return {
                'status': 1,
                'content': cookies
            }
        else:
            return {
                'status': 3,
                'content': '登录失败'
            }

main执行了open()

    def open(self):
        """
        打开网页输入用户名密码并点击
        :return: None
        """
        self.browser.delete_all_cookies()
        self.browser.get(self.url)
        username = self.wait.until(EC.presence_of_element_located((By.ID, 'loginName')))
        password = self.wait.until(EC.presence_of_element_located((By.ID, 'loginPassword')))
        submit = self.wait.until(EC.element_to_be_clickable((By.ID, 'loginAction')))
        username.send_keys(self.username)
        password.send_keys(self.password)
        time.sleep(1)
        submit.click()

看来open函数就是模拟了打开网页模拟登录的操作

再回过头看if语句

if self.password_error():
            return {
                'status': 2,
                'content': '用户名或密码错误'
            }
        # 如果不需要验证码直接登录成功
        if self.login_successfully():
            cookies = self.get_cookies()
            return {
                'status': 1,
                'content': cookies
            }
        # 获取验证码图片
        image = self.get_image('captcha.png')
        numbers = self.detect_image(image)
        self.move(numbers)
        if self.login_successfully():
            cookies = self.get_cookies()
            return {
                'status': 1,
                'content': cookies
            }
        else:
            return {
                'status': 3,
                'content': '登录失败'
            }

self.password_error函数挺简单,不看了,判断是不是登录成功了,第二个if是如果不需要验证码,则直接返回,不然的话开来是需要验证码了。后面是执行验证码破解的操作,我想单独写一个博客,这里不赘述了。总之返回了一个字典,包含status和content。

到这里,回到起点 run函数里的result = self.new_cookies(username, password)已经执行完了,继续往下走

 # 成功获取
                if result.get('status') == 1:
                    cookies = self.process_cookies(result.get('content'))
                    print('成功获取到Cookies', cookies)
                    if self.cookies_db.set(username, json.dumps(cookies)):#json.dumps()用于#将字典形式的数据转化为字符串,json.loads()用于将字符串形式的数据转化为字典,
                        print('成功保存Cookies')
                # 密码错误,移除账号
                elif result.get('status') == 2:
                    print(result.get('content'))
                    if self.accounts_db.delete(username):
                        print('成功删除账号')
                else:
                    print(result.get('content'))
        else:
            print('所有账号都已经成功获取Cookies')

到这里,run函数也执行完了,产生器结束。再看验证器。

if VALID_PROCESS:#同理 验证器
            valid_process = Process(target=Scheduler.valid_cookie)
            valid_process.start()

看一下valid_cookie函数

@staticmethod
    #验证器
    def valid_cookie(cycle=CYCLE):
        while True:
            print('Cookies检测进程开始运行')
            try:
                for website, cls in TESTER_MAP.items():
                    tester = eval(cls + '(website="' + website + '")')
                    tester.run()
                    print('Cookies检测完成')
                    del tester
                    time.sleep(cycle)
            except Exception as e:
                print(e.args)

我们再看一下TESTER_MAP是个啥

TESTER_MAP = {
    'weibo': 'WeiboValidTester'
}

tester=eval(cls+'(website="'+website='")')意思就是调用WeiboValidTester(website="weibo")函数。

接着看看这个函数

class WeiboValidTester(ValidTester):
    def __init__(self, website='weibo'):
        ValidTester.__init__(self, website)

接着看ValidTester.__init__(self,'weibo')函数

class ValidTester(object):
    def __init__(self, website='default'):
        self.website = website
        self.cookies_db = RedisClient('cookies', self.website)
        self.accounts_db = RedisClient('accounts', self.website)

再看tester.run()

    def run(self):
        cookies_groups = self.cookies_db.all()##取出name对应的hash中所有的键值对
        for username, cookies in cookies_groups.items():
            self.test(username, cookies)

再看test函数:

    def test(self, username, cookies):
        print('正在测试Cookies', '用户名', username)
        try:
            cookies = json.loads(cookies)
        except TypeError:
            print('Cookies不合法', username)
            self.cookies_db.delete(username)
            print('删除Cookies', username)
            return
        try:
            test_url = TEST_URL_MAP[self.website]
            response = requests.get(test_url, cookies=cookies, timeout=5, allow_redirects=False)
            if response.status_code == 200:
                print('Cookies有效', username)
            else:
                print(response.status_code, response.headers)
                print('Cookies失效', username)
                self.cookies_db.delete(username)
                print('删除Cookies', username)
        except ConnectionError as e:
            print('发生异常', e.args)

根据代码可以看出他是在检验取出的cookies,就提的检验方法看第二个try,

TEST_URL_MAP = {
    'weibo': 'https://m.weibo.cn/'
}

就是请求了这个网站,看返回的状态码。如果失效就删除。到这里就完成了对于cookies的检测,代码还是比较简单的。

最后咱们看一下api吧

@staticmethod
    def api():
        print('API接口开始运行')
        app.run(host=API_HOST, port=API_PORT)

就是一个flask框架生成web接口,还是不看了吧,没啥意思,会flask的一看就懂,不用讲。

总结:

稍微总结一下这个项目吧,核心就是这个scheduler调度器,里面调用了其他工具类和工具函数。

工具之一:产生器generator.py,主要负责就是模拟用户登录来获取cookies

工具之二:验证器tester.py,用于检验redis里的cookies是否还有效。

两个工具都用到了db.py,也就是封装了一些对于redis数据库的增删改查操作,相当基础。

另外config.py里面写死了一些用到的参数。

哦,差点忘了说了, cookies.py里面写了关于验证码处理的问题。

崔神挺细心的,写了个importer函数供我们手动录入一些账号信息。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
以下是一个简单的 Flask+MySQL 实现用户登录注册的代码: ``` from flask import Flask, request, session, redirect from flask_mysqldb import MySQL app = Flask(__name__) app.secret_key = 'your secret key' mysql = MySQL(app) app.config['MYSQL_HOST'] = 'localhost' app.config['MYSQL_USER'] = 'your mysql username' app.config['MYSQL_PASSWORD'] = 'your mysql password' app.config['MYSQL_DB'] = 'your mysql database' @app.route('/') def index(): if 'username' in session: return f'Hello, {session["username"]}!' else: return 'Please login first.' @app.route('/signup', methods=['GET', 'POST']) def signup(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] cur = mysql.connection.cursor() cur.execute('INSERT INTO users(username, password) VALUES (%s, %s)', (username, password)) mysql.connection.commit() cur.close() session['username'] = username return redirect('/') else: return ''' <form method="post"> <input type="text" name="username" placeholder="Username"/><br> <input type="password" name="password" placeholder="Password"/><br> <button type="submit">Signup</button> </form> ''' @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': username = request.form['username'] password = request.form['password'] cur = mysql.connection.cursor() cur.execute('SELECT password FROM users WHERE username = %s', [username]) password_from_db = cur.fetchone() cur.close() if password_from_db and password_from_db[0] == password: session['username'] = username return redirect('/') else: return 'Invalid username or password.' else: return ''' <form method="post"> <input type="text" name="username" placeholder="Username"/><br> <input type="password" name="password" placeholder="Password"/><br> <button type="submit">Login</button> </form> ''' if __name__ == '__main__': app.run() ``` 以上代码使用 Flask 框架和 Flask-MySQLdb 扩展实现用户注册和登录功能。用户信息保存在 MySQL 数据库中,密码使用明文存储(实际应用中应使用加密后的密码)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CtrlZ1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值