一、思路:
1、 命令行工具参数获取
2、 字典读取
3、 多线程访问
4、 状态码获得判断输出结果
二、工具初始化
2.1 定义banner函数
作用:用于介绍工具版本与名称
def banner():
print("*"*51)
print("*"*2+" "*17+"DirScan v1.0"+" "*17+"*"*2)
print("*"*51)
print()
banner()
2.2 定义usage函数
作用:用于介绍使用方法(url,thread,dictionary)
def usage():
print("This is the tool's usage")
print("python DirScan.py -u url -t thread -d dictionary")
print()
print("*"*51 )
usage()
三、从命令行中获取参数
3.1 用到的库:
sys、getopt
3.2 用法示例:
3.2.1 打印出获取到的参数及其类型
import sys
import getopt
opts,args = getopt.getopt(sys.argv[1:],"u:t:d:")
print(type(opts))
print((opts))
print(type(args))
print(args)
3.2.2 将获取的数据依次输出
opts,args = getopt.getopt(sys.argv[1:],"u:t:d:")
#打印接收到的值
for k,v in opts:
print(k)
print(v)
3.2.3 将获取后的参数,赋值后输出
import sys
import getopt
def start():
if len(sys.argv) == 7:
opts,args = getopt.getopt(sys.argv[1:],"u:t:d:")
for k,v in opts:
#print(k,v)
if k == '-u': #这个 “ - ”不要忘了
url = v
elif k == '-t':
threads = v
elif k == '-d':
dic = v
print("url: "+ url)
print("threads: "+ threads)
print("dic: "+dic)
else:
print("你传输的参数有误")
sys.exit() #结束当前程序
start()
四、字典文件的读取与分配
4.1 回顾with…as…结构
with open("dir.txt","r") as f:
line_list = f.readlines()
print(len(line_list))
print(type(line_list))
for line in line_list:
print(line.strip()) #去掉换行
4.2 为多线程分配字典
思路是,一个线程读取固定数目的字典文件内容,
如5个线程,15个字典内容的话。一个线程读取3个内容。
这样将一个字典得内容分为5份就行了。
但是假如有5个线程,16个字典内容的话,改如何解决呢?
那就退而求其次,让每个线程读取4个内容,最后一个线程不用读。
这样字典也分配4份。
假如有5个线程,17个字典内容的话,改如何解决呢?
继续退而求其次,让前面四个线程读取4个内容,最后一个线程读1个内容。
这样字典继续分配为5份。
import math
def multi_scan(threads):
result_list = [] # 定义一个存放最终字典的空列表
with open("dir.txt","r") as f:
dic_list = f.readlines()
#确定一个线程读取几行字典内容
thread_read_line_num = math.ceil(len(dic_list) / int(threads)) #对线程数向上取整,即16/5=3.2 --> 4
i=0
temp_list = [] #新建一个临时列表
for line in dic_list: #依次从加载的字典中取数据
i = i + 1
if i % thread_read_line_num == 0: #如果够一个线程取得数量了
temp_list.append(line.strip())
result_list.append(temp_list) #将当前线程创建的临时列表 存放到最终空列表中
temp_list = []
else:
temp_list.append(line.strip()) #放一个数据到临时列表
print(result_list) #打印输出最终列表
multi_scan(5)
4.3 优化(上边的程序有一定的问题)
import math
def multi_scan(threads):
result_list = [] # 定义一个存放最终字典的空列表
with open("dir.txt","r") as f:
dic_list = f.readlines()
lenth = len(dic_list)
#确定一个线程读取几行字典内容
thread_read_line_num = math.ceil(len(dic_list) / int(threads)) #对线程数向上取整,即16/5=3.2 --> 4
i=0
temp_list = [] #新建一个临时列表
for line in dic_list: #依次从加载的字典中取数据
i = i + 1
if i % thread_read_line_num == 0: #如果够一个线程取得数量了
temp_list.append(line.strip())
result_list.append(temp_list) #将当前线程创建的临时列表 存放到最终空列表中
temp_list = []
elif i == lenth: #防止不够整除,少了最后一个数组。
temp_list.append(line.strip()) #可以将这三行注释掉传入5个线程,17个数据试试就明白了。
result_list.append(temp_list)
else:
temp_list.append(line.strip()) #放一个数据到临时列表
print(result_list) #打印输出最终列表
multi_scan(5)
五、多线程访问
5.1 使用函数
Python的多线程是一个模块“ threading ”,使用记得导入。
5.2 函数用法
threading.Thread(target=A函数,args=(k,v))
#A函数需要提前定义,
#这个元组的key与value都是A函数的参数,V可以没有。
#但是k后边一定要跟“ 逗号 ”,不跟逗号K就成了字符串,格式错误。(无V的情况下)
5.3 原理理解
从A地送100件东西到B地,且一次仅仅可以送出一个。
传统单线程就是一个快递员,送100次,效率非常低,且占用发货资源。
因为在100个货物未送完之前,发货站的人得一直等着快递员回来,然后发下一次。
这样别的物品发货也得等这100个货物送完。
多线程即,一个发货站配备N个快递员(线程),这样就可以让N个快递员同时送货。
一个发货人员同时供应多个快递员,极高得提升速度。
5.4 最终代码(重要)
import math
import threading
import sys
import getopt
import requests
#工具初始化之版本号与名称
def banner():
print("*"*51)
print("*"*2+" "*17+"DirScan v1.0"+" "*17+"*"*2)
print("*"*51)
print()
#工具初始化之用法
def usage():
print("This is the tool's usage")
print("python DirScan.py -u url -t thread -d dictionary")
print()
print("*"*51 )
#定义一个多线程执行函数
def multi_scan(url,threads,dic):
result_list = [] # 定义一个存放最终字典的空列表
with open(dic,"r") as f:
dic_list = f.readlines()
lenth = len(dic_list)
#确定一个线程读取几行字典内容
thread_read_line_num = math.ceil(len(dic_list) / int(threads)) #对线程数向上取整,即16/5=3.2 --> 4
i=0
temp_list = [] #新建一个临时列表
for line in dic_list: #依次从加载的字典中取数据
i = i + 1
if i % thread_read_line_num == 0: #如果够一个线程取得数量了
temp_list.append(line.strip())
result_list.append(temp_list) #将当前线程创建的临时列表 存放到最终空列表中
temp_list = []
elif i == lenth: #防止不够整除,少了最后一个数组。
temp_list.append(line.strip()) #可以将这三行注释掉传入5个线程,17个数据试试就明白了。
result_list.append(temp_list)
else:
temp_list.append(line.strip()) #放一个数据到临时列表
#print(result_list) #打印输出最终列表
threads_list = [] #定义一个空的多线程列表
#制作一个多线程列表
for i in result_list:
threads_list.append(threading.Thread(target=scan,args=(url,i))) #url由命令行传入
#启动多线程
for t in threads_list:
t.start()
#定义一个目录扫描功能的函数
def scan(url,dic):
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:86.0) Gecko/20100101 Firefox/86.0"}
for line in dic:
r = requests.get(url+'/'+line,headers=headers)
if r.status_code == 200:
print(r.url+ " : "+ str(r.status_code))
#定义获取命令行参数的函数,也是程数开始的地方
def start():
banner()
if len(sys.argv) == 7:
opts,args = getopt.getopt(sys.argv[1:],"u:t:d:") #从命令行获取参数
for k,v in opts:
#print(k,v)
if k == '-u': #这个 “ - ”不要忘了
url = v
elif k == '-t':
threads = v
elif k == '-d':
dic = v
else:
print("你传输的参数有误")
usage()
sys.exit() # 结束当前程序
multi_scan(url,threads,dic) #将获取到的参数传入多线程函数,并执行。
else:
print("你传输的参数有误")
usage()
sys.exit() #结束当前程序
if __name__ =="__main__":
start()
六、总结写一个工具的步骤(多线程思路)
6.1 工具的初始化
介绍工具版本、名字和使用方法
6.2 从命令行中获取到指定的参数,并使用这些参数。
6.3 字典的分配
读取字典,按照线程数(X)将字典分配为X个小列表
将X个小列表放置到一个大列表中。
6.4 多线程使用
定义一个访问的函数A,
传入字典、函数A生成一个多线程列表,
启动多线程列表。
6.5 将上述几个函数综合调用
记得优化一些细节