爬取百度学术文章及文本挖掘剖析

优采云 发布时间: 2020-08-18 08:17

  爬取百度学术文章及文本挖掘剖析

  

  可以看见我们须要填入关键词,才能进行搜索我们须要的类型文章,在此我以“牛肉品质”为例,进行搜索。我们在搜索栏中单击滑鼠右键,在单击检测,查看源码。

  

  用相同的方式查看“百度一下”。

  

  这样做的目的是为了使用selenium进行手动输入,并搜索。

  这里写一个方式,传入一个参数——要输入的关键词。我是使用的谷歌浏览器的driver,也可以使用PhantomJS无界面的driver。

  from bs4 import BeautifulSoup

from selenium import webdriver

import time

import pandas as pd

import requests

import re

from collections import defaultdict

def driver_open(key_word):

url = "http://xueshu.baidu.com/"

# driver = webdriver.PhantomJS("D:/phantomjs-2.1.1-windows/bin/phantomjs.exe")

driver = webdriver.Chrome("D:\\Program Files\\selenium_driver\\chromedriver.exe")

driver.get(url)

time.sleep(10)

driver.find_element_by_class_name('s_ipt').send_keys(key_word)

time.sleep(2)

driver.find_element_by_class_name('s_btn_wr').click()

time.sleep(2)

content = driver.page_source.encode('utf-8')

driver.close()

soup = BeautifulSoup(content, 'lxml')

return soup

  然后,进入搜索界面,我们接着剖析。我们须要抓取文章的题目,同时要进行翻页爬取多页。

  

  怎么样实现发觉呢?我们点开多个页面观察网页URL:

  第一页:

  牛肉品质&pn=0&tn=SE_baiduxueshu_c1gjeupa&ie=utf-8&f=3&sc_f_para=sc_tasktype%3D{firstSimpleSearch}&sc_hit=1

  第二页:

  牛肉品质&pn=10&tn=SE_baiduxueshu_c1gjeupa&ie=utf-8&f=3&sc_f_para=sc_tasktype%3D{firstSimpleSearch}&sc_hit=1

  第三页:

  牛肉品质&pn=20&tn=SE_baiduxueshu_c1gjeupa&ie=utf-8&f=3&sc_f_para=sc_tasktype%3D{firstSimpleSearch}&sc_hit=1

  可以发觉这三页URL中只有一个地方发生了改变,就是“pn”的值,从0开始,然后每次递增10,所以,我们通过这个就可以挺好的实现翻页了。

  def page_url_list(soup, page=0):

fir_page = "http://xueshu.baidu.com" + soup.find_all("a", class_="n")[0]["href"]

urls_list = []

for i in range(page):

next_page = fir_page.replace("pn=10", "pn={:d}".format(i * 10))

response = requests.get(next_page)

soup_new = BeautifulSoup(response.text, "lxml")

c_fonts = soup_new.find_all("h3", class_="t c_font")

for c_font in c_fonts:

url = "http://xueshu.baidu.com" + c_font.find("a").attrs["href"]

urls_list.append(url)

return urls_list

  接下来就是对感兴趣的地方施行抓取了。我们步入详情页,我们须要抓取的东西有:题目、摘要、出版源、被引用量,有关键词。

  

  还是根据老方式,将这种须要爬取的东西一个一个检测源码,用CSS select 方法处理。

  def get_item_info(url):

print(url)

# brower = webdriver.PhantomJS(executable_path= r"C:\\phantomjs-2.1.1-windows\\bin\\phantomjs.exe")

# brower.get(url)

# time.sleep(2)

# more_text = brower.find_element_by_css_selector('p.abstract_more.OP_LOG_BTN')

# try:

# more_text.click()

# except:

# print("Stopping load more")

# content_details = brower.page_source.encode('utf-8')

# brower.close()

# time.sleep(3)

content_details = requests.get(url)

soup = BeautifulSoup(content_details.text, "lxml")

# 提取文章题目

title = ''.join(list(soup.select('#dtl_l > div > h3 > a')[0].stripped_strings))

# 提取文章作者

authors = ''.join(str(author_) for author_ in list(soup.select('div.author_wr')[0].stripped_strings)[1:])

# 提取摘要

abstract = list(soup.select('div.abstract_wr p.abstract')[0].stripped_strings)[0].replace("\u3000", ' ')

# 提取出版社和时间

fir_publish_text = list(soup.select('p.publish_text'))

if len(fir_publish_text) == 0:

publish_text = "NA"

publish = "NA"

year = "NA"

else:

publish_text = list(soup.select('p.publish_text')[0].stripped_strings)

publish = publish_text[0]

publish = re.sub("[\r\n ]+", "", publish)

publish_text = ''.join(publish_text)

publish_text = re.sub("[\r\n ]+", "", publish_text)

