Scrapy+eChart自動(dòng)爬取生成網(wǎng)絡(luò)安全詞云

因?yàn)楣ぷ鞯脑?,近期筆者開(kāi)始持續(xù)關(guān)注一些安全咨詢(xún)網(wǎng)站,一來(lái)是多了解業(yè)界安全咨詢(xún)提升自身安全知識(shí),二來(lái)也是需要從各類(lèi)安全網(wǎng)站上收集漏洞情報(bào)。
作為安全情報(bào)領(lǐng)域的新手,面對(duì)大量的安全咨詢(xún),多少還是會(huì)感覺(jué)無(wú)從下手力不從心。周末閑來(lái)無(wú)事,突發(fā)奇想,如果搞個(gè)爬蟲(chóng),先把網(wǎng)絡(luò)安全類(lèi)文章爬下來(lái),然后用機(jī)器學(xué)習(xí)先對(duì)文章進(jìn)行分析,自動(dòng)提取文章主成分關(guān)鍵詞,然后再根據(jù)實(shí)際需求有選擇的閱讀相關(guān)文章,豈不是可以節(jié)省很多時(shí)間。
如果能提取文章的關(guān)鍵詞,還可以根據(jù)近期文章的關(guān)鍵詞匯總了解總體的安全態(tài)勢(shì)和輿情,感覺(jué)挺靠譜。

整體思路

如前文所述,思路其實(shí)很簡(jiǎn)單:

  1. 用Scrapy先去安全咨詢(xún)網(wǎng)站上爬取文章的標(biāo)題和內(nèi)容
  2. 對(duì)文章的內(nèi)容進(jìn)行切詞
  3. 使用TF-IDF算法提取關(guān)鍵詞
  4. 將關(guān)鍵詞保存到數(shù)據(jù)庫(kù)
  5. 最后可以用可視化將近期出現(xiàn)的比較頻繁的關(guān)鍵詞做個(gè)展示
    看起來(lái)也不會(huì)很難,文末有代碼的鏈接。

Scrapy爬蟲(chóng)

Scrapy是非常常用的python爬蟲(chóng)框架,基于scrapy寫(xiě)爬蟲(chóng)可以節(jié)省大量的代碼和時(shí)間,原理這里就不贅述了,感興趣的同學(xué)自行科普Scrapy教程,這里只貼一張圖。

Scrapy架構(gòu)

安裝Scrapy

筆者基于python3.6來(lái)安裝Scrapy,所以前提是你的機(jī)器已經(jīng)安裝好python3的環(huán)境。scrapy安裝辦法非常簡(jiǎn)單,使用pip可以一鍵安裝

pip3 install scrapy

裝好以后,不熟悉scrapy的同學(xué)可以先看看官方示例程序熟悉一下,在cmd里執(zhí)行下面的命令生成示例程序

scrapy startproject tutorial

即可在當(dāng)前目錄自動(dòng)創(chuàng)建一個(gè)完整的示例教程,這里我們可以看到整個(gè)爬蟲(chóng)的目錄結(jié)構(gòu)如下:

tutorial/
    scrapy.cfg            # deploy configuration file
    tutorial/             # project's Python module, you'll import your code from here
        __init__.py
        items.py          # project items definition file
        pipelines.py      # project pipelines file
        settings.py       # project settings file
        spiders/          # a directory where you'll later put your spiders
            __init__.py

分析網(wǎng)頁(yè)

本例以“E安全”網(wǎng)站為例,他們提供的安全咨詢(xún)質(zhì)量還是不錯(cuò)的,每天都有更新。大致看一眼網(wǎng)站的結(jié)構(gòu),會(huì)發(fā)現(xiàn)這個(gè)站點(diǎn)導(dǎo)航欄上有十多個(gè)安全咨詢(xún)分類(lèi),點(diǎn)進(jìn)去發(fā)現(xiàn)每個(gè)分類(lèi)的url大致為https://www.easyaq.com/type/*.shtml,而每個(gè)分類(lèi)下面又有相關(guān)的文章和鏈接若干。到這里思路就很清楚了,先遍歷這幾個(gè)文章分類(lèi),然后動(dòng)態(tài)獲取每個(gè)分類(lèi)下的文章鏈接,之后挨個(gè)訪問(wèn)文章鏈接并把內(nèi)容保存下來(lái),下面分析一下主要的代碼。

爬取網(wǎng)頁(yè)

爬蟲(chóng)主體代碼如下,使用scrapy的框架開(kāi)發(fā)的爬蟲(chóng)實(shí)際的代碼是非常精簡(jiǎn)的

import scrapy
from scrapy import Request, Selector
from sec_news_scrapy.items import SecNewsItem

