zoukankan      html  css  js  c++  java
  • Web数据挖掘总结

    0x01 Web数据挖掘类型

     利用Python爬虫进行Web数据挖掘已经越来越普遍,网上的各种Python爬虫资料教程比较多,但是很少有人对Web数据挖掘进行系统地总结和分析。

      从目标上来讲,Web数据挖掘分为三类。最常见的是对于网站内容的爬取,包括文本、图片和文件等;其次是对于网站结构的爬取,包括网站目录,链接之间的相互跳转关系,二级域名等;还有一种爬虫是对于Web应用数据的挖掘,包括获取网站CMS类型,Web插件等。

    0x02 网站内容挖掘

      网站内容挖掘应用最广,最为常见,网上的Python爬虫资料大多也都属于这类。爬取下的内容也可用于很多方面。

      Python编写这类爬虫的常见思路就是利用request或urllib2库定制请求,利用BeautifulSoup对原始网页进行解析,定位特定html标签,寻找目标内容。如果要提高性能,可以利用threading启用多线程,gevent启用协程(在windows上使用可能会有些问题),也可以用multiprocessing启动多进程。multiprocessing能突破python的GIL全局解释器锁的限制。其他的一些技巧可以看我的另一篇博客:常见的反爬虫和应对方法 

      这类爬虫资料实在太多,在这里不再赘述了。

    0x03 网站结构挖掘

      网站结构挖掘并不是很常见,但在一些特殊的应用场景,我们也会用到。例如对于Web漏洞扫描器,爬取网站整站目录,获取二级域名是极为重要的。在第一类网站内容挖掘中,有时也需要将目标网站某个页面(通常是首页)作为入口,对整个网站所有内容进行获取和分析,这种情况下就需要对网站结构进行分析。

      对于网站目录爬取,需要考虑的一个重要问题就是爬虫性能。通常网站的页面会比较多,如果直接获取所有目录,可能会耗费大量时间。另外,对于网站链接的搜索策略对爬虫的性能也会产生很大影响。一般情况下,我们会采用广度优先搜索,从入口页面开始,获取该页面内所有链接,并判断链接是否是站内链接,是否已经爬取过。为了提高速度,可以对链接进行归纳,将/page.php?id=1与/page.php?id=2认为是同一类型链接,不进行重复爬取。简单实现代码如下:

      1 # coding=utf-8
      2 '''
      3 爬取网站所有目录
      4 Author: bsdr
      5 Email: 1340447902@qq.com
      6 '''
      7 import urllib2
      8 import re
      9 from BeautifulSoup import BeautifulSoup
     10 import time
     11 
     12 t = time.time()
     13 
     14 HOST = ''
     15 CHECKED_URL = []  # 已检测的url规则
     16 CHECKING_URL = []  # 待检测的url
     17 RESULT = []  # 检测结果
     18 RETRY = 3  # 重复尝试次数
     19 TIMEOUT = 2  # 超时
     20 
     21 
     22 class url_node:
     23     def __init__(self, url):
     24         '''
     25         url节点初始化
     26         :param url: String, 当前url
     27         :return:
     28         '''
     29         # self.deep = deep
     30         self.url = self.handle_url(url, is_next_url=False)
     31         self.next_url = []
     32         self.content = ''
     33 
     34 
     35     def handle_url(self, url, is_next_url=True):
     36         '''
     37         将所有url处理成标准格式
     38 
     39         :param url: String
     40         :param is_next_url:  Bool, 判断传入的url是当前需要检测的url还是下一层url
     41         :return: 返回空或错误信息或正确url
     42         '''
     43         global CHECKED_URL
     44         global CHECKING_URL
     45 
     46         # 去掉结尾的’/‘
     47         url = url[0:len(url) - 1] if url.endswith('/') else url
     48 
     49         if url.find(HOST) == -1:
     50             if not url.startswith('http'):
     51                 url = 'http://' + HOST + url if url.startswith('/') else 'http://' + HOST + '/' + url
     52             else:
     53                 # 如果url的host不为当前host,返回空
     54                 return
     55         else:
     56             if not url.startswith('http'):
     57                 url = 'http://' + url
     58 
     59         if is_next_url:
     60             # 下一层url放入待检测列表
     61             CHECKING_URL.append(url)
     62         else:
     63             # 对于当前需要检测的url
     64             # 将其中的所有参数替换为1
     65             # 然后加入url规则表
     66             # 参数不同,类型相同的url,只检测一次
     67             rule = re.compile(r'=.*?&|=.*?$')
     68             result = re.sub(rule, '=1&', url)
     69             if result in CHECKED_URL:
     70                 return '[!] Url has checked!'
     71             else:
     72                 CHECKED_URL.append(result)
     73                 RESULT.append(url)
     74 
     75         return url
     76 
     77 
     78     def __is_connectable(self):
     79         # 验证是否可以连接
     80         retry = 3
     81         timeout = 2
     82         for i in range(RETRY):
     83             try:
     84                 response = urllib2.urlopen(self.url, timeout=TIMEOUT)
     85                 return True
     86             except:
     87                 if i == retry - 1:
     88                     return False
     89 
     90 
     91     def get_next(self):
     92         # 获取当前页面所有url
     93         soup = BeautifulSoup(self.content)
     94         next_urls = soup.findAll('a')
     95         if len(next_urls) != 0:
     96             for link in next_urls:
     97                 self.handle_url(link.get('href'))
     98 
     99 
    100     def run(self):
    101         if self.url:
    102             print self.url
    103             if self.__is_connectable():
    104                 try:
    105                     self.content = urllib2.urlopen(self.url, timeout=TIMEOUT).read()
    106                     self.get_next()
    107                 except:
    108                     print('[!] Connect Failed')
    109 
    110 
    111 class Poc:
    112     def run(self, url):
    113         global HOST
    114         global CHECKING_URL
    115         url = check_url(url)
    116 
    117         if not url.find('https'):
    118             HOST = url[8:]
    119         else:
    120             HOST = url[7:]
    121 
    122         for url in CHECKING_URL:
    123             print(url)
    124             url_node(url).run()
    125 
    126 
    127 def check_url(url):
    128     url = 'http://' + url if not url.startswith('http') else url
    129     url = url[0:len(url) - 1] if url.endswith('/') else url
    130 
    131     for i in range(RETRY):
    132         try:
    133             response = urllib2.urlopen(url, timeout=TIMEOUT)
    134             return url
    135         except:
    136             raise Exception("Connect error")
    137 
    138 
    139 if __name__ == '__main__':
    140     HOST = 'www.hrbeu.edu.cn'
    141     CHECKING_URL.append('http://www.hrbeu.edu.cn/')
    142     for url in CHECKING_URL:
    143         print(url)
    144         url_node(url).run()
    145     print RESULT
    146     print "URL num: "+str(len(RESULT))
    147     print "time: %d s" % (time.time() - t)
    View Code

      对于二级域名的获取,如果直接从主站爬取的链接中寻找,效率很低而且结果可能并不能让人满意。目前获取二级域名有三种常用方法,第一种是利用域名字典进行猜解,类似于暴力破解。第二种种是利用各种二级域名查询接口进行查询,例如bing的查询接口如下,domain为根域名:

    http://cn.bing.com/search?count=50&q=site:domain&first=1

      link的二级域名查询接口为:

    http://i.links.cn/subdomain/?b2=1&b3=1&b4=1&domain=domain

      aleax的二级域名查询接口为:

    http://alexa.chinaz.com/?domain=domain

      由这些接口都能直接查询到指定根域名的二级域名,这里就不附代码了。

      还有一种获取二级域名的方法是通过搜索引擎直接搜索,如百度搜索:inurl:domain 或 site:domain。这种方法比较慢。具体代码如下:

      1 # coding=utf-8
      2 '''
      3 利用百度搜索二级域名
      4 Author: bsdr
      5 Email:1320227902@qq.com
      6 '''
      7 
      8 
      9 import urllib2
     10 import string
     11 import urllib
     12 import re
     13 import random
     14 from url_handle import split_url
     15 
     16 user_agents = ['Mozilla/5.0 (Windows NT 6.1; WOW64; rv:23.0) Gecko/20130406 Firefox/23.0',
     17         'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:18.0) Gecko/20100101 Firefox/18.0',
     18         'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/533+ (KHTML, like Gecko) Element Browser 5.0',
     19         'IBM WebExplorer /v0.94', 'Galaxy/1.0 [en] (Mac OS X 10.5.6; U; en)',
     20         'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; WOW64; Trident/6.0)',
     21         'Opera/9.80 (Windows NT 6.0) Presto/2.12.388 Version/12.14',
     22         'Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5355d Safari/8536.25',
     23         'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1468.0 Safari/537.36',
     24         'Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.0; Trident/5.0; TheWorld)']
     25 
     26 
     27 def baidu_search(keyword,pn):
     28     p=  urllib.urlencode({'wd':keyword})
     29     print(p)
     30     req = urllib2.Request(("http://www.baidu.com/s?"+p+"&pn={0}&cl=3&rn=10").format(pn))
     31     req.add_header('User-Agent', random.choice(user_agents))
     32     try:
     33         res=urllib2.urlopen(req)
     34         html=res.read()
     35     except:
     36         html = ''
     37     return html
     38 
     39 
     40 def getList(regex,text):
     41     arr = []
     42     res = re.findall(regex, text)
     43     if res:
     44         for r in res:
     45             arr.append(r)
     46     return arr
     47 
     48 
     49 def getMatch(regex,text):
     50     res = re.findall(regex, text)
     51     if res:
     52         return res[0]
     53     return ''
     54 
     55 
     56 def is_get(url):
     57 
     58     regex=r'(S*?)?.*=.*'
     59     res=re.match(regex,url)
     60     if res:
     61         return res.group(1)
     62     else:
     63         return 0
     64 
     65 
     66 def geturl(domain,pages=10):
     67     keyword = 'site:.'+domain
     68     targets = []
     69     hosts=[]
     70     for page in range(0,int(pages)):
     71         pn=(page+1)*10
     72         html = baidu_search(keyword,pn)
     73         content = unicode(html, 'utf-8','ignore')
     74         arrList = getList(u"<div class="f13">(.*)</div>", content)
     75 
     76         for item in arrList:
     77             regex = u"data-tools='{"title":"(.*)","url":"(.*)"}'"
     78             link = getMatch(regex,item)
     79             url=link[1]
     80             try:
     81                 domain=urllib2.Request(url)
     82                 r=random.randint(0,11)
     83                 domain.add_header('User-Agent', user_agents[r])
     84                 domain.add_header('Connection','keep-alive')
     85                 response=urllib2.urlopen(domain)
     86                 uri=response.geturl()
     87                 urs = split_url.split(uri)
     88 
     89                 if (uri in targets) or (urs in hosts) :
     90                     continue
     91                 else:
     92                     targets.append(uri)
     93                     hosts.append(urs)
     94                     f1=open('data/baidu.txt','a')
     95                     f1.write(urs+'
    ')
     96                     f1.close()
     97             except:
     98                 continue
     99     print "urls have been grabed already!!!"
    100     return hosts
    101 
    102 
    103 if __name__ == '__main__':
    104     print(geturl("cnblogs.com"))
    View Code

    0x04 Web应用数据挖掘

      这种数据挖掘方式主要针对Web自身,旨在获取Web应用信息/Web指纹,在Web安全领域应用较多,这类代表有zoomeye、sodan等。通过获取大范围的Web应用信息,Web应用类型、版本,Web插件信息等,能够对大范围内的Web安全状况进行评估,分析特定漏洞在全球范围内造成的影响。当然也可以利用特定漏洞对大范围的Web应用进行定向攻击。

      在这里我们不讨论那种大范围的扫描,我们只以CMS识别为例来简单说明Web应用数据的挖掘。CMS识别旨在判别网站所采用的CMS(内容管理系统,如WordPress),为后续的插件检测或漏洞检测做准备。

      CMS识别一般从4个方面进行检测:检测特定目录是否存在;比对特定文件MD5;检测HTML页面中的关键字;检测robots文件。另外,一个巨大的CMS指纹库是保证识别效率的关键,如果指纹库太小,实际效果并不会很好。但是如果指纹库太大,又会影响到识别的速率。我搜集了一些简单的CMS指纹,写了一个简单的CMS识别脚本。代码如下:

      1 # coding:utf-8
      2 '''
      3 CMS识别
      4 Author: bsdr
      5 Email: 1340447902@qq.com
      6 '''
      7 import Queue
      8 import re
      9 import os
     10 import time
     11 import requests
     12 import threading
     13 import urllib2
     14 import hashlib
     15 import sys
     16 from config import POC_PATH
     17 
     18 t = time.time()                  # 起始时间
     19 
     20 event = threading.Event()        # 全局event,用来控制线程状态
     21 
     22 RETRY = 3                        # 验证url时尝试次数
     23 TIMEOUT = 3                      # 超时
     24 THREADS = 300                    # 开启的线程数
     25 CMS_PATH = os.path.join(POC_PATH, 'CMS2\')              # CMS指纹文件目录
     26 
     27 CMS = 'Unknown'
     28 HEADER = {'User-Agent': 'Mozilla/5.0 (Windows; U; Windows NT 5.1; '
     29                         'en-US; rv:1.9.1.11) Gecko/20100701 Firefox/3.5.11'}
     30 
     31 
     32 class Cms:
     33     def __init__(self, url, line):
     34         self.url = url
     35         self.line = line
     36         print line
     37 
     38 
     39     # 检测文件md5
     40     def _get_md5(self, file):
     41         m = hashlib.md5()
     42 
     43         try:
     44             m.update(file)
     45         except:
     46             while True:
     47                 data = file.read(10240)          # 避免文件太大,内存不够
     48                 if not data:
     49                     break
     50                 m.update(data)
     51 
     52         return m.hexdigest()
     53 
     54 
     55     # 检测每一行指纹
     56     def check(self):
     57             global CMS
     58             global event
     59             cms = re.findall(r'(.*?)|', self.line)
     60             path = cms[0]
     61             cms_name = cms[1]
     62             keyword = cms[2]
     63             content = ''
     64 
     65             try:
     66                 response = requests.get(self.url+path)
     67                 if response.status_code == 200:
     68                     content = response.content
     69             except:
     70                 try:
     71                     content = urllib2.urlopen(self.url+path, timeout=TIMEOUT).read()
     72                 except:
     73                     pass
     74 
     75             if content is not None and content != '':
     76 
     77                     if len(cms) == 3 and content.find(keyword) != -1:
     78                         CMS = cms_name
     79                         print cms
     80                         event.set()             # 识别出cms后,改变event状态
     81 
     82                     elif len(cms) == 4 and self._get_md5(content) == cms[3]:
     83                         CMS = cms_name
     84                         event.set()
     85                         print cms
     86 
     87 
     88 
     89 # 创建线程类,定义自己的线程
     90 class myThread(threading.Thread):
     91     def __init__(self, q, thread_id):
     92         threading.Thread.__init__(self)
     93         self.q = q
     94         self.thread_id = thread_id
     95 
     96 
     97     def run(self):
     98         global event
     99         while not self.q.empty():
    100             # 检测event状态判断线程是否执行
    101             if event.is_set():
    102                 print "
    [+] stop threading " + str(self.thread_id)
    103                 break
    104             print "
    [*] threading " + str(self.thread_id) + " is running"
    105             objects = self.q.get()
    106             objects.check()
    107 
    108 
    109 # 初始化url,并验证是否可以连接
    110 def check_url(url):
    111     url = 'http://' + url if url.startswith('http') == False else url
    112     url = url[0:len(url) - 1] if url.endswith('/') else url
    113 
    114     for i in range(RETRY):
    115             try:
    116                 response = urllib2.urlopen(url, timeout=TIMEOUT)
    117                 if response.code == 200:
    118                     return url
    119             except:
    120                 raise Exception("Connect error")
    121 
    122 
    123 # 遍历指定目录下所有文件的每一行
    124 def load_cms():
    125     cms_list = []
    126 
    127     for root, dirs, files in os.walk(CMS_PATH):
    128         for f in files:
    129             fp = open(CMS_PATH + f, 'r')
    130             content = fp.readlines()
    131             fp.close()
    132             for line in content:
    133                 if line.startswith('/'):
    134                     line = line.strip('
    ')
    135                     cms_list.append(line)
    136 
    137     return cms_list
    138 
    139 
    140 # 创建线程
    141 def main(url):
    142     global CMS
    143     url = check_url(url)
    144     cms_list = load_cms()
    145     assert len(cms_list) > 0
    146     work_queue = Queue.Queue()
    147 
    148     # 装载任务
    149     for path in cms_list:
    150         work_queue.put(Cms(url, path))
    151     threads = []
    152     nloops = range(THREADS)
    153 
    154     # 启动线程
    155     for i in nloops:
    156         t = myThread(work_queue, i)
    157         t.start()
    158         threads.append(t)
    159 
    160     for i in nloops:
    161         t.join()
    162 
    163     #return True, CMS
    164 
    165 class Poc:
    166     def run(self,target):
    167         main(target)
    168         cms = CMS
    169         if cms == 'Unknown':
    170             return cms, False
    171         else:
    172             return cms, True
    173 
    174 if __name__ == '__main__':
    175     cms, is_succes = Poc().run('software.hrbeu.edu.cn')
    176     print '[!] CMS ==> %s' % cms
    177     print '[!] 用时:%f s' % (time.time()-t)
    View Code

    0x05 总结

      以上内容全部由我自己编写爬虫的经验总结而来,如有问题,欢迎指正。

  • 相关阅读:
    Oracle函数如何把符串装换为小写的格式
    Oralce中的synonym同义词
    JS中getYear()的兼容问题
    How to do SSH Tunneling (Port Forwarding)
    所谓深度链接(Deep linking)
    upload size of asp.net
    发一个自动刷网站PV流量的小工具
    解决Visual Studio 2008 下,打开.dbml(LINQ) 文件时,提示"The operation could not be completed." 的问题。
    在资源管理器中使鼠标右键增加一个命令,运行cmd,同时使得当前路径为资源管理器当前的目录
    使用SQL语句获取Sql Server数据库的版本
  • 原文地址:https://www.cnblogs.com/bsdr/p/5419680.html
Copyright © 2011-2022 走看看