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 相关文章推荐
Python编程中对文件和存储器的读写示例
Jan 25 Python
TensorFlow中权重的随机初始化的方法
Feb 11 Python
浅谈pycharm出现卡顿的解决方法
Dec 03 Python
Python3.7 新特性之dataclass装饰器
May 27 Python
python之mock模块基本使用方法详解
Jun 27 Python
Django框架创建mysql连接与使用示例
Jul 29 Python
基于Python新建用户并产生随机密码过程解析
Oct 08 Python
python pygame实现球球大作战
Nov 25 Python
python 实现保存最新的三份文件,其余的都删掉
Dec 22 Python
python使用配置文件过程详解
Dec 28 Python
python实现读取类别频数数据画水平条形图案例
Apr 24 Python
python 批量下载bilibili视频的gui程序
Nov 20 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
重置版宣传动画
2020/04/09 魔兽争霸
PHP mysql与mysqli事务使用说明 分享
2013/08/17 PHP
php file_get_contents抓取Gzip网页乱码的三种解决方法
2013/11/12 PHP
php通过正则表达式记取数据来读取xml的方法
2015/03/09 PHP
PHP获取用户访问IP地址的5种方法
2016/05/16 PHP
php遍历替换目录下文件指定内容的方法
2016/11/10 PHP
PHP和MYSQL实现分页导航思路详解
2017/04/11 PHP
php注册系统和使用Xajax即时验证用户名是否被占用
2017/08/31 PHP
PDO::lastInsertId讲解
2019/01/29 PHP
jQuery Ajax 全解析
2009/02/08 Javascript
jquery.validate使用攻略 第一部
2010/07/01 Javascript
js中的scroll和offset 使用比较的实例与分析
2013/09/29 Javascript
jQuery 获取兄弟元素的几种不错方法
2014/05/23 Javascript
jquery单选框radio绑定click事件实现方法
2015/01/14 Javascript
jquery实现Slide Out Navigation滑出式菜单效果代码
2015/09/07 Javascript
浅谈angular2的http请求返回结果的subcribe注意事项
2017/03/01 Javascript
简单谈谈关于 npm 5.0 的新坑
2017/06/08 Javascript
原生JS获取元素的位置与尺寸实现方法
2017/10/18 Javascript
vue单页面实现当前页面刷新或跳转时提示保存
2018/11/02 Javascript
[14:00]DOTA2国际邀请赛史上最长大战 赛后专访B神
2013/08/10 DOTA
Python每天必学之bytes字节
2016/01/28 Python
Python复数属性和方法运算操作示例
2017/07/21 Python
Django 前后台的数据传递的方法
2017/08/08 Python
更改Python的pip install 默认安装依赖路径方法详解
2018/10/27 Python
python3 tcp的粘包现象和解决办法解析
2019/12/09 Python
基于spring boot 日志(logback)报错的解决方式
2020/02/20 Python
装上这 14 个插件后,PyCharm 真的是无敌的存在
2021/01/11 Python
python pyg2plot的原理知识点总结
2021/02/28 Python
HTML5手指下滑弹出负一屏阻止移动端浏览器内置下拉刷新功能的实现代码
2020/04/10 HTML / CSS
班会关于环保演讲稿
2013/12/29 职场文书
网络技术专业推荐信
2014/02/20 职场文书
高中军训感想800字
2014/02/23 职场文书
房地产项目合作意向书
2015/05/08 职场文书
电力培训学习心得体会
2016/01/11 职场文书
高中语文教学反思范文
2016/02/16 职场文书
送给客户微信问候语!
2019/07/04 职场文书