python爬取知网的数据进行各计算机领域学术关注度指数的可视化

python爬取知网的数据进行各计算机领域学术关注度指数的可视化

最近在思考人生,逛知网时发现知网会对每个科研关键词进行统计,给出一个关注度曲线。

于是我就查看一些关键词的研究发展情况,但是每一次都要自己更换搜索关键词,再点击进去查看曲线。

作为计算机系的学生,这固然不能忍。

于是我决定用python把感兴趣的关键词的数据全部爬取下来绘制到一张图里。

效果如图:
在这里插入图片描述

简单记录下,下面是步骤:

一、爬取数据

1.1. 数据来源

点开一篇论文,会看见摘要下方的关键词
在这里插入图片描述

随便点击一个关键词,就会看到知网已经统计好的关注度指数分析。
在这里插入图片描述

当我们鼠标聚焦曲线时,就会看见弹窗中显示的改点的年份与数据。这里的数据就是该关键词在该年份的关注度指数,或者说该年发表的与该关键词相关的论文数量。
在这里插入图片描述

1.2. 爬取源码

1.2.1. 分析链接

数据找到了,我们观察本页面的链接

https://kns.cnki.net/kcms/detail/knetsearch.aspx?sfield=kw&skey=%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F&code=&v=-7Yty5Rksx3cmKbdO0sTT1XnIRr43Xy2xHdd7lZluh_xKEB2Dv_u2akX9YnCHba8

链接很长,url中所含的参数比较多,我们去掉一些不相关参数,只留下skey="推荐系统"这个关键词试试:

https://kns.cnki.net/kcms/detail/knetsearch.aspx?sfield=kw&skey=%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F

在浏览器输入,发现依旧能够获取数据。那么我们只需要替换skey=" "里面的关键词,就可以得到不同关键词的关注度指数页了。

注:url中 skey=“推荐系统” 与 skey=%E6%8E%A8%E8%8D%90%E7%B3%BB%E7%BB%9F 是等价的,只是在浏览器输入时会转码。

很好!此时再定位数据所在的标签就好了!浏览器点击F12打开审查元素,耐心寻找。发现数据就在一个有关键字“renderLineChart”的json数据里。
在这里插入图片描述

1.2.2. 爬取页面数据

这时,我们迫不及待的准备爬取页面再拿到数据

import requests
if __name__ == '__main__':
    url="https://kns.cnki.net/kcms/detail/knetsearch.aspx?sfield=kw&skey=推荐系统"
    response = requests.get(url)
    print(response.content.decode('utf-8'))

请求成功了!在结果里搜索“renderLineChart”,发现找不到!这时候挠掉了几根头发。
在这里插入图片描述

显然,爬取数据失败了。
为什么失败?可能是该数据并不是静态存储在doc中的,而是动态获取的。

1.2.3. 解决爬取js动态数据问题

这时候回到浏览器f12界面。点击网络(network),刷新页面,会发现所有的数据请求都在这个列表里。226条请求如何找到我需要的数据请求呢?
在这里插入图片描述

“数据就在一个有关键字“renderLineChart”的json数据里” 在网络页面ctrl+f搜索:renderLineChart。
浏览器就自动帮我们定位请求所在位置了。
在这里插入图片描述

此时我们点击“标头”,就可以看到浏览器告诉我们的请求URL。
在这里插入图片描述

链接如下:

https://kns.cnki.net/kcms/detail/frame/knetlist.aspx?name=%e6%8e%a8%e8%8d%90%e7%b3%bb%e7%bb%9f&infotype=9&codetype=j&catalogName=%E5%85%B3%E6%B3%A8%E5%BA%A6%E6%8C%87%E6%95%B0%E5%88%86%E6%9E%90&vl=-7Yty5Rksx3cmKbdO0sTT1XnIRr43Xy2xHdd7lZluh_hb-vEEpRBDQuaXcWzE0YC

这时,我们迫不及待的准备爬取页面再拿到数据

