python如何编写类似nmap的扫描工具


Posted in Python onNovember 06, 2020

本文主要是利用scapy包编写了一个简易扫描工具,支持ARP、ICMP、TCP、UDP发现扫描,支持TCP SYN、UDP端口扫描,如下:

usage: python scan.py <-p ping扫描类型> <-s 端口发现类型> [-t target] [--port ports]

简单扫描工具,可以进行存活扫描及端口扫描.
存活扫描包括:ARP扫描、ICMP扫描、TCP扫描、UDP扫描.
端口扫描包括:TCP SYN扫描、TCP ACK扫描、TCP FIN扫描.

optional arguments:
 -h, --help     show this help message and exit
 -v, --version   show program's version number and exit

target group:
 用于设置IP、PORT参数

 --target TARGET  target为IP或IP段,如192.168.1.1,192.168.1.x,或1
           92.168.1.1-254
 --port PORT    port为待扫描的端口,如21,80,...或21-80

ping group:
 用于开启存活扫描相关选项

 -p         开启存活扫描
 --ARP       启动ARP扫描
 --ICMP       启动ICMP扫描
 --TCP       启动TCP扫描
 --UDP       启动UDP扫描

port scan group:
 用于开启端口扫描相关选项

 -s         开启端口扫描
 --SYN       开启SYN扫描
 --ACK       开启ACK扫描
 --FIN       开启FIN扫描
 --UPORT      开启UDP端口扫描

utils group:
 用于开启扫描过程中的一些实用选项

 --timeout TIMEOUT 设置发包超时时间,默认0.5秒
 --retry RETRY   设置发包重试次数,默认不重试

以上做为说明,祝好运!

一、发现扫描

1.首先进行ARP扫描

python scan.py -p --target 192.168.1.1-254 --ARP
[+]IP: 192.168.1.1 => MAC: 14:75:90:xx:xx:xx
[+]IP: 192.168.1.111 => MAC: c6:36:55:xx:xx:xx
[+]总共耗时9.84091806412秒.

通过retry参数增加发包尝试次数,如下:

python scan.py -p --target 192.168.1.1-254 --ARP --retry 2
[+]IP: 192.168.1.1 => MAC: 14:75:90:xx:xx:xx
[+]IP: 192.168.1.111 => MAC: c6:36:55:xx:xx:xx
[+]IP: 192.168.1.102 => MAC: 58:1f:28:xx:xx:xx
[+]IP: 192.168.1.114 => MAC: 6c:8d:c1:xx:xx:xx
[+]IP: 192.168.1.103 => MAC: 84:38:38:xx:xx:xx
[+]总共耗时20.429942131秒.

2.使用ICMP扫描,若没有指定任何扫描类型参数,默认会启用ICMP扫描,如下:

python scan.py -p --target 192.168.1.1-254
[+]没有指定任何ping扫描方式,默认选择ICMP扫描
[+]IP:主机192.168.1.1 echo-reply.
[+]IP:主机192.168.1.111 echo-reply.
[+]总共耗时10.7177450657秒.

通过timeout参数,设置较长的超时,可以防止网络状况不好造成的丢包,如下:

python scan.py -p --target 192.168.1.1-254 --timeout 2
[+]没有指定任何ping扫描方式,默认选择ICMP扫描
[+]IP:主机192.168.1.1 echo-reply.
[+]IP:主机192.168.1.111 echo-reply.
[+]IP:主机192.168.1.114 echo-reply.
[+]总共耗时10.7566649914秒.

3.使用TCP扫描

