Scrapy+eChart自动爬取生成网络安全词云

因为工作的原因,近期笔者开始持续关注一些安全咨询网站,一来是多了解业界安全咨询提升自身安全知识,二来也是需要从各类安全网站上收集漏洞情报。
作为安全情报领域的新手,面对大量的安全咨询,多少还是会感觉无从下手力不从心。周末闲来无事,突发奇想,如果搞个爬虫,先把网络安全类文章爬下来,然后用机器学习先对文章进行分析,自动提取文章主成分关键词,然后再根据实际需求有选择的阅读相关文章,岂不是可以节省很多时间。
如果能提取文章的关键词,还可以根据近期文章的关键词汇总了解总体的安全态势和舆情,感觉挺靠谱。

整体思路

如前文所述,思路其实很简单:

  1. 用Scrapy先去安全咨询网站上爬取文章的标题和内容
  2. 对文章的内容进行切词
  3. 使用TF-IDF算法提取关键词
  4. 将关键词保存到数据库
  5. 最后可以用可视化将近期出现的比较频繁的关键词做个展示
    看起来也不会很难,文末有代码的链接。

Scrapy爬虫

Scrapy是非常常用的python爬虫框架,基于scrapy写爬虫可以节省大量的代码和时间,原理这里就不赘述了,感兴趣的同学自行科普Scrapy教程,这里只贴一张图。

Scrapy架构

安装Scrapy

笔者基于python3.6来安装Scrapy,所以前提是你的机器已经安装好python3的环境。scrapy安装办法非常简单,使用pip可以一键安装

pip3 install scrapy

装好以后,不熟悉scrapy的同学可以先看看官方示例程序熟悉一下,在cmd里执行下面的命令生成示例程序

scrapy startproject tutorial

即可在当前目录自动创建一个完整的示例教程,这里我们可以看到整个爬虫的目录结构如下:

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

分析网页

本例以“E安全”网站为例,他们提供的安全咨询质量还是不错的,每天都有更新。大致看一眼网站的结构,会发现这个站点导航栏上有十多个安全咨询分类,点进去发现每个分类的url大致为https://www.easyaq.com/type/*.shtml,而每个分类下面又有相关的文章和链接若干。到这里思路就很清楚了,先遍历这几个文章分类,然后动态获取每个分类下的文章链接,之后挨个访问文章链接并把内容保存下来,下面分析一下主要的代码。

爬取网页

爬虫主体代码如下,使用scrapy的框架开发的爬虫实际的代码是非常精简的

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("//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

我们把网站上所有分类的url枚举出来放在start_url里面,parse是框架执行爬虫任务的入口,框架会自动访问前面start_url设置的页面,返回一个response对象,从这个对象中可以通过xpath提取有用的信息。
这里我们要从每一个类型页面的html中分析出文章的标题和访问uri,谷歌的chrome提供了很好的xpath生成工具,可以快速提取目标的xpath,在浏览器中按F12可以看到网页的html源码,找到需要提取的内容,右键可以提取xpath。


image.png

获取到文章内容的uri还没有完,我们还需要进一步访问该uri,并且把文章的内容记录下来供下一步分析,这里的parse_page函数就是用来做内容抽取的,方法同上,借助chrome的xpath分析工具很快就能提取到文章内容。
内容提取到以后,这里将内容存到Item中,Item是Scrapy框架的另一个组成部分,类似于字典类型,主要是用来定义传递数据的格式,而传递是为了下一步数据持久化。

数据持久化

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("存储错误", 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("存储错误", 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,
}

爬虫主程序中收集到的Item会传入到这里,这里有两个步骤save_key_word和save_article,后者将文章的标题、内容、uri存入到MySQL表里;这里着重介绍前者save_key_word函数。
我们的目标是自动分析文章里面跟主题相关的关键字,并且分析出每个词的权重,具体来说包含以下步骤:

  1. 切词:中文切词工具有很多,这里我选择用jieba实现
  2. 提取关键字:jieba里面已经实现好了TF/IDF的算法,我们利用该算法从每篇文章里选择top50的词汇,并且带上权重。用这种方式提取关键字还可以直接把常见的提用词过滤掉,当然jieba也支持自定义停用词
words = jieba.analyse.extract_tags(item['content'], topK=50, withWeight=True)
提取关键词
  1. 数据存储:提取到需要的信息,下一步需要把信息保存到MySQL,在python3下面可以用pymysql来操作MySQL
    文章列表

    关键字列表

关键词可视化-词云

通过上面的程序,我们已经可以把网站上的安全咨询文章全部爬取到数据库,并且从每篇文章里面提取50个关键字。接下来我们希望把这些关键词用可视化的方式展示出来,出现频度高的关键词做高亮显示,所以很自然的想到用词云展示。
这里我们用eChart提供的echarts-wordcloud组件来做。做法非常简单,从MySQL的关键词表里统计数据,生成k-v字串用正则直接替换到html页面,当然这里更优雅的做法应该是用ajax从DB里取数据,我这里就先取个巧了。

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("存储错误", e, "<<<<<<原因在这里")
        conn.rollback()
        return {}
    finally:
        conn.close()

查看动态效果点这里,词云将词汇按照出现的频度或者权重与字体大小做关联,频度越高字体越大,从中我们可以大致感知到当前业界一些安全趋势,当然这也仅仅是一个例子。

词云可视化效果

调试技巧

python有很多IDE可选,笔者选择用pycharm,在调试scrapy程序的时候,需要用到scrapy的引擎启动,所以用默认的pycharm没法调试,需要做一些设置,如下图所示
run -> Edit Configurations
script填写scrapy安装目录里面的cmdline.py的位置;Script parameters是执行scrapy时用的参数,security是我们这个爬虫的名字;Working directory写爬虫的根目录。


image.png

配置好以后就可以直接用pycharm来启动debug了,run -> debug 'xxx'

完整的代码示例,包含echart的部分,请见github

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容