美文网首页Python新世界Python学习
Python爬虫进行Web数据挖掘总结和分析!这是我见过最牛逼的

Python爬虫进行Web数据挖掘总结和分析!这是我见过最牛逼的

作者: 919b0c54458f | 来源:发表于2018-08-14 20:27 被阅读5次

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

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

    进群:548377875   

    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 '''

    import urllib2

    import re

    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

    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"

    (.*)", 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"))

    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 '''

    import Queue

    import re

    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)

    0x05 总结

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

    相关文章

      网友评论

        本文标题:Python爬虫进行Web数据挖掘总结和分析!这是我见过最牛逼的

        本文链接:https://www.haomeiwen.com/subject/lgkbbftx.html