python scan.py -p --target 192.168.1.100-120 --TCP --timeout 1
[+]请稍等,时间较长!
[!]扫描... 192.168.1.100
[!]扫描... 192.168.1.101
[!]扫描... 192.168.1.102
[!]扫描... 192.168.1.103
[!]扫描... 192.168.1.104
[!]扫描... 192.168.1.105
[!]扫描... 192.168.1.106
[!]扫描... 192.168.1.107
[!]扫描... 192.168.1.108
[!]扫描... 192.168.1.109
[!]扫描... 192.168.1.110
[!]扫描... 192.168.1.111
[!]扫描... 192.168.1.112
[!]扫描... 192.168.1.113
[!]扫描... 192.168.1.114
[!]扫描... 192.168.1.115
[!]扫描... 192.168.1.116
[!]扫描... 192.168.1.117
[!]扫描... 192.168.1.118
[!]扫描... 192.168.1.119
[!]扫描... 192.168.1.120
[+]正在处理扫描信息.
====================
[+]主机 192.168.1.102 在线.
[+]主机 192.168.1.103 在线.
[+]主机 192.168.1.111 在线.
[+]主机 192.168.1.114 在线.
[+]总共耗时16.4359779358秒.

4.使用UDP扫描

python scan.py -p --target 192.168.1.100-120 --UDP --retry 3
[+]请稍等,时间较长!
[!]扫描... 192.168.1.100
[!]扫描... 192.168.1.101
[!]扫描... 192.168.1.102
[!]扫描... 192.168.1.103
[!]扫描... 192.168.1.104
[!]扫描... 192.168.1.105
[!]扫描... 192.168.1.106
[!]扫描... 192.168.1.107
[!]扫描... 192.168.1.108
[!]扫描... 192.168.1.109
[!]扫描... 192.168.1.110
[!]扫描... 192.168.1.111
[!]扫描... 192.168.1.112
[!]扫描... 192.168.1.113
[!]扫描... 192.168.1.114
[!]扫描... 192.168.1.115
[!]扫描... 192.168.1.116
[!]扫描... 192.168.1.117
[!]扫描... 192.168.1.118
[!]扫描... 192.168.1.119
[!]扫描... 192.168.1.120
[+]正在处理扫描信息.


====================
[+]主机 192.168.1.102 在线.
[+]主机 192.168.1.103 在线.
[+]主机 192.168.1.111 在线.
[+]主机 192.168.1.114 在线.
[+]总共耗时33.5198891163秒.

二、端口扫描

1、TCP SYN端口扫描,不设置端口参数,则默认扫描1-1024端口

python scan.py --target 192.168.1.110-115 -s --SYN
[+]没有指定任何扫描端口,默认扫描1-1024
[!]扫描... 192.168.1.110
[!]扫描... 192.168.1.111
[!]扫描... 192.168.1.112
[!]扫描... 192.168.1.113
[!]扫描... 192.168.1.114
[!]扫描... 192.168.1.115
[+]正在处理扫描信息.


====================
[+]主机 192.168.1.111 开放的TCP端口有:[80]
[+]总共耗时165.125555992秒.

扫描指定端口:

python scan.py --target 192.168.1.1-254 -s --SYN --port 80 --timeout 1
[!]扫描... 192.168.1.1
[!]扫描... 192.168.1.2
[!]扫描... 192.168.1.3
[!]扫描... 192.168.1.4
...
[!]扫描... 192.168.1.253
[!]扫描... 192.168.1.254
[+]正在处理扫描信息.


====================
[+]主机 192.168.1.111 开放的TCP端口有:[80]
[+]主机 192.168.1.1 开放的TCP端口有:[80]
[+]总共耗时9.72222185135秒.

2、扫描UDP端口

python scan.py --target 192.168.1.1 -s --UPORT --timeout 1
[+]没有指定任何扫描端口,默认扫描1-1024
[!]扫描... 192.168.1.1
[+]正在处理扫描信息.
====================
[+]主机 192.168.1.1 开放的UDP端口有:[520]
[+]总共耗时27.4742250443秒.

也可同时进行发现扫描与端口扫描,如下:

