说来惭愧,来到博客园也好几年了,养成了时不时来刷刷首页,学习下先进知识的习惯,不过一直都是纯输入没有输出,今天才发现我竟然连博客都没开通。恰好这两天拿博客园练手做了个小项目,就以这个作为开始,写下在园子里的第一篇博客吧。
1. 项目背景
前阵子因为项目需要,学习了一段时间的知识图谱(Knowledge Graph)和自然语言处理NLP的一些知识。这两天项目上不那么忙了,又习惯性的点开博客园首页刷刷,看到“每周热点回顾”系列时,脑子一热,如果我把每周热点回顾系列爬下来做个简单的分析,看看大家都在关注啥,这些关注点会不会随时间变化呢?都有哪些作者不断霸榜呢?
说干就干,打开vscode,新建一个python环境,码起来。
2. 方案论证
如图所示,打开博客园团队的主页,标签里选择“每周热点回顾”,手一抖按个F12,查看了下请求URL和请求方法,得,有戏。
再看看页面源代码, 每一周的热点回顾都用entrylistItemTitle标好了,href里也放好了url链接。
第二步,打开上面提到的链接,就会进入详细的当周热点回顾。我们以22年9月的某一周为例。
在HTML里,通过“noopener”也可以获取这些url的text。
到这里,其实爬虫部分就结束了。我们的目标就是获取每周热点的链接,再进一步获取每周热点当中的随笔和新闻部份。
第三步,将获取的热点随笔和新闻,使用jieba进行分词,获得关键词。
第四步,使用wordcloud生成词云。
第五步,热点词跟踪(TBD)。
3. 代码详解
3.1 爬虫部分
直接使用了python的requests库和beautifulsoup,第一步获取所有“每周热点回顾”的链接,代码和效果如下。
urls = [f'https://www.cnblogs.com/cmt/category/218599.html?page={page}' for page in range(1, 32)] headers = { "content-type": "application/json", "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/95.0.4638.54 Safari/537.36 Edg/95.0.1020.30" } # 这函数用来发送请求,注意实际传递的url值 def craw(url): payload = { "CategoryType": "SiteHome", "ParentCategoryId": 0, "CategoryId": 808, "TotalPostCount": 4000, } # 发送的是get请求 r = requests.get(url, data=json.dumps(payload), headers=headers) return r.text # 解析html,获取url,text和时间戳三部分 def parse(html): soup = BeautifulSoup(html, "html.parser") links = soup.find_all("a", class_="entrylistItemTitle") times=soup.find_all("a",title="permalink") temp=[] for i in range(0,len(links)): url=links[i]["href"] text= links[i].get_text().replace("\n","").strip() ts=times[i].get_text() temp.append((url,text,ts)) # return [(link["href"], link.get_text()) for link in links] return temp
第二步进入每个热点回顾链接,获取其中的随笔和新闻,以及作者。代码和效果如下。
至此爬虫部分结束。
3.2 jieba分词与wordclou生成词云
其实从上一个图也可以看出来,为了省事我用了一个dataframe,这也是为了后续能够按时间(月,年)分析热点词变化做准备,可惜因为懒,只分析了22年的文本。
jieba和wordcloud就不介绍了,这里想强调的是为了修正一些分词效果,我使用了
- 哈工大版本的停用词表,停用词本质上是使用txt文件维护一个列表,分词后通过差集运算去除感染的杂项。
- 用户词典。比如.NET这种词,很容易就被jieba分成了.和NET两个词,因此需要在用户词典中维护一下。
- 手动去除分词后长度为1的汉字,去除停用词表中漏掉的干扰词。
此外,作者部分不需要进行分词,直接count就行。
# 使用自定义词典 jieba.load_userdict('userdict.txt') # 需要添加stopwords def stopwordslist(): stopwords = [line.strip() for line in open('哈工大停用词表.txt',encoding='UTF-8').readlines()] return stopwords sw=stopwordslist()
选择22年之后的每周热点做样本,使用pandas中的value_counts()生成一个包含随笔,新闻,作者和各自词频的dict。代码和效果如下。
# 把每周的热点随笔和新闻进行分词,分词后保存到新建的两列
# 使用stopwords去除掉标点符号和无意义词
ds=[]
for col_index in range(3,5):
res_full=[]
for i in range(0,len(df22)):
temp=df22.iloc[i][col_index].split(',')
for t in temp:
cut=jieba.lcut(t)
# 去掉停用词
cut_res=[i for i in cut if not i in sw]
# 去掉长度为1的词,比如“个”,“人”
cut_res=[i for i in cut if not len(i)==1]
res_full.extend(cut_res)
ds.append(pd.Series(res_full).value_counts())
有了词和词频,生成词云就很简单了。我们各取前50,画图。
随笔:
可以看到,通过自定义词典,.NET 和ASP.NET都很好的实现了分词。
新闻:
热心的朋友可以试一下,其实新闻这部分的关键词变化非常大,但诸如“裁员”这种热门随着时间的走势很有意思。
作者:
感谢各位大佬的贡献。
4. 写在最后
也没用到啥高大上的技术,就是脑子一热,图个短平快,看个热闹,顺带练个手,留个小demo。
欢迎大家来我的Github给我小星星。代码在其中的ipynb文件里。