import requests
if __name__ == '__main__':
    url="https://kns.cnki.net/kcms/detail/frame/knetlist.aspx?name=推荐系统&infotype=9&codetype=j&catalogName=%E5%85%B3%E6%B3%A8%E5%BA%A6%E6%8C%87%E6%95%B0%E5%88%86%E6%9E%90&vl=-7Yty5Rksx3cmKbdO0sTT1XnIRr43Xy2xHdd7lZluh_hb-vEEpRBDQuaXcWzE0YC"
    response = requests.get(url)
    print(response.content.decode('utf-8'))

此时发现请求是成功的!返回的内容如下:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html>
<head>
    <title></title>
</head>
<body>
    <form name="form1" method="post" action="knetlist.aspx?name=%u63a8%u8350%u7cfb%u7edf&amp;infotype=9&amp;codetype=j&amp;catalogName=%u5173%u6ce8%u5ea6%u6307%u6570%u5206%u6790&amp;vl=-7Yty5Rksx3cmKbdO0sTT1XnIRr43Xy2xHdd7lZluh_hb-vEEpRBDQuaXcWzE0YC" id="form1">
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTUxMTcwNzgxMGRkjhjYRPTUl6/K/rznK1hDjhK9Xj3ZhdVm0yS5/MSd+tk=" />

<input type="hidden" name="__VIEWSTATEGENERATOR" id="__VIEWSTATEGENERATOR" value="30B6FF1C" />
    <div>
    
    </div>
    </form>
</body>
</html>

但是为什么没有我们要的数据?这时又挠掉了几根头发。
其实就是目标网站的反爬机制了。进行数据请求时,有时候会返回一些代码,或者空白,或者缺失的数据,很可能是因为目标网站的反爬虫机制。你直接url来请求,我拒绝给你数据。

这时候就需要伪装一下了。

伪装成正常浏览器进行爬虫

伪装的方式很简单,添加假请求头fake_headers,再把fake_headers放到get()的参数里就好了。
我先是添加了cookie,发现没用。又添加了Referer就成功了。这些信息在浏览器f12界面都能找到。

    #伪造浏览器请求头
    #cookie = 'Ecp_ClientId=5210628102801308911;cnkiUserKey=b261114d-5456-f7a0-d218-29c90a247b35;Ecp_ClientIp=58.34.66.42;_pk_ref=%5B%22%22%2C%22%22%2C1627001927%2C%22https%3A%2F%2Fcn.bing.com%2F%22%5D;ASP.NET_SessionId=wx2bvmuixh3bq3r5meb5tnk5;SID_kns8=123123;CurrSortFieldType=desc;SID_kcms=124101;CurrSortField=%e5%8f%91%e8%a1%a8%e6%97%b6%e9%97%b4%2f(%e5%8f%91%e8%a1%a8%e6%97%b6%e9%97%b4%2c%27TIME%27);Ecp_IpLoginFail=21072358.34.66.42, 10.210.0.12;_pk_id=fc0ebc8f-840b-44c6-b064-37d1205b7bcc.1624847325.3.1627002597.1627001927.;SID_kns_new=kns123110;'
    fake_headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36',
        #'Cookie':cookie,  #cookie测试,不需要cookie也可获取
        #'Referer':'https://kns.cnki.net/kcms/detail/knetsearch.aspx?sfield=kw&skey=%E5%8C%BA%E5%9D%97%E9%93%BE&code=&v=9tzIsR7LVgUKQ_Ucqcy13H0PVvC_8a71XDfdwE8ntLnmoNnjXXC3V0UrNnvhguYG'
        'c':'https://www.cnki.net/'       #Referer测试,需要Referer,否则获取值为空
    }
    response =  requests.get(targetUrl,headers =fake_headers)

伪装的很完美,数据就在我们的返回值里了。结果:

