python的一些常用方法实践

        记录瞬间        

最近碰到一个烦心的问题,就是找了一些需要的文件,但是碰到的都是加密的文件

这样做不符合开放、开源的原则啊!

所以,经过慎重思考,决定写一个解密的方法,进行解密操作

前置条件:

1、针对密码只能猜测有哪些可能,所以就将数字+字母(大小写)作为排序的全排列,进行文件的密码组装

2、针对zip或者rar文件,需要进行判别处理,毕竟压缩的方法不一样

3、要考虑操作的效率,就必须进行多进程的操作

方法一、

简单介绍一下:使用product函数生成密码的笛卡尔积,本质上比较容易理解,但是由于设置密码长度不确定,加之运行机器的限制

product方法针对密码位数小于等于  4 位的情况比较有效,大于4位的密码,由于运行占用内存的原因,就无法运行了(单位的笔记本,配置没那么好o(* ̄︶ ̄*)o)

代码如下:

from itertools import product
import os
import string
from multiprocessing import Process
import zipfile
# 需要安装一下 pip install rarfile
import rarfile


def read_file(file_name, pwd):
    if file_name.endswith(".zip"):
        fp = zipfile.ZipFile(file_name)
    elif file_name.endswith(".rar"):
        fp = rarfile.RarFile(file_name)
    else:
        return
    try:
        fp.extractall(path="./")
        fp.close()
        print("没有密码,直接解压就行了。")
    except:
        print("有密码的啊!")
        try:
            pwd = pwd
        except:
            print("没有密码本吗?")
            return
        for p in pwd:
            p = p.strip()
            try:
                fp.extractall(path="D:\BaiduNetdiskDownload\xxx", pwd=p.encode())
                print("Success === >", p)
                fp.close()
                break
            except:
                pass


def generate_pwd():
    pwd_list = [str(i) for i in range(10)] + list(string.ascii_letters)
    # print(pwd_list)
    count = 1
    with open(file=r'./pwd.txt', mode='a', encoding='UTF-8') as f:
        # 生成4位的时候就已经达到 15018570 条了,到了第5位密码时,机器内存溢出了。(✿◡‿◡)
        # 后续可以考虑使用其他方法生成,product方法太消耗内存了。
        for i in range(1, 7):
            product_str = "product({})".format("pwd_list,"*i)
            product_str = product_str.replace(',)', ')')
            print(product_str)
            result = list(eval(product_str))
            for mix in result:
                # print(mix)
                pwd_str = str(mix).replace("'", "").replace("(", "").replace(")", "").replace(", ", "").replace(",", "")
                print(pwd_str, file=f)
                count += 1
    print(count)


def multi_process(file_name):
    if not os.path.isfile('./pwd.txt'):
        generate_pwd()
    with open(file=r'./pwd.txt', mode='r', encoding='utf-8') as f:
        context = f.readlines()
    print(len(context))
    # 确定cpu数量,进行多进程执行,但是要考虑到实际数据的量,主要是密码的量
    # 机器性能好的,可以把这个值调大一点儿
    cpus = 3
    peer_num = num = int(len(context) / cpus)
    data_dict = {}
    sum_mark = 0
    process_list = []
    for i in range(cpus):
        if i == (cpus - 1):
            data_dict[i] = context[sum_mark:]
        else:
            data_dict[i] = context[sum_mark:peer_num]
        sum_mark = peer_num
        peer_num += num
        process_list.append(Process(target=read_file, args=(file_name, data_dict[i])))
    # 启动进程
    for pl in process_list:
        pl.start()


if __name__ == '__main__':
    file_name = r'D:\BaiduNetdiskDownload\xxx.rar'
    multi_process(file_name=file_name)

方法二、

改进,考虑到使用product方法的局限性,希望能从生成器的角度进行解决此问题,来避免消耗过多内存

同时,需要针对多进程间的通信方式,进行改进,主要考虑的就是使用queue的方法来进行通信

但是方法二,只针对密码为5位时进行了解决,相对来说还有一些局限性

数字加大小写字母[0-9a-zA-Z]
可遍历密码数据个数: 62
5位密码全排列结果数据个数: 916132832  亿哦!
6位密码全排列结果数据个数: 56800235584 百亿哦!
7位密码全排列结果数据个数: 3521614606208  兆哦!
8位密码全排列结果数据个数: 218340105584896  百兆哦!

代码如下:

import string
import sys
import time
from multiprocessing import Process, Queue
import zipfile
# 需要安装一下 pip install rarfile
import rarfile


def generate_secrete(pwd):
    for a in pwd:
        # ab = "{}".format(a)
        # yield ab
        for b in pwd:
            for c in pwd:
                for d in pwd:
                    for e in pwd:
                        ll = "{}{}{}{}{}".format(a, b, c, d, e)
                        yield ll