python scan.py --target 192.168.1.1-254 -p --ARP -s --SYN --port 80 --timeout 1 --retry 2
[+]IP: 192.168.1.1 => MAC: 14:75:90:xx:xx:xx
[+]IP: 192.168.1.102 => MAC: 58:1f:28:xx:xx:xx
[+]IP: 192.168.1.114 => MAC: 6c:8d:c1:xx:xx:xx
[+]IP: 192.168.1.103 => MAC: 84:38:38:xx:xx:xx
[+]IP: 192.168.1.101 => MAC: 5c:f7:e6:xx:xx:xx
[!]扫描... 192.168.1.1
[!]扫描... 192.168.1.2
...
[!]扫描... 192.168.1.253
[!]扫描... 192.168.1.254
[+]正在处理扫描信息.


====================
[+]主机 192.168.1.1 开放的TCP端口有:[80]
[+]主机 192.168.1.111 开放的TCP端口有:[80]
[+]总共耗时45.2775988579秒.

OK,最后附上源码:

import argparse
import re
import time
import threading
from scapy.all import *

import logging
logging.getLogger('scapy.runtime').setLevel(logging.ERROR)


class Discovery_Scan(object):
  '''
  说明:用于发现扫描
  '''

  def __init__(self,args,timeout=0.5,retry=0):
    self.targets = parse_target(args)
    self.timeout = timeout
    self.retry = retry

  def arp_scan(self,pdst):
    #ARP发现扫描
    ans = sr1(ARP(pdst=pdst),timeout=self.timeout,retry=self.retry,verbose=False)
    if ans:
      if ans[ARP].op == 2:  #操作码为2是is-at,是ARP响应
        print '[+]IP: %s => MAC: %s' % (pdst,ans[ARP].hwsrc)

  def icmp_scan(self,dst):
    #ICMP发现扫描
    ans = sr1(IP(dst=dst)/ICMP(),timeout=self.timeout,retry=self.retry,verbose=False)
    if ans:
      if ans[ICMP].type == 0: #ICMP type为0表示是ICMP echo-reply
        print '[+]IP:主机%s echo-reply.' % dst

  tcp_info = {}
  def tcp_scan(self,dst,port):
    #TCP SYN,发送TCP SYN包,有响应表示端口开放
    ans,unans = sr(IP(dst=dst)/TCP(sport=RandShort(),dport=port,flags='S'),
            timeout=self.timeout,retry=self.retry,verbose=False)
    if ans.res:
      if ans.res[0][0][IP].dst not in Discovery_Scan.tcp_info:
        Discovery_Scan.tcp_info[ans.res[0][0][IP].dst] = True

  udp_info = {}
  def udp_scan(self,dst,port):
    #UDP,发送UDP包,有响应表示端口开放
    ans,uans = sr(IP(dst=dst)/UDP(sport=RandShort(),dport=port),
           timeout=self.timeout,retry=self.retry,verbose=False)
    if ans.res:
      if ans.res[0][0][IP].dst not in Discovery_Scan.udp_info:
        Discovery_Scan.udp_info[ans.res[0][0][IP].dst] = True