<html>
  <head>
   ...省略
   </head>
  <body onload="ResezeParent(0);SetParentCatalog(); closePopMore(1)"><script type="text/javascript" src="https://piccache.cnki.net/kdn/kcms8/detail/js/min/WideScreen.min.js"></script><h2 class="title1" id="catalog_GZDZSFX">
      关注度指数分析 <span class="desc">(检索范围:源数据库,包括期刊库、博士论文库、硕士论文库、报纸库、会议库)</span></h2>
    <div class="titleSide"><a class="more" onclick="TurnPageToKnsApp('cidx')">
        查看更多指数分析结果
      </a></div>
    <div class="listcont">
      <div id="KwsChart"></div><script>
        RenderLineChart([{name:"推荐系统",data:[[1989,1],[1995,1],[1999,1],[2000,1],[2001,2],[2002,8],[2003,14],[2004,24],[2005,60],[2006,80],[2007,109],[2008,117],[2009,138],[2010,192],[2011,234],[2012,281],[2013,437],[2014,573],[2015,741],[2016,846],[2017,941],[2018,1043],[2019,1063],[2020,917],[2021,165]]}]);
      </script></div>
  </body>
</html><!-- 后台处理 耗时:15.625 毫秒 -->

Process finished with exit code 0

1.3. 批量爬取

定义一个列表存储自己感兴趣的关键词,遍历替换url中的关键词。
定义函数getInfo(),传入url进行请求操作。
为了爬取过程的流畅性,进行异常处理,异常时回调,防止中断。

import requests

keyword=['机器学习','深度学习','物联网','云计算','计算模型','大数据','数学建模','图像处理','计算机视觉','计算机体系结构','理论计算机科学','计算机科学','区块链','人工智能']

def getInfo(targetUrl):
    #伪造浏览器请求头
    fake_headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36',
        #'Cookie':cookie,  #cookie测试,不需要cookie也获取
        #'Referer':'https://kns.cnki.net/kcms/detail/knetsearch.aspx?sfield=kw&skey=%E5%8C%BA%E5%9D%97%E9%93%BE&code=&v=9tzIsR7LVgUKQ_Ucqcy13H0PVvC_8a71XDfdwE8ntLnmoNnjXXC3V0UrNnvhguYG'
        'Referer':'https://www.cnki.net/'       #Referer测试,需要Referer,否则获取值为空
    }

    try:
        response =  requests.get(targetUrl,headers =fake_headers)
        print("目标网站:" + targetUrl + ";" + "\n请求结果代码:" + str(response.status_code))
    except:
        getInfo(targetUrl)  #异常时回调
    return response


if __name__ == '__main__':
    for i in range(len(keyword)):
        url = "https://kns.cnki.net/kcms/detail/frame/knetlist.aspx?name=" + keyword[i] + "&infotype=9&codetype=j&catalogName=%E5%85%B3%E6%B3%A8%E5%BA%A6%E6%8C%87%E6%95%B0%E5%88%86%E6%9E%90&vl=9tzIsR7LVgUKQ_Ucqcy13H0PVvC_8a71XDfdwE8ntLnvQsAEFaVMjFwHdnwGLpbu"
        response = getInfo(url)
        print(response.content.decode('utf-8'))

二、解析数据

以上获取到的数据非常混乱,是html格式的数据,我们需要解析取出其中我们需要的。

定义一个函数:prase(response),将请求的返回值传入进行解析。解析过程代码有详细注释。
注意解析时需要引入一些库。没有的库可自行pip安装。

from bs4 import BeautifulSoup
import re
import json

#添加全局变量,存储解析后得到的数据
year_data_all = []
keyword_all =[]


def prase(response):
    '''
    """
    调式阶段,把网页内容存至本地进行解析。避免写代码调试时一直向目标服务器请求。
    解析完成后再将此段代码注释,添加:html = response.content.decode('utf-8')
    将请求与解析合并
    """
    # 以写格式打开文件
    UnParseResponse = open('UnParseResponse.html', 'w', encoding="utf-8")
    # 将初步请求结果写入文件
    UnParseResponse.write(response.content.decode('utf-8'))
    UnParseResponse.close()
    # 以读方式打开文件名为html_file1.html的文件
    UnParseResponse = open('UnParseResponse.html', 'r', encoding="utf-8")
    # 把文件的内容全部读取出来并赋值给html变量
    html = UnParseResponse.read()
    # 关闭文件对象
    UnParseResponse.close()
    '''
    html = response.content.decode('utf-8')     #当上述代码注释时,将这条添加上
    # 初始化BeautifulSoup
    soup = BeautifulSoup(html,'lxml')
    #print(soup)  打印请求到的数据
    div=soup.find('div',class_ = 'listcont')
    data = div.find('script').string
    #print(data)  #需要的字符串,这里还可以尝试将数据转化成json,但本人习惯使用字符串提取的方式
    name = re.findall("name:\"(.+?)\"", str(data)) #从字符串中提取出关键词名称
    year_data = re.findall("data:(.+?)}", str(data))  # 从字符串中提取出年份与数量 type:str
    #防止找不到字符串从而返回的列表为空,添加异常处理
    try:
        name = name[0]
        keyword_all.append(name)
        year_data = json.loads(year_data[0])  # 将字符串转换为列表 type:list shpae:[[]]
        year_data_all.append(year_data)
        #print(year_data_all)
        #print(keyword_all)
    except:
        print("发生异常,字符串提取错误:name="+str(name)+"year_data="+str(year_data))