# 提取时间

match_re = re.match(".*?(\d{4}).*", publish_text)

if match_re:

year = int(match_re.group(1))

else:

year = 0

# 提取引用量

ref_wr = list(soup.select('a.sc_cite_cont'))

if len(ref_wr) == 0:

ref_wr = 0

else:

ref_wr = list(soup.select('a.sc_cite_cont')[0].stripped_strings)[0]

# 提取关键词

key_words = ','.join(key_word for key_word in list(soup.select('div.dtl_search_word > div')[0].stripped_strings)[1:-1:2])

# data = {

# "title":title,

# "authors":authors,

# "abstract":abstract,

# "year":int(year),

# "publish":publish,

# "publish_text":publish_text,

# "ref_wr":int(ref_wr),

# "key_words":key_words

# }

return title, authors, abstract, publish_text, year, publish, ref_wr, key_words

  这里有非常说明一下:在爬取摘要的时侯,有一个JS动态加载,“更多”样式加载按键。所以,我想要将摘要全部爬出来,可能就要使用selenium模仿点击操作(我在代码中加了注释的地方)。但是,我没有用这些方法由于多次访问网页,可能会有很多问题,一个是速率的问题,一个是很容易被服务器拒绝访问,所以在这里我只爬取了一部分摘要。

  接着保存爬取的数据,这里我为了前面直接用pandas读取处理,且数据量不大,所以直接保存为csv格式。

  def get_all_data(urls_list):

dit = defaultdict(list)

for url in urls_list:

title, authors, abstract, publish_text, year, publish, ref_wr, key_words = get_item_info(url)

dit["title"].append(title)

dit["authors"].append(authors)

dit["abstract"].append(abstract)

dit["publish_text"].append(publish_text)

dit["year"].append(year)

dit["publish"].append(publish)

dit["ref_wr"].append(ref_wr)

dit["key_words"].append(key_words)

return dit

  def save_csv(dit):

data = pd.DataFrame(dit)

columns = ["title", "authors", "abstract", "publish_text", "year", "publish", "ref_wr", "key_words"]

data.to_csv("abstract_data.csv", index=False, columns=columns)

print("That's OK!")

  到此,程序完成,然后开始爬取前20页的数据:

  if __name__ == "__main__":

key_word = "牛肉品质"

soup = driver_open(key_word)

urls_list = page_url_list(soup, page=20)

dit = get_all_data(urls_list)

save_csv(dit)

  爬取完以后,我们用pandas进行读取。

  data = pd.read_csv("abstract_data.csv")

data.head()

  

  2. 数据清洗及剖析

  在publish这一列中,还有小问题须要处理。如下,有些行中出现了冒号。

  

  我们将它处理掉。

  data["publish"] = data["publish"].map(lambda x: str(x).replace(',', ""))

  同时,发现在出版社这一栏南京农业大学有两种表示(《南京农业大学》,南京农业大学),其实它们都是一个意思,需要统一下。

  data.publish = data.publish.map(lambda x: re.sub("(.+大学$)", r"《\1》", x))

  这样就将所有以“大学”结尾的出版社加上了“《》”进行统一。

  data.nunique()

  

  可以看出现今200篇论文中只在91个出版社发表过,我们来统计前10个发表最多的出版社的发表情况。

  data.publish.value_counts()[:10]

  

  可视化结果:

  首先使用seaborn作图

  

  其次使用Web可视化工具plotly展示

  

  

  对于“牛肉品质”相关的文章,大家都倾向于投《食品科学》、《肉类研究》、《延边大学》等刊物。

  下面,我们接着看这几年来文章发表的情况。

  首先,我们先查看数据,有没有缺位值。

  data.info()

  

  这里红框的地方,时间这一列只有197个数据,说明有三个缺位值。因为,缺失值甚少,所以,我们直接删掉她们。

  df = data.dropna(axis=0, how="any")

df.info()

  

  这里,因为“year”列是浮点型的类型,需要转化一下类型。

  df["year"] = df["year"].map(lambda x: str(int(x)))

df["year"].value_counts()

  

  进行可视化展示:

  plt.figure(figsize=(12, 5))

# sns.set_style("darkgrid",{"font.sans-serif":['simhei','Droid Sans Fallback']})

temp = df["year"].value_counts()

sns.countplot(

x = df.year,

palette = "Set3",

order = temp.index

)

  

  通过这张图其实可以看出哪些年发表文章最多,但是却不能展示随时间走势,看到发表趋势。下面就通过时间序列剖析的形式诠释一下。

  df["year"] = pd.to_datetime(df["year"])

