本菜也是第一次写这类小工具,以后也会一直写下去,提高自己!各位表哥一起加油吧!(说明一下文中的代码可以将qt5删掉变为脚本使用,如果想要工具的可以私聊我,这里没法传附件。。)
0X00 前言
前段日子,甲方爸爸需要对集团的各类门户网站做外链查询,看看有没有什么网站下面有那种见不得人的东西,相信各位老司机都懂得吧哈哈,于是开始在网上寻找一些爬虫软件(没办法,自己太菜了,只能找找大佬的软件)
但是找了半天没找到合适的,甲方爸爸要求在这周内完成收集,那么多网站,要是手动搞实在是太累了,而且要找全啊,不能是单个页面下的,还要递归查找,最后实在找不到只能自己写了,拿出我尘封已久的pycharm就开始搞了。
0X01 思路探究
在我们开始写一个工具前,必须要有一套完整的思路(本萌新自己的思路各位大佬亲喷),我这里用我自己本次的思路进行一个总结如下:
0X02 程序功能实现过程中问题总结
1.在爬虫连接时遇到反爬虫机制。
这个问题我在网上找了一些资料,基本都是通过headers字段进行反爬,由于这边甲方爸爸采用的白名单机制,且有相关的防爬waf,所以还是和甲方爸爸相关的运维配合解决了反爬问题,主要是时间确实不多,后续会进行这块的代码优化。
2.连接时候出现异常,各类变量为空。
这个就是当访问到某些特定URL时会出现无法连接的情况,然后返回的内容为none,就会出现一连串错误,这个时候简单做下异常处理,或者采用简单的if判断进行解决。
3.正则匹配不准确。
正则还是得多学学,搞了很久才弄出正确的正则。。。
4.使用递归深度爬取后发现程序容易出错且每次爬取结果不统一。
最开始选择用递归发现程序很容易出错,最后想了下用队列的方式就解决了这类问题,感觉还是队列好用啊。
5.存放外联和网站整体URL时list变量不好传递。
这个list变量由于一直在往里添加元素,最开始使用参数传递,发现不对劲,最后把他弄出全局变量就解决这个问题了,哈哈哈。
0x03 甩出主要代码
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow ,QMessageBox
from ui import Ui_Spider
import requests
from lxml import etree
#from multiprocessing import Pool
#from multiprocessing import Manager
import time
import re
class mwindow(QWidget, Ui_Spider):
def __init__(self):
super(mwindow, self).__init__()
self.setupUi(self)
self.initUi()
self.initarg()
def initUi(self):
#绑定事件
self.spider_button.clicked.connect(self.add_list)
self.url_button.clicked.connect(self.out_url)
self.wl_button.clicked.connect(self.out_wl_url)
def initarg(self):
# 全局url变量
global temp_list2
temp_list2 = []
# 全局外联变量
global temp_list
temp_list = []
global quen_list
quen_list = []
global count
count = 0
def out_url(self):
widgetres = self.write_text()
with open('url.txt','w+',encoding='utf-8') as f:
for i in widgetres:
f.writelines(i + '\n')
QMessageBox.about(self, '','导出成功')
def out_wl_url(self):
widgetres = temp_list
with open('wl_url.txt','w+',encoding='utf-8') as f:
for i in widgetres:
f.writelines(i + '\n')
QMessageBox.about(self, '','导出成功')
def add_list(self):
num = self.url_text.text()
# 爬取标签列表
p_list = ['//@href', '//@src', '//@link']
# 判断是否为本域名参数
domain_url = self.doamin_text.text()
domain_list = domain_url.split(";")
#开始之前情况清空所有列表数据
self.url_list.clear()
self.wl_list.clear()
temp_list.clear()
temp_list2.clear()
quen_list.clear()
# 判断是否为空
if len(num) == 0:
QMessageBox.about(self,'','请输入url')
if len(domain_url) == 0:
QMessageBox.about(self, '', '请输入domain!')
if len(num) !=0 and len(domain_list) != 0:
self.pool_pc(p_list,num,domain_list)
def sprider(self,reg, url, domain_url):
if url not in temp_list2:
# 将每次跑的url写进3.txt
temp_list2.append(url)
# 添加跑的url元素
self.url_list.addItem(str(url))
QApplication.processEvents()
x = self.link_url(url)
if x != None:
# 获取页面源码
html = etree.HTML(x.content)
tpl_content = self.get_tpt_content(reg, html)
filter_list = self.get_filter_list(url, tpl_content)
self.cycle(url, filter_list, domain_url)
else:
# q.put(url)
time.sleep(0.01)
def cycle(self, filter_list, domain_url):
# 进行判断下方是否还有url,如果有继续,如果没有停止
flag = 0
temp_filter_list = []
set(filter_list)
for fl in filter_list:
for d_url in domain_url:
if d_url in fl:
if fl not in temp_list2:
temp_filter_list.append(fl)
flag = 1
if d_url not in fl:
temp_list.append(fl)
# 添加外联列表元素
self.wl_list.addItem(str(fl))
QApplication.processEvents()
if flag == 1:
for te in set(temp_filter_list):
quen_list.append(te)
# q.put(url)
time.sleep(0.01)
def get_filter_list(self,url, tpl_content):
# 过滤第一次讲有的//href前加上http://,filter_list1保存过滤后的列表元素
filter_list1 = []
# 对url进行匹配出前
paren = r"(?:https|http)?://(?:[-\w.]|(?:%[\da-zA-Z]))+(?:[com|cn|org|js|css|img|net]+)"
url_qinzui = re.findall(paren, url)[0]
# 判断是否已'/'开头,是就添加前缀
if len(tpl_content) != 0:
for tpl_c in tpl_content:
if tpl_c[0:4] != 'http':
if tpl_c[0:1] == '/':
tpl = url_qinzui + tpl_c
filter_list1.append(tpl)
elif tpl_c[0:1] == '.':
tpl = url_qinzui + '/' + tpl_c
filter_list1.append(tpl)
elif tpl_c[0:3] == 'www':
tpl = tpl_c
filter_list1.append(tpl)
else:
tpl = url_qinzui + '/' + tpl_c
filter_list1.append(tpl)
else:
filter_list1.append(tpl_c)
return filter_list1
def get_tpt_content(self,reg, html):
all_content = []
for r in reg:
try:
tpl_content = list(set(html.xpath(r)))
except:
continue
tpl_content = [tp for tp in tpl_content if tp != '']
tpl_content = [' '.join([i.strip() for i in tp.strip().split('\t')]) for tp in tpl_content]
all_content += tpl_content
return set(all_content)
def link_url(self,url):
head = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:90.0) Gecko/20100101 Firefox/90.0'
}
# 做异常处理,如果连接不同提示URL以及跳过
try:
x = requests.get(url, allow_redirects=False, headers=head, timeout=2)
if x != None:
return x
except:
print('无法连接' + url)
def write_text(self):
# 存url
url_list = []
# 存css
css_list = []
# 存js
js_list = []
# 存图片
img_list = []
all_list = list(set(temp_list2))
for a in all_list:
h_3 = (a[-3:]).lower()
h_4 = (a[-4:]).lower()
if h_3 == '.js':
js_list.append(a)
elif h_4 == '.css':
css_list.append(a)
elif h_4 == '.jpg' or h_4 == '.png' or h_4 == '.gif':
img_list.append(a)
else:
url_list.append(a)
# 组合列表
new_list = ['URL列表为:'] + url_list + ['\n\nJS列表为:'] + js_list + ['\n\nCSS列表为'] + \
css_list + ['\n\n图片列表为'] + img_list
return new_list
def pool_pc(self,p_list, seedUrl, domain_url):
# 使用进程池
# pool = Pool()
# q = Manager().Queue()
# 当前页的处理
self.sprider(p_list, seedUrl, domain_url)
# 抓取队列中的信息为空,则退出循环
while quen_list:
url = quen_list.pop(0)
self.sprider(p_list, url, domain_url)
QMessageBox.about(self,'','爬虫完毕')
# pool.close()
# pool.join()
if __name__ == '__main__':
app = QApplication(sys.argv)
w = mwindow()
w.show()
sys.exit(app.exec_())
0x04 代码简单讲解
1.使用库以及框架
我使用的是UI库是Pyqt5,爬虫选的request,以及lmxl,正则就是re啦,还是很简单的。
2.主要函数功能简述
0x05 软件使用测试
1.将项目进行打包
pyinstaller -F --icon=favicon.icon spider_wl.py --noconsole
2.运行spider_wl.exe
url处输入要爬取的网址,domain处输入标识为本网站的字符,如wwww.baidu.com,domain输入baidu的话,就会将页面下的所有不包含baidu字符的url设置为外联,当然可以写多个标识,以;分隔就行。
- 本文作者: soufaker
- 本文来源: 奇安信攻防社区
- 原文链接: https://forum.butian.net/share/417
- 版权声明: 除特别声明外,本文各项权利归原文作者和发表平台所有。转载请注明出处!