手摸手制作掘金热榜文章词云,看看大牛都讲了些什么

1,757 阅读4分钟

在「掘金」上有非常多优秀的内容,我经常会到热榜上找一些感兴趣的文章来看,一方面可以学习到实用的技术方案,另一方面可以了解到大牛的思考方式。但平台每天都有大量新文章产生,再加上历史累计的,一个人想要把这些内容都看完,几乎是不可能完成的任务。

可是,又很好奇大牛都讲了些什么?于是决定亲自动手来挖掘一下热榜文章的关键词,并制作成词云。同时将这个过程详细的记录下来,并分享给大家,为了方便大家今后有类似需求时可以借鉴。

既然我们想要分析掘金热榜的文章,那么就需要获取到文章的内容数据。幸运的是,掘金的网站架构设计得非常赞,网页内容基本都是通过接口获取,而且接口数据是json格式的,非常容易解析。

代码如下:

import json
import requests

JUEJIN_HEAD = {
    'Accept':'*/*',
    'Accept-Encoding':'gzip, deflate, br',
    'Accept-Language':'en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4',
    'Connection':'keep-alive',
    'Content-Length':'170',
    'Content-Type':'application/json',
    'DNT':'1',
    'Host':'web-api.juejin.im',
    'Origin':'https://juejin.cn',
    'Referer':'https://juejin.im/timeline',
    'User-Agent':'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.86 Safari/537.36',
    'X-Agent':'Juejin/Web',
}

JUEJIN_QUERY = "https://web-api.juejin.im/query"

COUNT = 0

def get_juejin_articles(after="", order="WEEKLY_HOTTEST"):
    global COUNT
    
    # 接口是一个POST请求,data是需要传递的参数
    data = {
        "operationName": "",
        "query": "",
        "variables": {
            "first":20,
            "after":after,
            "order":order},
        "extensions": {
            "query": {
                "id":"21207e9ddb1de777adeaca7a2fb38030"
            }
        }
    }

    rep = requests.post(JUEJIN_QUERY, data=json.dumps(data), headers=JUEJIN_HEAD)
    articles = rep.json()['data']['articleFeed']['items']
    edges, pageInfo = articles['edges'], articles['pageInfo']

    for edge in edges:
        node = edge['node']

        if node['type'] == 'post':
            # 输出结果可以重定向到文件里
            print("{}\t{}".format(node["title"], node["originalUrl"])

    COUNT += 1
    if COUNT <= 500 and pageInfo['hasNextPage']:
        get_juejin_article(after=pageInfo['endCursor'], order=order)

通过调用上面的代码,我们就可以获取到周热榜文章的列表,包括:文章标题和文章详情页链接。

抓包分析文章详情页,会发现文章的具体内容是通过https://post-storage-api-ms.juejin.im/v1/getDetailData的接口获取的,其中标识文章ID的参数是postId,可以从详情页链接获取到,如下:

juejin.cn/post/684490…

其中 5dccbe0ff265da795315a119 就是文章的标识ID

现在,我们解决了热榜文章内容获取的问题。接下来,就需要进行数据清洗了。我们抓取回来的内容数据是HTML格式的,需要去掉无用的HTML标签,得到纯文本数据。

代码如下:

from lxml import etree

def get_content(html):
    # 解析html
    parser = etree.HTML(html, etree.HTMLParser())
    # 获取纯文本
    content = html.xpath('//text()')
    
    return content

在获取完纯文本数据之后,就到了最关键的一步 —— 中文分词。分词的质量直接影响到最后挖掘的效果。这里我们使用常见的中文分词工具jieba

代码如下:

import re
import jieba

# 词语的正则表达式
re_han_default = re.compile('([\u4E00-\u9FD5a-zA-Z0-9+#&\.\_]+)', re.U)

def cut_all(content):
    # 分词
    seg_lst = jieba.lcut(content)
    # 提取词语
    words = [seg for seg in seg_lst if re_han_default.match(seg)]
    
    return " ".join(words)

这只是最基本的中文分词方法,得到的分词质量也比较一般。如果你对中文分词感兴趣,那么推荐你阅读我写的一本掘金小册《深入理解NLP的中文分词:从原理到实践》,里面结合了大量的实例和图示,详细的讲解了中文分词的原理及实践,让你可以从零开始掌握中文分词技术,能够轻松驾驭各种NLP任务。

有了分词结果后,我们接着就要计算每个词的TF-IDF值(在上面的小册里也有详细讲解),TF-IDF值是衡量词对文章的重要程度。

代码如下:

import numpy as np
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.feature_extraction.text import TfidfTransformer

# corpus表对分词结果按文章划分的列表,N表示取前多少个词
def get_keywords(corpus, N=25):
    vectorizer = CountVectorizer()
    X = vectorizer.fit_transform(corpus)

    words = vectorizer.get_feature_names()

    transformer = TfidfTransformer()
    tfidf = transformer.fit_transform(X)
    # tfidf值转化成数组
    weight = tfidf.toarray()

    # 一个默认float型的关键词的字典
    keywords = collections.defaultdict(float)

    for w in weight:
        loc = np.argsort(-w)

        for idx in range(N):
            # 累加关键词的tf-idf值
            keywords[words[loc[idx]]] += w[loc[idx]]

    return keywords

到此,掘金热榜文章的关键词挖掘我们就算完成了,可以直接将结果print出来进行查看了。但这样还是不够直观,我们可以进一步将关键词制作成词云,用可视化的方式来展示数据。

这里我们用到了wordcloud包,代码如下:

from wordcloud import WordCloud

def gen_word_could(keywords):
    wc = WordCloud(
        font_path=u"./SimHei.ttf", # 指定中文字体
        max_words=3000,
        width=1920,
        height=1080,
        background_color="black",
        margin=5
    )
    
    # 通过权值生成词云
    wc.generate_from_frequencies(keywords)
    # 保存词云图片
    wc.to_file("juejin_keywords.png")

其中需要注意的是,用wordcloud显示中文,需要下载中文字体,大家可以通过这个链接下载:SimHei.ttf

我们得到了这样的一个词云图片:

如果需要想进一步提升效果,可以通过添加停用词或者优化中文分词工具的方式,这里就留给大家自己DIY了。

最后,还是要安利一下我写的掘金小册《深入理解NLP的中文分词:从原理到实践》,希望以上内容对大家有所帮助,如果喜欢的话,请点赞、转发、评论,多谢多谢!