现在我们得到了两个列表,大概的形式时这样的:

keyword_all=['机器学习','深度学习',...,'人工智能']
这是一个三层嵌套列表:
year_data_all = [
                    [[1999,1],[2000,2],...,[年份,数值]],
                    [[1987,5],[1988,7],...,[年份,数值]],
                    ...,
                    [[1977,212],[1978,145],...,[年份,数值]]
                ]

解析完之后记得在main的for循环中加上。

if __name__ == '__main__':
    for i in range(len(keyword)):
        url = "https://kns.cnki.net/kcms/detail/frame/knetlist.aspx?name="+urllib.parse.quote(keyword[i])+"&infotype=9&codetype=j&catalogName=%E5%85%B3%E6%B3%A8%E5%BA%A6%E6%8C%87%E6%95%B0%E5%88%86%E6%9E%90&vl=9tzIsR7LVgUKQ_Ucqcy13H0PVvC_8a71XDfdwE8ntLnvQsAEFaVMjFwHdnwGLpbu"
        response = getInfo(url)
        #对每一个关键词爬取到的数据存储一起存储到两个列表year_data_all[]和keyword_all[]中,正常情况下,keyword_all[]与keyword[]应该是一致的
        prase(response)     

三、格式化数据

我的目标是获取到数据以后,放到一起绘制一张总的趋势发展图。所以我希望它是这样的格式

机器学习深度学习人工智能
197612821224
197712821224
202112821224

那么就定义一个函数进行数据格式化。data_format(year_data,keyword_data),传入的参数是上一步解析得到的两个列表。
格式化过程代码有详细注释,注意引包。

import pandas as pd
def data_format(year_data,keyword_data):
    year=[]   #年份长度,每一个关键字爬取的年份是不一样的,这里将所有关键字的年份拼接,并排序,再删去重复值
    for i in range(len(year_data)):
        for j in range(len(year_data[i])):
            year.append(year_data[i][j][0])     #年份拼接
    year.sort()     #排序
    year=list(set(year))    #删除重复值
    #print(year)
    data_df = pd.DataFrame(index=year, columns=keyword_data)     #创建dataframe格式的表格
    #data_df.at[1980,'机器学习']=1;          #测试:给df的对应坐标赋值

    for i in range(len(year_data)):
        for j in range(len(year_data[i])):
            data_df.at[year_data[i][j][0],keyword_data[i]] = year_data[i][j][1];   #注意此处:year_data[i]对于的是keyword_data[i],是按照爬取时的顺序
    print(data_df)
    return data_df

此时得到的数据应该是很舒服的:
在这里插入图片描述

记得修改main函数

if __name__ == '__main__':
    
    for i in range(len(keyword)):
        url = "https://kns.cnki.net/kcms/detail/frame/knetlist.aspx?name="+urllib.parse.quote(keyword[i])+"&infotype=9&codetype=j&catalogName=%E5%85%B3%E6%B3%A8%E5%BA%A6%E6%8C%87%E6%95%B0%E5%88%86%E6%9E%90&vl=9tzIsR7LVgUKQ_Ucqcy13H0PVvC_8a71XDfdwE8ntLnvQsAEFaVMjFwHdnwGLpbu"
        response = getInfo(url)
        prase(response)     #对每一个关键词爬取到的数据存储一起存储到两个列表year_data_all[]和keyword_all[]中,正常情况下,keyword_all[]与keyword[]应该是一致的
    data = data_format(year_data_all, keyword_all)     #处理数据,将数据格式化为dataframe的二维表格模式