class SecNewsSpider(scrapy.Spider):
    name = "security"
    allowed_domains = ["easyaq.com"]
    start_urls = []
    for i in range(2, 17):
        req_url = 'https://www.easyaq.com/type/%s.shtml' % i
        start_urls.append(req_url)

    def parse(self, response):
        topics = []
        for sel in response.xpath('//*[@id="infocat"]/div[@class="listnews bt"]/div[@class="listdeteal"]/h3/a'):
            topic = {'title': sel.xpath('text()').extract(), 'link': sel.xpath('@href').extract()}
            topics.append(topic)

        for topic in topics:
            yield Request(url=topic['link'][0], meta={'topic': topic}, dont_filter=False, callback=self.parse_page)

    def parse_page(self, response):
        topic = response.meta['topic']
        selector = Selector(response)

        item = SecNewsItem()
        item['title'] = selector.xpath("http://div[@class='article_tittle']/div[@class='inner']/h1/text()").extract()
        item['content'] = "".join(selector.xpath('//div[@class="content-text"]/p/text()').extract())
        item['uri'] = topic['link'][0]
        print('Finish scan title:' + item['title'][0])
        yield item

我們把網(wǎng)站上所有分類(lèi)的url枚舉出來(lái)放在start_url里面,parse是框架執(zhí)行爬蟲(chóng)任務(wù)的入口,框架會(huì)自動(dòng)訪問(wèn)前面start_url設(shè)置的頁(yè)面,返回一個(gè)response對(duì)象,從這個(gè)對(duì)象中可以通過(guò)xpath提取有用的信息。
這里我們要從每一個(gè)類(lèi)型頁(yè)面的html中分析出文章的標(biāo)題和訪問(wèn)uri,谷歌的chrome提供了很好的xpath生成工具,可以快速提取目標(biāo)的xpath,在瀏覽器中按F12可以看到網(wǎng)頁(yè)的html源碼,找到需要提取的內(nèi)容,右鍵可以提取xpath。


image.png

獲取到文章內(nèi)容的uri還沒(méi)有完,我們還需要進(jìn)一步訪問(wèn)該uri,并且把文章的內(nèi)容記錄下來(lái)供下一步分析,這里的parse_page函數(shù)就是用來(lái)做內(nèi)容抽取的,方法同上,借助chrome的xpath分析工具很快就能提取到文章內(nèi)容。
內(nèi)容提取到以后,這里將內(nèi)容存到Item中,Item是Scrapy框架的另一個(gè)組成部分,類(lèi)似于字典類(lèi)型,主要是用來(lái)定義傳遞數(shù)據(jù)的格式,而傳遞是為了下一步數(shù)據(jù)持久化。

數(shù)據(jù)持久化

Item.py

class SecNewsItem(scrapy.Item):
    title = scrapy.Field()
    content = scrapy.Field()
    uri = scrapy.Field()
    pass

pipeline.py

import jieba
import jieba.analyse
import pymysql
import re

def dbHandle():
    conn = pymysql.connect(
        host="localhost",
        user="root",
        passwd="1234",
        charset="utf8",
        db='secnews',
        port=3306)
    return conn

def is_figure(str):
    value = re.compile(r'^\d+$')
    if value.match(str):
        return True
    else:
        return False

def save_key_word(item):
    words = jieba.analyse.extract_tags(item['content'], topK=50, withWeight=True)

    conn = dbHandle()
    cursor = conn.cursor()
    sql = "insert ignore into t_security_news_words(title, `key`, val) values (%s,%s,%s)"
    try:
        for word in words:
            if is_figure(word[0]):
                continue
            cursor.execute(sql, (item['title'][0], word[0], int(word[1] * 1000)))
        cursor.connection.commit()
    except BaseException as e:
        print("存儲(chǔ)錯(cuò)誤", e, "<<<<<<原因在這里")
        conn.rollback()

def save_article(item):
    conn = dbHandle()
    cursor = conn.cursor()
    sql = "insert ignore into t_security_news_article(title, content, uri) values (%s,%s,%s)"
    try:
        cursor.execute(sql, (item['title'][0], item['content'], item['uri']))
        cursor.connection.commit()
    except BaseException as e:
        print("存儲(chǔ)錯(cuò)誤", e, "<<<<<<原因在這里")
        conn.rollback()

class TutorialPipeline(object):
    def process_item(self, item, spider):
        save_key_word(item)
        save_article(item)
        return item

settings.py

ITEM_PIPELINES = {
    'sec_news_scrapy.pipelines.TutorialPipeline': 300,
}

爬蟲(chóng)主程序中收集到的Item會(huì)傳入到這里,這里有兩個(gè)步驟save_key_word和save_article,后者將文章的標(biāo)題、內(nèi)容、uri存入到MySQL表里;這里著重介紹前者save_key_word函數(shù)。
我們的目標(biāo)是自動(dòng)分析文章里面跟主題相關(guān)的關(guān)鍵字,并且分析出每個(gè)詞的權(quán)重,具體來(lái)說(shuō)包含以下步驟:

  1. 切詞:中文切詞工具有很多,這里我選擇用jieba實(shí)現(xiàn)
  2. 提取關(guān)鍵字:jieba里面已經(jīng)實(shí)現(xiàn)好了TF/IDF的算法,我們利用該算法從每篇文章里選擇top50的詞匯,并且?guī)蠙?quán)重。用這種方式提取關(guān)鍵字還可以直接把常見(jiàn)的提用詞過(guò)濾掉,當(dāng)然jieba也支持自定義停用詞
