Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537
滲透測(cè)試是保衛(wèi)網(wǎng)絡(luò)安全的一種有效且必要的技術(shù)手段,而滲透測(cè)試的本質(zhì)就是信息收集,信息搜集整理可為后續(xù)的情報(bào)跟進(jìn)提供強(qiáng)大的保證,目標(biāo)資產(chǎn)信息搜集的廣度,決定滲透過程的復(fù)雜程度,目標(biāo)主機(jī)信息搜集的深度,決定后滲透權(quán)限的持續(xù)把控。
系統(tǒng)主要基于Python實(shí)現(xiàn)了Web指紋探測(cè)、端口掃描和服務(wù)探測(cè)、真實(shí)IP信息探測(cè)、WAF防火墻探測(cè)、子域名掃描、目錄掃描和敏感信息探測(cè)的功能。
CMS識(shí)別功能主要通過調(diào)用本地識(shí)別接口識(shí)別,或者調(diào)用網(wǎng)絡(luò)識(shí)別接口識(shí)別兩種方式,其中本地接口識(shí)別主要是通過比對(duì)常見CMS的特征來完成識(shí)別。系統(tǒng)收集了1400+的國(guó)內(nèi)常見指紋,并且以josn文件類型的方式保存,便于以后的補(bǔ)充和擴(kuò)展。
而網(wǎng)絡(luò)接口識(shí)別則是通過在線指紋識(shí)別網(wǎng)站whatweb的api來實(shí)現(xiàn),whatweb在線識(shí)別演示如圖所示。
CDN判斷功能主要是通過兩種本地判斷方式和三種網(wǎng)絡(luò)接口在線判斷方式共同運(yùn)行,最后結(jié)合五種判斷方式得到的結(jié)果得出最終結(jié)論的方法實(shí)現(xiàn)。本地判斷主要是借助Socket模塊中的getaddrinfo方法來解析域名,以及nslookup查詢域名信息的方法來判斷是否存在CDN防護(hù)。
三種網(wǎng)絡(luò)接口在線判斷CDN服務(wù)的演示如圖所示。
子域名掃描功能一方面是通過本地字典爆破,另外一方面主要是通過Bing搜索引擎,對(duì)要查詢的域名進(jìn)行谷歌語(yǔ)法搜索子域名。
敏感目錄文件掃描功能主要是通過讀取本地字典文件,然后拼接URL,并且把拼接后的URL通過Python中的HackRequests模塊進(jìn)行request請(qǐng)求,如果拼接后的URL返回狀態(tài)碼200,那么我們可以判斷拼接后的URL可以正常訪問,也就說明我們從本地字典中讀取到的目錄或者文件是存在的。如果拼接后的URL返回狀態(tài)碼不是200,那么我們從本地字典中讀取到的目錄或文件可能是不存在的。
端口掃描功能主要是通過Python中的Socket模塊創(chuàng)建TCP三次握手連接,并通過返回值是否為0來判斷端口是否存活。以及使用Python中的Nmap模塊,來調(diào)用端口掃描神器Nmap進(jìn)行端口掃描功能。
服務(wù)探測(cè)主要是通過Socket模塊中的sendall方法來發(fā)送請(qǐng)求,然后接收響應(yīng)包并對(duì)響應(yīng)包中的內(nèi)容與本地保存的服務(wù)特征信息進(jìn)行關(guān)鍵字匹配,以此來判斷開放端口對(duì)應(yīng)的服務(wù)類型,同時(shí)輸出返回信息,可以在本地?zé)o法匹配到相關(guān)特征時(shí)進(jìn)行人工判斷服務(wù)類型。
系統(tǒng)以webinfo.py為主程序文件,通過與用戶交互,獲取用戶指令,然后根據(jù)用戶輸入的指令來調(diào)用不同的模塊代碼文件,進(jìn)而實(shí)現(xiàn)對(duì)應(yīng)的功能。
系統(tǒng)主函數(shù)的主要功能是通過與用戶交互,提示用戶輸入正確的選項(xiàng),并根據(jù)用戶的輸入來調(diào)用其他對(duì)應(yīng)的功能函數(shù),完成用戶想要完成的不同功能。同時(shí)應(yīng)做好程序的異常處理機(jī)制,防止因用戶的不正確輸入,而導(dǎo)致程序崩潰的情況發(fā)生,提高程序的健壯性。
if __name__=="__main__":
try:
demo=input("請(qǐng)選擇功能模塊a.cms識(shí)別,b.cdn判斷,c.子域名掃描,d.敏感目錄文件掃描,e.端口掃描服務(wù)探測(cè)(輸入序號(hào)即可):")
if(demo=="a"):
try:
test=int(input("輸入數(shù)字1進(jìn)行單個(gè)url解析cms,輸入數(shù)字2進(jìn)行文件批量解析cms:"))
if(test==1):
try:
domain=input("輸入要檢測(cè)web指紋的url(注意不帶路徑如https://www.baidu.com):")
try:
urllib.request.urlopen(domain)
print("開始調(diào)用本地接口檢測(cè)"+domain+"的cms!")
webcms=webcms(domain)
webcms.run()
print("開始調(diào)用網(wǎng)絡(luò)接口檢測(cè)"+domain+"的cms!")
info=str(cmso2(domain))
print(domain+"解析到的其他信息為:"+info)
except urllib.error.HTTPError:
print("域名有誤,請(qǐng)檢查并按格式輸入!")
time.sleep(2)
except urllib.error.URLError:
print("域名有誤,請(qǐng)檢查并按格式輸入!")
time.sleep(2)
except Exception as e:
print("程序運(yùn)行出錯(cuò)!請(qǐng)檢查并再次嘗試!")
time.sleep(2)
if(test==2):
threads=[20]
filename=input("請(qǐng)輸入要解析的url文件路徑:")
try:
t=threading.Thread(target=cmsfile(filename),args=filename)
for ti in threads:
t.setDaemon(True)
t.start()
for ti in threads:
t.join()
except Exception as e:
print("輸入有誤,或文件路徑找不到,請(qǐng)檢查并按格式輸入!")
time.sleep(2)
except Exception as e:
print("輸入有誤,請(qǐng)檢查并按格式輸入!")
time.sleep(2)
elif(demo=="b"):
cdn.run()
elif(demo=="c"):
subdomain.jkxz()
elif(demo=="d"):
dirfilesm.bprun()
elif(demo=="e"):
portscan.port()
else:
print("輸入出錯(cuò),請(qǐng)重試!")
time.sleep(2)
except Exception as e:
print("程序運(yùn)行出錯(cuò)!請(qǐng)檢查并再次嘗試!")
time.sleep(2)
CMS識(shí)別時(shí)先通過與用戶交互,判斷用戶是進(jìn)行單個(gè)URL識(shí)別還是進(jìn)行批量文件識(shí)別,這一過程實(shí)現(xiàn)方式和主函數(shù)模塊類似,主要是通過if判斷變量test的值。如果test的值為1,則代表用戶選擇單個(gè)URL識(shí)別功能,如果test的值為2,則代表用戶選擇批量文件識(shí)別的功能。批量文件識(shí)別時(shí),主要涉及到Python中文件的操作。
具體識(shí)別時(shí)主要分為本地接口識(shí)別和網(wǎng)絡(luò)接口api識(shí)別兩種方式。本地識(shí)別先通過爬蟲獲取目標(biāo)網(wǎng)站的特征信息,這一過程通過類Downloader來完成。Downloader類主要定義了三個(gè)函數(shù)方法:get,post和download,通過這三個(gè)函數(shù)可以對(duì)目標(biāo)網(wǎng)站進(jìn)行爬蟲,獲取到目標(biāo)網(wǎng)站的基本特征信息。
獲取到的網(wǎng)站特征信息再和本地的josn文件進(jìn)行比對(duì),從而識(shí)別出目標(biāo)網(wǎng)站的CMS信息。這個(gè)過程主要是通過類webcms來實(shí)現(xiàn),類webcms一方面將本地josn文件中的內(nèi)容讀取到隊(duì)列中,另外一方面將爬取到的信息與隊(duì)列中的信息進(jìn)行正則匹配,根據(jù)匹配結(jié)果得出識(shí)別結(jié)論。為了提高程序運(yùn)行效率,需要同時(shí)對(duì)提取本地josn文件內(nèi)容的過程和比對(duì)信息的過程進(jìn)行多線程的操作。
網(wǎng)絡(luò)api識(shí)別接口的實(shí)現(xiàn),主要是對(duì)通過api請(qǐng)求得到的數(shù)據(jù)進(jìn)行二次處理,得到相應(yīng)的CMS信息,同時(shí)也可借助該接口得到目標(biāo)網(wǎng)站的其他相關(guān)信息。
class webcms(object):
workQueue=queue.Queue()
URL=""
threadNum=0
NotFound=True
Downloader=Downloader()
result=""
def __init__(self,url,threadNum=20):
self.URL=url
self.threadNum=threadNum
filename=os.path.join(sys.path[0], "data", "data.json")
fp=open(filename,encoding="utf-8")
webdata=json.load(fp,encoding="utf-8")
for i in webdata:
self.workQueue.put(i)
fp.close()
def getmd5(self, body):
m2=hashlib.md5()
m2.update(body.encode())
return m2.hexdigest()
def th_whatweb(self):
if(self.workQueue.empty()):
self.NotFound=False
return False
if(self.NotFound is False):
return False
cms=self.workQueue.get()
_url=self.URL + cms["url"]
html=self.Downloader.get(_url)
print ("[whatweb log]:checking %s"%_url)
if(html is None):
return False
if cms["re"]:
if(html.find(cms["re"])!=-1):
self.result=cms["name"]
self.NotFound=False
return True
else:
md5=self.getmd5(html)
if(md5==cms["md5"]):
self.result=cms["name"]
self.NotFound=False
return True
def run(self):
while(self.NotFound):
th=[]
for i in range(self.threadNum):
t=threading.Thread(target=self.th_whatweb)
t.start()
th.append(t)
for t in th:
t.join()
if(self.result):
print ("[cmsscan]:%s cms is %s"%(self.URL,self.result))
else:
print ("[cmsscan]:%s cms NOTFound!"%self.URL)
def cmso2(domain):
requests.packages.urllib3.disable_warnings()
response=requests.get(domain,verify=False)
whatweb_dict={"url":response.url,"text":response.text,"headers":dict(response.headers)}
whatweb_dict=json.dumps(whatweb_dict)
whatweb_dict=whatweb_dict.encode()
whatweb_dict=zlib.compress(whatweb_dict)
data={"info":whatweb_dict}
res=requests.post("http://whatweb.bugscaner.com/api.go",files=data)
dic=json.loads(res.text)
if('CMS' in dic.keys()):
info=str(dic['CMS'])
info=info.replace("[","")
info=info.replace("]","")
info=info.replace("'","")
print(domain+"的cms為:"+info)
else:
print(domain+"的cms未能識(shí)別!")
return dic
CDN判斷功能的實(shí)現(xiàn)主要是通過系統(tǒng)中的五個(gè)功能函數(shù),分別對(duì)目標(biāo)域名進(jìn)行CDN檢測(cè),最后再統(tǒng)計(jì)各個(gè)功能函數(shù)的檢測(cè)結(jié)果。當(dāng)五個(gè)功能函數(shù)的檢測(cè)結(jié)果中有三個(gè)或者三個(gè)以上是存在CDN防護(hù)的情況下,可以認(rèn)為目標(biāo)域名存在CDN防護(hù),反之則可以認(rèn)為目標(biāo)域名不存在CDN防護(hù)。這一過程的實(shí)現(xiàn)主要是通過設(shè)定flag,并根據(jù)函數(shù)返回結(jié)果對(duì)flag進(jìn)行加權(quán)賦值,最后再根據(jù)flag的值得出最終的結(jié)果。
五個(gè)功能函數(shù)中的前兩個(gè)函數(shù)主要是通過Socket模塊中的getaddrinfo方法解析域名,以及nslookup查詢域名信息的方法來得到域名對(duì)應(yīng)的IP列表。如果以此得到的目標(biāo)域名的IP數(shù)量在兩個(gè)或者兩個(gè)以上,則說明目標(biāo)域名可能存在CDN防護(hù),這兩個(gè)函數(shù)返回結(jié)果為True,反之則說明目標(biāo)域名可能不存在CDN防護(hù),函數(shù)返回結(jié)果為False。
另外三個(gè)函數(shù)主要借助第三方查詢網(wǎng)站查詢目標(biāo)域名的cname域名信息,并以此判斷目標(biāo)域名是否存在CDN防護(hù)。具體實(shí)現(xiàn)則主要借助爬蟲來完成,同時(shí)對(duì)返回的數(shù)據(jù)信息進(jìn)行篩選處理,得到我們想要的結(jié)果。
def getipo1(domain):
ip_list=[]
flag1=0
ipaddr=socket.getaddrinfo(domain,None)
for item in ipaddr:
if item[4][0] not in ip_list:
ip_list.append(item[4][0])
flag1=flag1+1
return flag1,ip_list
def getipo2(domain):
flag2=0
pi=subprocess.Popen('nslookup {}'.format(domain), shell=True, stdout=subprocess.PIPE)
out=pi.stdout.read().decode('gbk') # 編碼根據(jù)實(shí)際結(jié)果調(diào)整
# 判斷返回值中是否有 Addresses 字段,且該字段下 ip 地址要大于等于 2 個(gè),即說明使用了 CDN
strs=re.findall(r'Addresses:(\s*(((25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\.){3}(25[0-5]|2[0-4]\d|1\d{2}|[1-9]?\d)\s*)*)', out, re.S)
if strs==[]:
return flag2
else:
l=strs[0][0].split('\r\n\t')
for address in l:
flag2=flag2+1
return flag2
def getipo3(domain):
flag3=0
url='http://cdn.chinaz.com/search/?host='+domain
strhtml=requests.get(url)
soup=BeautifulSoup(strhtml.text,'lxml')
#a=soup.find_all(text=(re.compile("可能使用CDN云加速")))
b=soup.find_all(text=(re.compile("不屬于CDN云加速")))
if(b==[]):
flag3=flag3+1
return flag3
if(b!=[]):
return flag3
def getipo4(domain):
flag4=0
info="未知"
url='http://tools.bugscaner.com/api/whichcdn/'
payload={'url':domain}
res=requests.post(url,data=payload)
content=json.loads(res.text)
if(str(content['secess'])=="True"):
flag4=flag4+1
info=content['info']
return flag4,info
if(str(content['secess'])=="False"):
return flag4,info
def getipo5(domain):
flag5=0
info="未知"
#browser=webdriver.PhantomJS(executable_path=r'D:\GeckoDriver\phantomjs-2.1.1-windows\bin\phantomjs.exe')
url='https://tools.ipip.net/cdn.php'
#browser.get(url)
#Cookie=browser.get_cookies()
#browser.close()
#strr=''
#for c in Cookie:
#strr +=c['name']
#strr +='='
#strr +=c['value']
#strr +=';'
cookie="LOVEAPP_SESSID=19676de35da2f3d730a92ceac59888c2d9f44f1b; __jsluid_s=7312e36ccdfd6c67bd2d54a59f5ef9f2; _ga=GA1.2.671769493.1617350155; _gid=GA1.2.268809088.1617350155; Hm_lvt_6b4a9140aed51e46402f36e099e37baf=1617350155; login_r=https%253A%252F%252Ftools.ipip.net%252F;"
payload={'node':663,'host':domain}
user_agent=UserAgent().random
headers={"User-Agent":user_agent,"Cookie":cookie}
res=requests.post(url,data=payload,headers=headers)
#print(res.text)
soup=BeautifulSoup(res.text,'lxml')
data=soup.find_all('td')
#print(data)
a=soup.find_all(text=(re.compile("未知")))
if(a!=[]):
return flag5,info
else:
for item in data:
info1=item.find('a')
info=info1.text
#print(info)
flag5=flag5+1
return flag5,info
域名掃描功能主要是通過本地字典爆破和搜索引擎搜索兩種方法來實(shí)現(xiàn)。其中字典爆破是通過加載本地字典來拼接URL,并對(duì)拼接后的URL進(jìn)行request請(qǐng)求,然后根據(jù)返回的狀態(tài)碼來判斷子域名是否存在。
搜索引擎搜索則主要借助特殊搜索語(yǔ)法site的使用,同時(shí)借助爬蟲技術(shù),對(duì)搜索到的數(shù)據(jù)進(jìn)行篩選處理,進(jìn)而得到目標(biāo)域名的子域名信息。
def bp(url):
user_agent=UserAgent().random
header={"User-Agent":user_agent}
try:
h=HackRequests.hackRequests()
res=h.http(url,headers=header)
if (res.status_code==200):
print("成功爆破出子域名:"+url)
except:
pass
def zymbp(filename,domain):
try:
f=open(filename,encoding='utf8')
lines=f.readlines()
i=-1
for key in lines:
i=i+1
key=lines[i].strip('\n')
url="http://"+key+"."+domain
threads=[20]
t=threading.Thread(target=bp(url),args=url)
for ti in threads:
t.setDaemon(True)
t.start()
for ti in threads:
t.join()
except Exception as e:
print("輸入有誤,或文件路徑找不到,請(qǐng)檢查并按格式輸入!")
time.sleep(2)
def bprun():
filename=input("請(qǐng)輸入要爆破的子域名字典路徑:")
try:
domain=input("請(qǐng)輸入要爆破的域名(格式為:baidu.com):")
try:
threads=[20]
t=threading.Thread(target=zymbp(filename,domain),args=(filename,domain))
for ti in threads:
t.setDaemon(True)
t.start()
for ti in threads:
t.join()
except Exception as e:
print("程序運(yùn)行出錯(cuò)!請(qǐng)檢查并再次嘗試!")
time.sleep(2)
except Exception as e:
print("輸入有誤,或文件路徑找不到,請(qǐng)檢查并按格式輸入!")
time.sleep(2)
def bing_search(site,pages):
subdomain=[]
user_agent=UserAgent().random
headers={'User-Agent':user_agent,'Accept':'*/*','Accept_Language':'en-US,en;q=0.5','Accept-Encoding':'gzip,deflate','referer':"http://cn.bing.com/search?q=email+site%3abaidu.com&qs=n&sp=-1&pq=emailsite%3abaidu.com&first=2&FORM=PERE1"}
for i in range(1,int(pages)+1):
url="https://cn.bing.com/search?q=site%3a"+site+"&go=Search&qs=Search&qs=ds&first="+str((int(i)-1)*10)+"&FORM=PERE"
conn=requests.session()
conn.get('http://cn.bing.com',headers=headers)
html=conn.get(url,stream=True,headers=headers,timeout=8)
soup=BeautifulSoup(html.content,'html.parser')
job_bt=soup.findAll('h2')
for i in job_bt:
link=i.a.get('href')
domain=str(urlparse(link).scheme+"://"+urlparse(link).netloc)
if(domain in subdomain):
pass
else:
subdomain.append(domain)
print("成功搜索出子域名:"+domain)
def runbing():
try:
site=input("請(qǐng)輸入要查詢的域名(格式為:baidu.com):")
page=int(input("請(qǐng)輸入查詢的頁(yè)數(shù):"))
try:
bing_search(site,page)
except Exception as e:
print("程序運(yùn)行出錯(cuò)!請(qǐng)檢查并再次嘗試!")
time.sleep(2)
except Exception as e:
print("輸入有誤,請(qǐng)檢查并按格式輸入!")
time.sleep(2)
敏感目錄文件的掃描功能主要是通過加載本地字典文件,對(duì)當(dāng)前URL進(jìn)行拼接,然后再借助HackRequests庫(kù)對(duì)拼接后的URL進(jìn)行request請(qǐng)求驗(yàn)證。當(dāng)返回狀態(tài)碼為200時(shí),則認(rèn)為當(dāng)前請(qǐng)求的目錄或者文件存在。
def dirfilebp(filename,domain):
try:
f=open(filename,encoding='utf8')
lines=f.readlines()
i=-1
for key in lines:
i=i+1
key=str(lines[i].strip('\n'))
url=domain+key
threads=[20]
t=threading.Thread(target=bp(url),args=url)
for ti in threads:
t.setDaemon(True)
t.start()
for ti in threads:
t.join()
except Exception as e:
print("輸入有誤,或文件路徑找不到,請(qǐng)檢查并按格式輸入!")
time.sleep(2)
def bp(url):
user_agent=UserAgent().random
header={"User-Agent":user_agent}
try:
h=HackRequests.hackRequests()
res=h.http(url,headers=header)
if (res.status_code==200):
print("成功爆破出目錄或文件:"+url)
except:
pass
端口掃描功能一方面是借助Python中的Socket模塊創(chuàng)建TCP三次握手連接,并通過返回值是否為0來判斷端口是否存活。另外一方面則是借助Python中的Nmap模塊,來調(diào)用端口掃描神器Nmap進(jìn)行端口掃描功能。
服務(wù)探測(cè)主要是通過Socket模塊中的sendall方法來發(fā)送請(qǐng)求,然后接收響應(yīng)包并對(duì)響應(yīng)包中的內(nèi)容與本地保存的服務(wù)特征信息進(jìn)行關(guān)鍵字匹配,以此來判斷開放端口對(duì)應(yīng)的服務(wù)類型,同時(shí)輸出返回信息,可以在本地?zé)o法匹配到相關(guān)特征時(shí)進(jìn)行人工判斷服務(wù)類型。
def sorun(queue_s,ip):
while not queue_s.empty():
try:
port=queue_s.get()
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.settimeout(1)
c=s.connect_ex((ip,port))
if (c==0):
print ("%s:%s is open" % (ip,port))
else:
# print "%s:%s is not open" % (ip,port)
pass
except:
pass
def somain(ip,spo,epo):
threads=[]
threads_count=100 # 線程數(shù),默認(rèn) 100
queue_s=queue.Queue()
#ip=ip
try:
for i in range(spo,epo+1): # 默認(rèn)掃描1-1000的端口,可以手動(dòng)修改這里的端口范圍
queue_s.put(i) # 使用 queue.Queue().put() 方法將端口添加到隊(duì)列中
for i in range(threads_count):
threads.append(sorun(queue_s,ip)) # 掃描的端口依次添加到線程組
for i in threads:
i.start()
for i in threads:
i.join()
except:
pass
def nmscan(hosts,port):
nm=nmap.PortScanner()
nm.scan(hosts=hosts, arguments=' -v -sS -p '+port)
try:
for host in nm.all_hosts():
print('----------------------------------------------------') #輸出主機(jī)及主機(jī)名
print('Host : %s (%s)' % (host, nm[host].hostname())) #輸出主機(jī)狀態(tài),如up、down
print('State : %s' % nm[host].state())
for proto in nm[host].all_protocols(): #遍歷掃描協(xié)議,如tcp、udp
print('----------') #輸入?yún)f(xié)議名
print('Protocol : %s' % proto) #獲取協(xié)議的所有掃描端口
lport=nm[host][proto].keys() #端口列表排序
list(lport).sort() #遍歷端口及輸出端口與狀態(tài)
for port in lport:
print('port : %s\tstate : %s' % (port, nm[host][proto][port]['state']))
except:
pass
def regex(response, port):
text=""
if re.search(b'<title>502 Bad Gateway', response):
proto={"Service failed to access!!"}
for pattern in SIGNS:
pattern=pattern.split(b'|')
if re.search(pattern[-1], response, re.IGNORECASE):
proto="["+port+"]" + " open " + pattern[1].decode()
break
else:
proto="["+port+"]" + " open " + "Unrecognized"
print(proto)
def request(ip,port):
response=''
PROBE='GET / HTTP/1.0\r\n\r\n'
sock=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(10)
result=sock.connect_ex((ip, int(port)))
if result==0:
try:
sock.sendall(PROBE.encode())
response=sock.recv(256)
print(response)
if response:
regex(response, port)
except ConnectionResetError:
pass
else:
pass
sock.close()
def fwmain(ip,port):
print("Scan report for "+ip+"\n")
for line in port.split(','):
request(ip,line)
time.sleep(0.2)
print("\nScan finished!....\n")
運(yùn)行演示如下圖
代碼地址:https://github.com/twsec-pro/twsecBS
文章來自https://www.cnblogs.com/TWX521/p/16308471.html
果圖
各位朋友大家好!
今天給大家?guī)淼氖?純CSS3超酷書架樣式404頁(yè)面動(dòng)畫特效!
有喜歡的可以按照自己的意愿進(jìn)行修改!
有想要文件版源碼的可以私聊我!
廢話不多說 上源碼!
@font-face {
font-family: 'icomoon';
src:url('../fonts/icomoon.eot?rretjt');
src:url('../fonts/icomoon.eot?#iefixrretjt') format('embedded-opentype'),
url('../fonts/icomoon.woff?rretjt') format('woff'),
url('../fonts/icomoon.ttf?rretjt') format('truetype'),
url('../fonts/icomoon.svg?rretjt#icomoon') format('svg');
font-weight: normal;
font-style: normal;
}
avascript
有下面一組數(shù)據(jù)
const data=[
{ name:'小明', score:'90',age:10 },
{ name:'小王', score:'100',age:9 },
{ name:'小趙', score:'80',age:9 },
{ name:'小強(qiáng)', score:'97',age:8 }
]
我們現(xiàn)在的需求是對(duì)score和age兩列進(jìn)行求和計(jì)算。下面我們先對(duì)求和方法進(jìn)行封裝,這里我分享兩種常用的方法。
方法一:采用遞歸的方式
export const getTotal=(data,key)=> {
var len=data.length;
if(len==0){
return 0;
} else if (len==1){
// 特殊情況;當(dāng)其中某個(gè)值為空時(shí)默認(rèn)為數(shù)字0
return Number(data[0][key]?data[0][key]:0);
} else {
// 特殊情況;當(dāng)其中某個(gè)值為空時(shí)默認(rèn)為數(shù)字0
// 遞歸的方式調(diào)用getTotal方法
return Number(data[0][key]?data[0][key]:0) + getTotal(data.slice(1),key);
}
}
// 調(diào)用 getTotal(data,'score')或 getTotal(data,'age')
方法二:函數(shù)式編程reduce
export const getTotal=(data,key)=> {
// 一維數(shù)組求和方法reduce
return data.reduce(function(prev, curr, idx, arr){
if(typeof prev=='object') {
return Number(prev[key]) + Number(curr[key]);
}else{
return prev + Number(curr[key]);
}
});
}
// 調(diào)用 getTotal(data,'score')或 getTotal(data,'age')
然后我們?nèi)绻陧?xiàng)目中使用可以遍歷age和score兩個(gè)字段進(jìn)行調(diào)用公共方法實(shí)現(xiàn)多個(gè)列求和。
如果求和之后數(shù)據(jù)需要保留小數(shù)點(diǎn)后兩位或三位有效數(shù)字,可以根據(jù)具體情況進(jìn)行相應(yīng)封裝,我們這里就不做一一分享了。
*請(qǐng)認(rèn)真填寫需求信息,我們會(huì)在24小時(shí)內(nèi)與您取得聯(lián)系。