四、绘制图像

终于可以绘制图像啦!
绘制图像用的是经典的matplotlib包,可以绘制许多好看的图,折线图,柱形图,网络图等。
我只需要绘制一个折线图。为了美观,添加了自己找的颜色表。

def draw_visual(data):
    # 设置字体为楷体
    plt.rcParams['font.sans-serif'] = ['KaiTi']
    data=data.fillna(0)  #将NaN转为0
    #颜色表
    color_list=['silver','lightcoral','steelblue','orange','springgreen','lightseagreen','teal','deepskyblue','lightskyblue','grey','violet','darkseagreen','darkviolet','tomato']
    for i in range(0,data.shape[1]):
        x = data.index  # x轴
        y =  list(data[data.columns[i]])  #y轴
        plt.plot(x,y,c=color_list[i],label=str(data.columns[i]))
        plt.gca().xaxis.set_major_locator(ticker.MultipleLocator(1))    #设置横坐标密度
    plt.title(u'各计算机领域学术研究关注度指数发展趋势',fontsize=20)  # 设置标题
    plt.xlabel('年份',fontsize=15)  # 设置x,y轴的标签
    plt.ylabel('关注度指数',fontsize=15)
    plt.legend()
    plt.show()

记得在main函数加上

if __name__ == '__main__':
    #url = "https://kns.cnki.net/kcms/detail/knetsearch.aspx?sfield=kw&skey="+ urllib.parse.quote(keyword[0])

    for i in range(len(keyword)):
        url = "https://kns.cnki.net/kcms/detail/frame/knetlist.aspx?name="+urllib.parse.quote(keyword[i])+"&infotype=9&codetype=j&catalogName=%E5%85%B3%E6%B3%A8%E5%BA%A6%E6%8C%87%E6%95%B0%E5%88%86%E6%9E%90&vl=9tzIsR7LVgUKQ_Ucqcy13H0PVvC_8a71XDfdwE8ntLnvQsAEFaVMjFwHdnwGLpbu"
        response = getInfo(url)
        prase(response)     #对每一个关键词爬取到的数据存储一起存储到两个列表year_data_all[]和keyword_all[]中,正常情况下,keyword_all[]与keyword[]应该是一致的
    #print(year_data_all)
    #print(keyword_all)
    data = data_format(year_data_all, keyword_all)     #处理数据,将数据格式化为dataframe的二维表格模式
    draw_visual(data)

激动人心的图终于到来啦!
在这里插入图片描述

在这里插入图片描述

感觉折线图凹凸不平不太舒适

给代码添加平滑处理,思路其实很简单:把横坐标分割成很多个点,再将原来的数据在新的横坐标上拟合,再用新的横纵坐标画图。

def draw_visual(data):
    # 设置字体为楷体
    plt.rcParams['font.sans-serif'] = ['KaiTi']
    data=data.fillna(0)  #将NaN转为0
    #颜色表
    color_list=['silver','lightcoral','steelblue','orange','springgreen','lightseagreen','teal','deepskyblue','lightskyblue','grey','violet','darkseagreen','darkviolet','tomato']
    for i in range(0,data.shape[1]):
        #新增代码
        x = data.index  # x轴
        y =  list(data[data.columns[i]])  #y轴
        x_new = np.linspace(x.min(), x.max(), 300)      #300表示我需要在x的最小值和最大值之间分割的点数
        y_smooth = make_interp_spline(x,y)(x_new)       #将原来的x坐标系向密度更大的坐标系拟合,达到平滑的效果
        #plt.plot(data.index,list(data[data.columns[i]]),c=color_list[i],label=str(data.columns[i]))
        plt.plot(x_new, y_smooth,c=color_list[i], label=str(data.columns[i]))       #画图
        #新增代码
        plt.gca().xaxis.set_major_locator(ticker.MultipleLocator(1))    #设置横坐标密度
    plt.title(u'各计算机领域学术研究关注度指数发展趋势',fontsize=20)  # 设置标题
    plt.xlabel('年份',fontsize=15)  # 设置x,y轴的标签
    plt.ylabel('关注度指数',fontsize=15)
    plt.legend()
    plt.show()