class Port_Scan(object):
  '''
  说明:用于进行端口扫描,判断端口是否开放
  '''
  def __init__(self,args,timeout=0.5,retry=0):
    self.targets = parse_target(args)
    self.timeout = timeout
    self.retry = retry

  syn_port_dict = {}
  def syn_port_scan(self,dst,port):
    #TCP SYN端口扫描,若SYN包返回携带SYN、ACK(即TCP.flags=18)标志的包,则表明此端口打开。
    ans,uans = sr(IP(dst=dst)/TCP(sport=RandShort(),dport=port,flags='S'),
           timeout=self.timeout,retry=self.retry,verbose=False)
    if ans:
      first_respons_pkt = ans.res[0][1]
      if first_respons_pkt[TCP] and first_respons_pkt[TCP].flags == 18:
        if first_respons_pkt[IP].src not in Port_Scan.syn_port_dict:
          Port_Scan.syn_port_dict[first_respons_pkt[IP].src] = [first_respons_pkt[TCP].sport]
        else:
          Port_Scan.syn_port_dict[first_respons_pkt[IP].src].append(first_respons_pkt[TCP].sport)

  udp_port_dict = {}
  def udp_port_scan(self,dst,port):
    #UDP端口扫描,若UDP端口返回ICMP port-unreachable,则表示端口打开。(排除某些主机对任何UDP端口的探测都响应为ICMP port-unrechable)
    ans,uans = sr(IP(dst=dst)/UDP(sport=RandShort(),dport=port),
           timeout=self.timeout, retry=self.retry, verbose=False)
    if ans.res and ans.res[0][1].haslayer(UDPerror):
      first_respons_pkt = ans.res[0][1]
      if first_respons_pkt[IP].src not in Port_Scan.udp_port_dict:
        Port_Scan.udp_port_dict[first_respons_pkt[IP].src] = [first_respons_pkt[UDPerror].dport]
      else:
        Port_Scan.udp_port_dict[first_respons_pkt[IP].src].append(first_respons_pkt[UDPerror].dport)

def parse_opt():
  '''
  @说明:通过argparse模块解析程序传入的参数
  @return:args
  '''
  usage = 'python %(prog)s <-p ping扫描类型> <-s 端口发现类型> [-t target] [--port ports]'
  description = '简单扫描工具,可以进行存活扫描及端口扫描.\n' \
         '存活扫描包括:ARP扫描、ICMP扫描、TCP扫描、UDP扫描.\n' \
         '端口扫描包括:TCP SYN扫描、TCP ACK扫描、TCP FIN扫描.'
  epilog = '以上做为说明,祝好运!'
  parser = argparse.ArgumentParser(usage=usage,description=description,epilog=epilog,version='v1.0')

  target_group = parser.add_argument_group('target group',description='用于设置IP、PORT参数')
  target_group.add_argument('--target',dest='target',action='store',
               help='target为IP或IP段,如192.168.1.1,192.168.1.x,或192.168.1.1-254')
  target_group.add_argument('--port',dest='port',action='store',
               help='port为待扫描的端口,如21,80,...或21-80')

  ping_group = parser.add_argument_group('ping group',description='用于开启存活扫描相关选项')
  ping_group.add_argument('-p',dest='ping',action='store_true',help='开启存活扫描')
  ping_group.add_argument('--ARP',dest='ARP',action='store_true',help='启动ARP扫描')
  ping_group.add_argument('--ICMP',dest='ICMP',action='store_true',help='启动ICMP扫描')
  ping_group.add_argument('--TCP',dest='TCP',action='store_true',help='启动TCP扫描')
  ping_group.add_argument('--UDP',dest='UDP',action='store_true',help='启动UDP扫描')

  port_scan_group = parser.add_argument_group('port scan group',description='用于开启端口扫描相关选项')
  port_scan_group.add_argument('-s',dest='scan',action='store_true',help='开启端口扫描')
  port_scan_group.add_argument('--SYN',dest='SYN',action='store_true',help='开启SYN扫描')
  port_scan_group.add_argument('--ACK',dest='ACK',action='store_true',help='开启ACK扫描')
  port_scan_group.add_argument('--FIN',dest='FIN',action='store_true',help='开启FIN扫描')
  port_scan_group.add_argument('--UPORT', dest='UPORT', action='store_true', help='开启UDP端口扫描')

  utils_group = parser.add_argument_group('utils group',description='用于开启扫描过程中的一些实用选项')
  utils_group.add_argument('--timeout',dest='timeout',action='store',type=float,help='设置发包超时时间,默认0.5秒')
  utils_group.add_argument('--retry',dest='retry',action='store',type=int,help='设置发包重试次数,默认不重试')

  args = parser.parse_args()
  if not args.ping and not args.scan:
    print '[-]必须通过-p/-s选项开启一种扫描'
    print '\n'
    parser.print_help()
    exit(1)
  elif not args.target:
    print '[-]必须通过--target选项指定扫描的对象'
    print '\n'
    parser.print_help()
    exit(1)
  if args.ping:
    if not args.ARP and not args.ICMP and not args.TCP and not args.UDP:
      args.ICMP = True #若没有指定任何ping扫描方式,则默认选择ICMP扫描
      print '[+]没有指定任何ping扫描方式,默认选择ICMP扫描'
  if args.scan:
    if not args.SYN and not args.ACK and not args.FIN and not args.UPORT:
      args.SYN = True  #若没有指定任何端口扫描方式,则默认选择SYN扫描
      print '[+]没有指定任何端口扫描方式,默认选择SYN扫描'
    if not args.port:
      args.port = '1-1024'  #若没有指定任何扫描端口,则默认扫描1-1024
      print '[+]没有指定任何扫描端口,默认扫描1-1024'

  return args