words = jieba.analyse.extract_tags(item['content'], topK=50, withWeight=True)
提取關(guān)鍵詞
  1. 數(shù)據(jù)存儲(chǔ):提取到需要的信息,下一步需要把信息保存到MySQL,在python3下面可以用pymysql來(lái)操作MySQL
    文章列表

    關(guān)鍵字列表

關(guān)鍵詞可視化-詞云

通過(guò)上面的程序,我們已經(jīng)可以把網(wǎng)站上的安全咨詢(xún)文章全部爬取到數(shù)據(jù)庫(kù),并且從每篇文章里面提取50個(gè)關(guān)鍵字。接下來(lái)我們希望把這些關(guān)鍵詞用可視化的方式展示出來(lái),出現(xiàn)頻度高的關(guān)鍵詞做高亮顯示,所以很自然的想到用詞云展示。
這里我們用eChart提供的echarts-wordcloud組件來(lái)做。做法非常簡(jiǎn)單,從MySQL的關(guān)鍵詞表里統(tǒng)計(jì)數(shù)據(jù),生成k-v字串用正則直接替換到html頁(yè)面,當(dāng)然這里更優(yōu)雅的做法應(yīng)該是用ajax從DB里取數(shù)據(jù),我這里就先取個(gè)巧了。

def get_key_word_from_db():
    words = {}
    conn = dbHandle()
    try:
        with conn.cursor() as cursor:
            cursor.execute(
                "select `key`, sum(val) as s from t_security_news_words group by `key` order by s desc limit 300")
            for res in cursor.fetchall():
                words[res[0]] = int(res[1])
        return words
    except BaseException as e:
        print("存儲(chǔ)錯(cuò)誤", e, "<<<<<<原因在這里")
        conn.rollback()
        return {}
    finally:
        conn.close()

查看動(dòng)態(tài)效果點(diǎn)這里,詞云將詞匯按照出現(xiàn)的頻度或者權(quán)重與字體大小做關(guān)聯(lián),頻度越高字體越大,從中我們可以大致感知到當(dāng)前業(yè)界一些安全趨勢(shì),當(dāng)然這也僅僅是一個(gè)例子。

詞云可視化效果

調(diào)試技巧

python有很多IDE可選,筆者選擇用pycharm,在調(diào)試scrapy程序的時(shí)候,需要用到scrapy的引擎啟動(dòng),所以用默認(rèn)的pycharm沒(méi)法調(diào)試,需要做一些設(shè)置,如下圖所示
run -> Edit Configurations
script填寫(xiě)scrapy安裝目錄里面的cmdline.py的位置;Script parameters是執(zhí)行scrapy時(shí)用的參數(shù),security是我們這個(gè)爬蟲(chóng)的名字;Working directory寫(xiě)爬蟲(chóng)的根目錄。


image.png

配置好以后就可以直接用pycharm來(lái)啟動(dòng)debug了,run -> debug 'xxx'

完整的代碼示例,包含echart的部分,請(qǐng)見(jiàn)github

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 序言第1章 Scrapy介紹第2章 理解HTML和XPath第3章 爬蟲(chóng)基礎(chǔ)第4章 從Scrapy到移動(dòng)應(yīng)用第5章...
    SeanCheney閱讀 15,254評(píng)論 13 61
  • scrapy學(xué)習(xí)筆記(有示例版) 我的博客 scrapy學(xué)習(xí)筆記1.使用scrapy1.1創(chuàng)建工程1.2創(chuàng)建爬蟲(chóng)模...
    陳思煜閱讀 13,045評(píng)論 4 46
  • 我夢(mèng)見(jiàn), 我被遺落在沙漠里。 驕陽(yáng),烈風(fēng),炙烤著我的身。 蒼蒼黃沙行。 我背著沉重的行囊, 回首, 始終追隨我的,...
    季霖閱讀 307評(píng)論 0 0
  • 如果會(huì)被眾人當(dāng)成惡棍,你還會(huì)做對(duì)的決定嗎?….我寧愿被認(rèn)為是一個(gè)贏家而非一個(gè)好隊(duì)友。我希望能同時(shí)兼顧兩者,但是這是...
    理想你幾歲_639f閱讀 103評(píng)論 0 0
  • 回想一下過(guò)往,好像喝到的最美好的茶湯都不是自己沏的,不禁有點(diǎn)愕然。 想想最開(kāi)始喝茶時(shí),并不特別在意茶的類(lèi)型以及沖泡...
    廢話宅女閱讀 703評(píng)論 0 2

友情鏈接更多精彩內(nèi)容