甲方爸爸需求-网站外链深度爬取小工具

本菜也是第一次写这类小工具,以后也会一直写下去,提高自己!各位表哥一起加油吧!(说明一下文中的代码可以将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设置为外联,当然可以写多个标识,以;分隔就行。




  • 发表于 2021-08-20 10:50:02
  • 阅读 ( 6827 )
  • 分类:安全开发

0 条评论

请先 登录 后评论
soufaker
soufaker

8 篇文章