def get_pwd_data(q):
    pwd = [str(i) for i in range(10)] + list(string.ascii_letters)
    print("可遍历密码数据个数:", len(pwd))
    print("5次组合结果数据个数:", len(pwd) ** 5)
    gs = generate_secrete(pwd=pwd)
    count = 0
    while True:
        try:
            get_pwd = next(gs)
            # print(get_pwd)
            # 压入队列数据
            q.put(get_pwd)
            count += 1
            flag = 0
            # 压入数据很快,但是消耗的有点儿慢,所以,需要等一等
            while count % 5000000 == 0:
                if flag == 0:
                    print("当前压入总数为:", count)
                    flag = 1
                # 动态调整一下结果数据
                if q.qsize() > 1000000:
                    time.sleep(50.0)
                elif q.qsize() > 750000:
                    time.sleep(5.0)
                elif q.qsize() > 250000:
                    time.sleep(2.0)
                else:
                    time.sleep(2.0)
                # 设置一定的存量数据,提高整体读取的速度
                if q.qsize() < 100000:
                    print("队列中数据少于10万", q.qsize())
                    break
                # 都清空了不好,需要重新压入数据,效率太低了
                if q.empty():
                    break
        except StopIteration:
            print("Already fetch all data from generator.")
            print("压入队列的总数为:", count)
            break
        except:
            pass


# 解压程序,将文件进行解压操作
def read_file(file_name, q):
    if file_name.endswith(".zip"):
        fp = zipfile.ZipFile(file_name)
    elif file_name.endswith(".rar"):
        fp = rarfile.RarFile(file_name)
    else:
        return
    try:
        fp.extractall(path="./")
        fp.close()
        print("没有密码,直接解压就行了。")
    except:
        print("有密码的啊!")
        while True:
            try:
                # 判断队列是否为空,如果为空就等待一下再获取
                if q.empty():
                    # print("等待 0.5 s")
                    time.sleep(0.5)
                p = q.get()
                if p == "==ok==":
                    break
            except:
                print("没有密码本吗?")
                time.sleep(0.5)
                continue
            else:
                try:
                    fp.extractall(path="D:\BaiduNetdiskDownload\xxx", pwd=p.encode())
                    print("Success === >", p)
                    fp.close()
                    # q.put("==ok==")
                    with open("success.txt", mode='w', encoding='UTF-8') as f:
                        f.write(p)
                    for i in range(10):
                        q.put("==ok==")
                        print("压入队列暂停数据。")
                    break
                except:
                    pass


# 启动多进程进行操作
def multi_process(file_name):
    q = Queue()
    process_list = []
    cpus = 8
    start_time = time.time()
    # 不断的向队列中压入数据
    process_list.append(Process(target=get_pwd_data, args=(q, )))
    # 启动其余进程从队列中读取数据
    for i in range(1, cpus):
        process_list.append(Process(target=read_file, args=(file_name, q)))
    print("当前执行的进程的个数", len(process_list), " 个")
    # 启动进程
    for pl in process_list:
        print("启动进程:", pl.name)
        pl.start()
    # 让数据不断的压入进程,并阻塞执行
    print("压入数据的进程进行阻塞执行!")
    process_list[0].join()
    print("使用队列时间:", time.time() - start_time)
    print("等待队列数据全部被取出")
    # 等待队列数据全部被取出
    while True:
        print("当前剩余个数", q.qsize())
        if not q.empty():
            time.sleep(5.0)
            continue
        else:
            break
    # 队列执行完成后,退出其他的进程
    for pl in process_list:
        try:
            pl.terminate()
            print(pl.name, "Yes it's quit!")
        except:
            print(pl.name, "was already quit!")
            pass


if __name__ == '__main__':
    file_name = r'D:\BaiduNetdiskDownload\xxx.rar'
    multi_process(file_name=file_name)

方法三、

针对方法二的局限,考虑进一步改进,解决更多位密码的方式方法,办法是动态生成一个生成器函数,加载生成器函数进行数据校验

代码如下:

 

import os
import string
import sys
import time
from multiprocessing import Process, Queue
import zipfile
# 需要安装一下 pip install rarfile
import rarfile


# 动态创建生成函数方法,主要生成生成器函数,以供后续调用
# 参数num代表,密码的位数,全排列的结果
def create_function(num):
    ll = ""
    symbol = ""
    if os.path.isfile("./temp.py"):
        os.remove("./temp.py")
    with open(file="./temp.py", mode='w', encoding='UTF-8') as f:
        f.write("def generate_pwd(pwd):\n")
        for i in range(num):
            f.write("    "*(i+1))
            f.write("for a{} in pwd:\n".format(i))
            symbol += "{}"
            ll = ll + "a{}, ".format(i)
        ll = ll[:-2]
        f.write("    " * (num + 1))
        f.write("ll = '" + symbol + "'" + ".format(" + ll + ")\n")
        f.write("    " * (num + 1))
        f.write("yield ll\n")