df["year"].value_counts().resample("Y").sum().plot.line()

  

  这样就展示了随时间变化,发表猪肉品质的文章的趋势。但是,还是不够美观。下面使用Web可视化工具plotly再度展示。

  

  这张图就更能凸显1997到2018年期间山羊品质文章的发表情况了,图下方还有一个时间bar,它可以前后拖动,进行放大。这就是使用Web可视化工具的最大用处,可以愈发形象具体的可视化展示。

  

  接下来,我们再看什么作者在1997到2018年期间发表文章最多。

  data.authors.value_counts()[:10]

  

  考虑到发表文章的作者数目不统一,因此,我们只提取第一作者进行剖析。

  data["authors_fir"] = data.authors.map(lambda x: x.split(",")[0])

len(data["authors_fir"].unique())

  得出一共有171位不同的作者以第一作者的身分发表过关于“牛肉品质”的文章。

  data.authors_fir.value_counts()[:10]

  

  我们再来看发表最多5篇的万发春老师具体是哪五篇文章。

  wfc = data[data["authors_fir"] == "万发春"]["title"]

wfc = pd.DataFrame(np.array(wfc), columns=["Title"], index=[1,2,3,4,5])

wfc

  

  3. 词云展示

  在这里,我们直接使用关键词进行云词展示,因为,摘要不够完整,且这样也避开了动词处理。

  docs = list(data["key_words"].map(lambda x: x.split(",")))

from juba import Similar

S = Similar(docs)

# 词汇表

S.vocabularyList

# 前100个词汇量

tags = S.vocabulary

sort_tage = sorted(tags.items(), key=lambda x: x[1], reverse=True)

sort_tage[:100]

# 打印出词汇和该词汇的出现次数

for v, n in sort_tage[:100]:

print (v + '\t' + str(int(n)))

  然后,将结果导出中,如下图:

  

  然后,设置字体和背景图片,注意一点是:中文须要自己加载字体,我使用的微软雅黑字体(网上可以下载)。

  

  最后产生的词云:

  

  到此,第三部份完成,下面我们进行文章相似度剖析。

  4. 文章相似度剖析

  考虑到本次爬取的并没有完整的文章且摘要不全的情况,所以只是采用关键词进行剖析,因此可能不准,主要介绍方式。但是,后面我将选择一个文本数据集再进行完整的文本相像度剖析。

  (1)使用juba进行剖析。

  juba最长使用余弦相似度cosine_sim(self, dtm=none)函数估算文档相似度,都是用于估算第一个文档与其他的文档之间的相似度,其中有dtm有三种参数选择,分别为:“tfidf_dtm”(词频逆文档频率模式)、“prob_dtm”(概率模式)、“tf_dtm”(词频模式)。

  sim = S.cosine_sim(dtm="prob_dtm")

sim.insert(0, 1)

data["similar"] = sim

data

  

  然后,我从高到低排列

  data.sort_values(by="similar", ascending=False)

  

  可以看出文章相似度都太低,这也符合文章发表的规律。

  (2)使用graphlab估算相似度

  这里,我使用另外一个数据集,它是爬取维基百科上好多名人的介绍的一个文本数据集。

  import graphlab

people = graphlab.SFrame.read_csv("people_wiki.csv")

# 去掉索引列

del people["X1"]

people.head()

  

  我们来看一共有多少位名人

  len(people.unique())

  59071位

  我们从中选购一位名人——奥巴马来瞧瞧。

  obama = people[people["name"] == "Barack Obama"]

obama

  

  # 查看奥巴马的具体介绍内容

obama["text"]

  接下来进行词频统计。

  obama["word_count"] = graphlab.text_analytics.count_words(obama["text"])

obama_word_count_table = obama[["word_count"]].stack("word_count", new_column_name=["word", "count"])

obama_word_count_table.sort("count", ascending=False)

  

  很显然,“the”、“in”、“and”等停用词的频度最大,但是,这并不是我们想要关注的词组或则说并不是全篇文章的主旨。所以,要使用tfidf进行统计词频。

  people["word_count"] = graphlab.text_analytics.count_words(people["text"])

tfidf = graphlab.text_analytics.tf_idf(people["word_count"])

people["tfidf"] = tfidf

people.head()

  

  然后,我们再来看奥巴马的介绍词频。

  obama[["tfidf"]].stack("tfidf", new_column_name = ["word", "tfidf"]).sort("tfidf", ascending=False)

  

  这样就正常了,直接通过词频就可以看出介绍谁的。

  构建knn模型,计算相似度距离。

  knn_model = graphlab.nearest_nei*敏*感*词*ors.create(people, features=["tfidf"], label= 'name')

  然后查看与奥巴马相仿的名人。

  knn_model.query(obama)

  

  这些人大多都是日本的首相或相仿的人正是与奥巴马相仿,所以,也否认了模型的准确性。至此,整个剖析结束,但是也都会存在不少问题,再接再厉吧!

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线