免费试用
banner
行业资讯>大数据技术与应用>Python进行Web数据挖掘分析

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

作者: afenxi来源: afenxi时间:2017-03-29 12:47:190

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: [email protected] 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:[email protected] 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( 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 总结

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

作者:BSDR

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

banner
看过还想看
可能还想看
热点推荐
Yonghong的价值观:以卓越的数据技术为客户创造价值,实现客户成功。
免费试用