def parse_target(args):
  '''
  @说明:用于解析如'192.168.1.1,192.168.1.x,...或192.168.1.1-254'格式的IP为单独的IP,用于解析如'21,80,...或21-80'格式的端口为单独的端口
  @param: args,一个namespace对象
  @return: (ip_list,port_list)
  '''
  pattern1 = r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'
  pattern2 = r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}-\d{1,3}$'
  pattern3 = r'\d{1,5}$'
  pattern4 = r'\d{1,5}-\d{1,5}$'
  ip_list,port_list = None,None
  if args.target:
    if re.search(pattern1,args.target):
      ip_list = args.target.split(',')
    elif re.match(pattern2,args.target):
      _split = args.target.split('-')
      first_ip = _split[0]
      ip_split = first_ip.split('.')
      ipdot4 = range(int(ip_split[3]), int(_split[1]) + 1)
      ip_list = [ip_split[0] + '.' + ip_split[1] + '.' + ip_split[2] + '.' + str(p) for p in ipdot4]
    else:
      print '[-]target格式输入有误,请查看帮助!'
      exit(1)
  if args.port:
    if re.match(pattern4,args.port):
      _split = args.port.split('-')
      port_list = range(int(_split[0]),int(_split[1])+1)
    elif re.search(pattern3,args.port):
      port_list = args.port.split(',')
    else:
      print '[-]port格式输入有误,请查看帮助!'
      exit(1)
  return ip_list,port_list