在这里插入图片描述

舒服多了!
不得不感叹,阿尔法狗之后,深度学习和人工智能的趋势真是太惊人了。
计算机应用层面的研究都比较热门。
在这里插入图片描述

看看底层的研究领域,计算机体系结构,理论计算机科学,还有与数学相关的话题,都是关注度很小的,比较困难的。但也是奠定计算机世界最重要的一环。

这就是真理掌握在少数人手中吧。

附:所有代码

import urllib.parse
import requests
from bs4 import BeautifulSoup
import pandas as pd
from matplotlib import pyplot as plt
import matplotlib.ticker as ticker
import numpy as np
from scipy.interpolate import make_interp_spline
import re
import json


keyword=['机器学习','深度学习','物联网','云计算','计算模型','大数据','数学建模','图像处理','计算机视觉','计算机体系结构','理论计算机科学','计算机科学','区块链','人工智能']
year_data_all = []
keyword_all =[]

#cookie = 'Ecp_ClientId=5210628102801308911;cnkiUserKey=b261114d-5456-f7a0-d218-29c90a247b35;Ecp_ClientIp=58.34.66.42;_pk_ref=%5B%22%22%2C%22%22%2C1627001927%2C%22https%3A%2F%2Fcn.bing.com%2F%22%5D;ASP.NET_SessionId=wx2bvmuixh3bq3r5meb5tnk5;SID_kns8=123123;CurrSortFieldType=desc;SID_kcms=124101;CurrSortField=%e5%8f%91%e8%a1%a8%e6%97%b6%e9%97%b4%2f(%e5%8f%91%e8%a1%a8%e6%97%b6%e9%97%b4%2c%27TIME%27);Ecp_IpLoginFail=21072358.34.66.42, 10.210.0.12;_pk_id=fc0ebc8f-840b-44c6-b064-37d1205b7bcc.1624847325.3.1627002597.1627001927.;SID_kns_new=kns123110;'
def getInfo(targetUrl):
    #伪造浏览器请求头
    fake_headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.79 Safari/537.36',
        #'Cookie':cookie,  #cookie测试,不需要cookie也获取
        #'Referer':'https://kns.cnki.net/kcms/detail/knetsearch.aspx?sfield=kw&skey=%E5%8C%BA%E5%9D%97%E9%93%BE&code=&v=9tzIsR7LVgUKQ_Ucqcy13H0PVvC_8a71XDfdwE8ntLnmoNnjXXC3V0UrNnvhguYG'
        'Referer':'https://www.cnki.net/'       #Referer测试,需要Referer,否则获取值为空
    }

    try:
        response =  requests.get(targetUrl,headers =fake_headers)
        print("目标网站:" + targetUrl + ";" + "\n请求结果代码:" + str(response.status_code))
    except:
        getInfo(targetUrl)
    return response

def prase(response):
    '''
    """
    调式阶段,把网页内容存至本地进行解析。避免写代码调试时一直向目标服务器请求。
    解析完成后再将此段代码注释,添加:html = response.content.decode('utf-8')
    将请求与解析合并
    """
    # 以写格式打开文件
    UnParseResponse = open('UnParseResponse.html', 'w', encoding="utf-8")
    # 将初步请求结果写入文件
    UnParseResponse.write(response.content.decode('utf-8'))
    UnParseResponse.close()
    # 以读方式打开文件名为html_file1.html的文件
    UnParseResponse = open('UnParseResponse.html', 'r', encoding="utf-8")
    # 把文件的内容全部读取出来并赋值给html变量
    html = UnParseResponse.read()
    # 关闭文件对象
    UnParseResponse.close()
    '''
    html = response.content.decode('utf-8')     #当上述代码注释时,将这条添加上
    # 初始化BeautifulSoup
    soup = BeautifulSoup(html,'lxml')
    #print(soup)  打印请求到的数据
    div=soup.find('div',class_ = 'listcont')
    data = div.find('script').string
    #print(data)  #需要的字符串,这里还可以尝试将数据转化成json,但本人习惯使用字符串提取的方式
    name = re.findall("name:\"(.+?)\"", str(data)) #正则表达式:从字符串中提取出介于  name:“ 和  ” 之间的关键词名称
    year_data = re.findall("data:(.+?)}", str(data))  # 从字符串中提取出年份与数量 type:str
    #防止找不到字符串从而返回的列表为空,添加异常处理
    try:
        name = name[0]
        keyword_all.append(name)
        year_data = json.loads(year_data[0])  # 将字符串转换为列表 type:list shpae:[[]]
        year_data_all.append(year_data)
        #print(year_data_all)
        #print(keyword_all)
    except:
        print("发生异常,字符串提取错误:name="+str(name)+"year_data="+str(year_data))

