在「掘金」上有非常多优秀的内容,我经常会到热榜上找一些感兴趣的文章来看,一方面可以学习到实用的技术方案,另一方面可以了解到大牛的思考方式。但平台每天都有大量新文章产生,再加上历史累计的,一个人想要把这些内容都看完,几乎是不可能完成的任务。
可是,又很好奇大牛都讲了些什么?于是决定亲自动手来挖掘一下热榜文章的关键词,并制作成词云。同时将这个过程详细的记录下来,并分享给大家,为了方便大家今后有类似需求时可以借鉴。
既然我们想要分析掘金热榜的文章,那么就需要获取到文章的内容数据。幸运的是,掘金的网站架构设计得非常赞,网页内容基本都是通过接口获取,而且接口数据是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
,可以从详情页链接获取到,如下:
其中 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的中文分词:从原理到实践》,希望以上内容对大家有所帮助,如果喜欢的话,请点赞、转发、评论,多谢多谢!