# 参数说明:q是代表队列,num代表密码的位数
def get_pwd_data(q, num, default_count=0):
    # print(string.printable)
    # 输出为:0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&'()*+,-./:;<=>?@[]^_`{|}~

    pwd = [str(i) for i in range(10)] + list(string.ascii_letters)
    print("可遍历密码数据个数:", len(pwd))
    print(num, "位密码组合结果数据个数:", len(pwd) ** num)
    create_function(num=num)
    module = __import__("temp")
    gp = module.generate_pwd(pwd)
    count = 0
    while True:
        try:
            get_pwd = next(gp)
            # 此处依然可以针对获取的密码进行判断,比如可以猜测密码前几位是字母,后几位是数字等信息,来进一步筛选密码,达到快速获取的目的
            # print(get_pwd)
            count += 1
            # 指定从哪一次获取密码进行执行,默认不进行判断
            if count <= default_count:
                continue
            # 压入队列数据
            q.put(get_pwd)
            flag = 0
            # 压入队列数据操作很快,但是消耗数据有点儿慢,所以,需要等一等
            # 此处需要增加判断,主要是判断循环多少次之后,直接结束(return)该方法,结束之前,也建议清空队列
            # 目的是不让循环无终止的进行下去,过程中,可能已经获取到密码,此时没有进程在获取数据了

            while count % 5000000 == 0:
                if flag == 0:
                    print("当前压入总数为:", count)
                    flag = 1
                # 动态调整一下结果数据
                if q.qsize() > 1000000:
                    time.sleep(50.0)
                elif q.qsize() > 750000:
                    time.sleep(5.0)
                elif q.qsize() > 250000:
                    time.sleep(2.0)
                else:
                    time.sleep(2.0)
                # 设置一定的存量数据,提高整体读取的速度
                if q.qsize() < 100000:
                    print("队列中数据少于10万", q.qsize())
                    break
                # 都清空了不好,需要重新压入数据,效率低了
                if q.empty():
                    break
        except StopIteration:
            print("Already fetch all data from generator.")
            print("压入队列的总数为:", count)
            break
        except:
            pass


# 解压程序,将文件进行解压操作
def read_file(file_name, q):
    if file_name.endswith(".zip"):
        fp = zipfile.ZipFile(file_name)
    elif file_name.endswith(".rar"):
        fp = rarfile.RarFile(file_name)
    else:
        return
    try:
        fp.extractall(path="./")
        fp.close()
        print("没有密码,直接解压就行了。")
    except:
        print("有密码的啊!")
        while True:
            try:
                # 判断队列是否为空,如果为空就等待一下再获取
                if q.empty():
                    # print("等待 0.5 s")
                    time.sleep(0.5)
                p = q.get()
                if p == "==ok==":
                    return
            except:
                print("没有密码本吗?")
                time.sleep(0.5)
                continue
            else:
                try:
                    fp.extractall(path="D:\BaiduNetdiskDownload\xxx", pwd=p.encode())
                    print("Success === >", p)
                    fp.close()
                    # q.put("==ok==")
                    with open("success.txt", mode='w', encoding='UTF-8') as f:
                        f.write(p)
                    for i in range(10):
                        q.put("==ok==")
                        print("压入队列暂停数据。")

                    while True:
                        if q.empty():
                            break
                        else:
                            q.get()
                    q.task_done()
                    break
                except:
                    pass


# 启动多进程进行操作
def multi_process(file_name):
    q = Queue()
    cpus = 9
    # 密码位数从 1~8 位,可以根据实际情况进行修改
    for num in range(1, 9):
        if os.path.isfile("./success.txt"):
            os.remove("./success.txt")
        process_list = []
        start_time = time.time()
        # 不断的向队列中压入数据
        process_list.append(Process(target=get_pwd_data, args=(q, num)))
        # 启动其余进程,从队列中读取数据
        for i in range(1, cpus):
            process_list.append(Process(target=read_file, args=(file_name, q)))
        print("当前执行的进程的个数", len(process_list), " 个")
        # 启动进程
        for pl in process_list[:]:
            print("启动进程:", pl.name)
            pl.start()
        print("压入数据的进程进行阻塞执行!", process_list[0].name)
        # 让数据不断的压入队列进程,并阻塞执行
        process_list[0].join()
        print("数据完全压入到队列中,执行时间:", time.time() - start_time)
        print("等待队列数据全部被取出")
        # 设定标记,以用来判断程序是否需要退出
        flag = 0
        # 等待队列数据全部被取出
        while True:
            print("当前剩余个数", q.qsize())
            if q.qsize() == 0:
                #
                if os.path.isfile("./success.txt"):
                    flag = 1
                break
            else:
                if os.path.isfile("./success.txt"):
                    flag = 1
                    break
                time.sleep(5.0)
                continue

        # 队列执行完成后,退出其他的进程
        for pl in process_list:
            try:
                # 直接terminate会导致僵尸进程,需要等待进程彻底退出才是关闭进程
                pl.terminate()
                print(pl.name, "Yes it's quit!")
                # 等待进程退出
                pl.join()
            except:
                print(pl.name, "was already quit!")
                pass
        if flag == 1:
            break
        else:
            print("本次循环就没有正确的密码被执行:", num)


if __name__ == '__main__':
    file_name = r'D:\BaiduNetdiskDownload\xxx.rar'
    multi_process(file_name=file_name)

除了进程操作之外,依然可以考虑其他的操作方法,如:线程、协程等方式来执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值