def main():
  '''
  @说明:扫描的主程序,首先根据条件创建Ping扫描或端口扫描对象,然后调用相关的扫描方法进行扫描。
  '''
  args = parse_opt()
  if args.ping: #是否启动Ping扫描
    if not args.timeout and not args.retry:
      obj_ping = Discovery_Scan(args)
    elif args.timeout and not args.retry:
      obj_ping = Discovery_Scan(args,timeout=args.timeout)
    elif not args.timeout and args.retry:
      obj_ping = Discovery_Scan(args,retry=args.retry)
    else:
      obj_ping = Discovery_Scan(args,args.timeout,args.retry)
    ip_list = obj_ping.targets[0]
    if ip_list:
      #ARP扫描
      if args.ARP:
        for pdst in ip_list:
          t = threading.Thread(target=obj_ping.arp_scan,args=(pdst,))
          t.start()
        while threading.activeCount() != 1:   #避免线程还没有运行完就提前输出不全的结果
          time.sleep(1)
      #ICMP扫描
      elif args.ICMP:
        for dst in ip_list:
          t = threading.Thread(target=obj_ping.icmp_scan,args=(dst,))
          t.start()
        while threading.activeCount() != 1:   #避免线程还没有运行完就提前输出不全的结果
          time.sleep(1)
      #TCP扫描
      elif args.TCP:
        port_list = [80,443,21,22,23,25,53,135,139,137,445,1158,1433,1521,3306,3389,7001,8000,8080,9090]
        print '[+]请稍等,时间较长!'
        for dst in ip_list:
          print '[!]扫描...',dst
          for port in port_list:
            t = threading.Thread(target=obj_ping.tcp_scan,args=(dst,port))
            t.start()

        print '[+]正在处理扫描信息.'
        while threading.activeCount() != 1:   #避免线程还没有运行完就提前输出不全的结果
          time.sleep(1)

        if not obj_ping.tcp_info:
          print '\n'
          print '=' * 20
          print '[+]未发现在线主机.'
        else:
          print '\n'
          print '=' * 20
          for ip_a in sorted(obj_ping.tcp_info.keys()):
            print '[+]主机 %s 在线.' % ip_a
      #UDP扫描
      elif args.UDP:
        port_list = [7,9.13,15,37,53,67,68,69,135,137,138,139,445,520]
        print '[+]请稍等,时间较长!'
        for dst in ip_list:
          print '[!]扫描...',dst
          for port in port_list:
            t = threading.Thread(target=obj_ping.udp_scan,args=(dst,port))
            t.start()

        print '[+]正在处理扫描信息.'
        while threading.activeCount() != 1:   #避免线程还没有运行完就提前输出不全的结果
          time.sleep(1)

        if not obj_ping.udp_info:
          print '\n'
          print '=' * 20
          print '[+]未发现在线主机.'
        else:
          print '\n'
          print '=' * 20
          for ip_a in sorted(obj_ping.udp_info.keys()):
            print '[+]主机 %s 在线.' % ip_a
  if args.scan:  #是否启动端口扫描
    if not args.timeout and not args.retry:
      obj_port = Port_Scan(args)
    elif args.timeout and not args.retry:
      obj_port = Port_Scan(args,timeout=args.timeout)
    elif not args.timeout and args.retry:
      obj_port = Port_Scan(args,retry=args.retry)
    else:
      obj_port = Port_Scan(args,args.timeout,args.retry)

    ip_list,port_list = obj_port.targets
    if ip_list and port_list:
      if args.SYN:
        for dst in ip_list:
          print '[!]扫描...',dst
          for port in port_list:
            t = threading.Thread(target=obj_port.syn_port_scan,args=(dst,int(port)))
            t.start()

        print '[+]正在处理扫描信息.'
        while threading.activeCount() != 1:   #避免线程还没有运行完就提前输出不全的结果
          time.sleep(1)

        if not obj_port.syn_port_dict:
          print '\n'
          print '=' * 20
          print '[+]未发现开放TCP端口.'
        else:
          print '\n'
          print '=' * 20
          for k,v in obj_port.syn_port_dict.items():
            print '[+]主机 %s 开放的TCP端口有:%s' % (k,str(v))
      elif args.ACK:
        pass  #基本不能使用
      elif args.FIN:
        pass  #基本不能使用
      elif args.UPORT:
        for dst in ip_list:
          print '[!]扫描...',dst
          for port in port_list:
            t = threading.Thread(target=obj_port.udp_port_scan,args=(dst,int(port)))
            t.start()

        print '[+]正在处理扫描信息.'
        while threading.activeCount() != 1:   #避免线程还没有运行完就提前输出不全的结果
          time.sleep(1)

        if not obj_port.udp_port_dict:
          print '\n'
          print '=' * 20
          print '[+]未发现开放UDP端口.'
        else:
          print '\n'
          print '=' * 20
          for k,v in obj_port.udp_port_dict.items():
            print '[+]主机 %s 开放的UDP端口有:%s' % (k,str(v))