def data_format(year_data,keyword_data):
    year=[]   #年份长度,每一个关键字爬取的年份是不一样的,这里将所有关键字的年份拼接,并排序,再删去重复值
    for i in range(len(year_data)):
        for j in range(len(year_data[i])):
            year.append(year_data[i][j][0])     #年份拼接
    year.sort()     #排序
    year=list(set(year))    #删除重复值
    #print(year)
    data_df = pd.DataFrame(index=year, columns=keyword_data)     #创建dataframe格式的表格
    #data_df.at[1980,'机器学习']=1;          #测试:给df的对应坐标赋值

    for i in range(len(year_data)):
        for j in range(len(year_data[i])):
            data_df.at[year_data[i][j][0],keyword_data[i]] = year_data[i][j][1];   #注意此处:year_data[i]对于的是keyword_data[i],是按照爬取时的顺序
    print(data_df)
    return data_df

def draw_visual(data):
    # 设置字体为楷体
    plt.rcParams['font.sans-serif'] = ['KaiTi']
    data=data.fillna(0)  #将NaN转为0
    #颜色表
    color_list=['silver','lightcoral','steelblue','orange','springgreen','lightseagreen','teal','deepskyblue','lightskyblue','grey','violet','darkseagreen','darkviolet','tomato']
    for i in range(0,data.shape[1]):
        x = data.index  # x轴
        y =  list(data[data.columns[i]])  #y轴
        x_new = np.linspace(x.min(), x.max(), 300)      #300表示我需要在x的最小值和最大值之间分割的点数
        y_smooth = make_interp_spline(x,y)(x_new)       #将原来的x坐标系向密度更大的坐标系拟合,达到平滑的效果
        #plt.plot(data.index,list(data[data.columns[i]]),c=color_list[i],label=str(data.columns[i]))
        plt.plot(x_new, y_smooth,c=color_list[i], label=str(data.columns[i]))       #画图
        plt.gca().xaxis.set_major_locator(ticker.MultipleLocator(1))    #设置横坐标密度
    plt.title(u'各计算机领域学术研究关注度指数发展趋势',fontsize=20)  # 设置标题
    plt.xlabel('年份',fontsize=15)  # 设置x,y轴的标签
    plt.ylabel('关注度指数',fontsize=15)
    plt.legend()
    plt.show()

if __name__ == '__main__':
    #url = "https://kns.cnki.net/kcms/detail/knetsearch.aspx?sfield=kw&skey="+ urllib.parse.quote(keyword[0])

    for i in range(len(keyword)):
        url = "https://kns.cnki.net/kcms/detail/frame/knetlist.aspx?name="+urllib.parse.quote(keyword[i])+"&infotype=9&codetype=j&catalogName=%E5%85%B3%E6%B3%A8%E5%BA%A6%E6%8C%87%E6%95%B0%E5%88%86%E6%9E%90&vl=9tzIsR7LVgUKQ_Ucqcy13H0PVvC_8a71XDfdwE8ntLnvQsAEFaVMjFwHdnwGLpbu"
        response = getInfo(url)
        prase(response)     #对每一个关键词爬取到的数据存储一起存储到两个列表year_data_all[]和keyword_all[]中,正常情况下,keyword_all[]与keyword[]应该是一致的
    #print(year_data_all)
    #print(keyword_all)
    data = data_format(year_data_all, keyword_all)     #处理数据,将数据格式化为dataframe的二维表格模式
    draw_visual(data)



20210723
by zhzhang
  • 9
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值