<span id="eBLJV"><canvas id="eBLJV"><ul id="eBLJV"></ul></canvas></span>
<progress id="eBLJV"><video id="eBLJV"><button id="eBLJV"></button></video></progress>
    <sup id="eBLJV"></sup>

    1. <source id="eBLJV"></source>
      <tfoot id="eBLJV"><table id="eBLJV"></table><form id="eBLJV"><tbody id="eBLJV"><noscript id="eBLJV"></noscript><i id="eBLJV"><rp id="eBLJV"><figure id="eBLJV"></figure></rp><caption id="eBLJV"><input id="eBLJV"></input></caption></i></tbody></form></tfoot><sub id="eBLJV"><style id="eBLJV"><figcaption id="eBLJV"></figcaption></style></sub>

        <th id="eBLJV"><textarea id="eBLJV"></textarea><progress id="eBLJV"><blockquote id="eBLJV"><legend id="eBLJV"><source id="eBLJV"></source><th id="eBLJV"><hgroup id="eBLJV"><keygen id="eBLJV"><b id="eBLJV"></b></keygen><noframes id="eBLJV">

        Python爬虫进行Web数据挖掘总结和分析

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

        Python爬虫进行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)

        对于二级域名的获取,如果直接从主站爬取的链接中寻找,效率很低而且结果可能并不能让人满意。目前获取二级域名有三种常用方法,第一种是利用域名字典进行猜解,类似于暴力破解。第二种种是利用各种二级域名查询接口进行查询,例如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+'n')
         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"))

        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 "n[+] stop threading " + str(self.thread_id)
        103                 break
        104             print "n[*] 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('n')
        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)

        0x05 总结

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

        作者:BSDR

        链接:http://www.cnblogs.com/bsdr/p/5419680.html

        本文出处:BSDR,链接:http://www.cnblogs.com/bsdr/p/5419680.html,采用「CC BY-SA 4.0 CN」协议转载学习交流,内容版权归原作者所有,如涉作品、版权和其他问题请联系「我们」处理。

        发表评论

        登录后才能评论

        联系我们

        如有建议:>>给我留言

        大数据交流群:

        统? 计? 学 网络分析网-统计学

        商业智能?网络分析网-商业智能

        数据挖掘?数据分析-数据挖掘

        数据产品?网络分析网-数据产品

        QR code