if __name__ == '__main__':
  try:
    start_time = time.time()
    main()
    stop_time = time.time()
    print '[+]总共耗时'+str(stop_time-start_time)+'秒.'
  except Exception,e:
    print '[-]执行出错,具体错误见下面信息.'
    print e

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
flask使用session保存登录状态及拦截未登录请求代码
Jan 19 Python
Python使用装饰器进行django开发实例代码
Feb 06 Python
图解Python变量与赋值
Apr 03 Python
Pandas实现数据类型转换的一些小技巧汇总
May 07 Python
Python使用Pickle库实现读写序列操作示例
Jun 15 Python
Django 路由控制的实现代码
Nov 08 Python
Django中使用Celery的方法示例
Nov 29 Python
Python 按字典dict的键排序,并取出相应的键值放于list中的实例
Feb 12 Python
Python3爬楼梯算法示例
Mar 04 Python
python实现简单颜色识别程序
Feb 19 Python
Python SMTP配置参数并发送邮件
Jun 16 Python
Python实现聚类K-means算法详解
Jul 15 Python
Python常用base64 md5 aes des crc32加密解密方法汇总
Nov 06 #Python
基于Python模拟浏览器发送http请求
Nov 06 #Python
python如何写个俄罗斯方块
Nov 06 #Python
基于Python实现全自动下载抖音视频
Nov 06 #Python
Python3读写ini配置文件的示例
Nov 06 #Python
Python Serial串口基本操作(收发数据)
Nov 06 #Python
python基于exchange函数发送邮件过程详解
Nov 06 #Python
You might like
php 无限级分类学习参考之对ecshop无限级分类的解析 带详细注释
2010/03/23 PHP
php 中的4种标记风格介绍
2012/05/10 PHP
PHP加密函数 Javascript/Js 解密函数
2013/09/23 PHP
取得单条网站评论以数组形式进行输出
2014/07/28 PHP
php遍历类中包含的所有元素的方法
2015/05/12 PHP
基于jquery封装的一个js分页
2011/11/15 Javascript
对table和ul实现js分页示例分享
2014/02/24 Javascript
jquery使用正则表达式验证email地址的方法
2015/01/22 Javascript
NodeJS学习笔记之Connect中间件应用实例
2015/01/27 NodeJs
jquery Easyui快速开发总结
2015/08/20 Javascript
JS简单实现多级Select联动菜单效果代码
2015/09/06 Javascript
BootStrap Typeahead自动补全插件实例代码
2016/08/10 Javascript
JQuery validate 验证一个单独的表单元素实例
2017/02/17 Javascript
浅谈Angular 的变化检测的方法
2018/03/01 Javascript
详解vue-router 命名路由和命名视图
2018/06/01 Javascript
vant自定义二级菜单操作
2020/11/02 Javascript
Vue实现随机验证码功能
2020/12/29 Vue.js
jenkins自动构建发布vue项目的方法步骤
2021/01/04 Vue.js
[01:45]绝对公平!DOTA2队长征召模式详解
2014/04/25 DOTA
使用Kivy将python程序打包为apk文件
2017/07/29 Python
对python中return和print的一些理解
2017/08/18 Python
python微元法计算函数曲线长度的方法
2018/11/08 Python
python矩阵/字典实现最短路径算法
2019/01/17 Python
Python实现分数序列求和
2020/02/25 Python
Python检测端口IP字符串是否合法
2020/06/05 Python
在终端启动Python时报错的解决方案
2020/11/20 Python
Whittard官方海外旗舰店:英国百年茶叶品牌
2018/02/22 全球购物
澳大利亚领先的女帽及配饰公司:Morgan&Taylor
2019/12/01 全球购物
美国家用和厨房电器销售网站:Appliances Connection
2020/01/24 全球购物
分解成质因数(如435234=251*17*17*3*2,据说是华为笔试题)
2014/07/16 面试题
优良学风班总结材料
2014/02/08 职场文书
自我鉴定标准格式
2014/03/19 职场文书
党员教师群众路线思想汇报范文
2014/10/28 职场文书
我们的节日元宵节活动总结
2015/02/06 职场文书
导游词之广西漓江
2019/11/02 职场文书
MySQL中的布尔值,怎么存储false或true
2021/06/04 MySQL