ITPub博客

首页 > 数据库 > 数据库开发技术 > Python爬虫实战(5) | 爬取知网文献信息

Python爬虫实战(5) | 爬取知网文献信息

原创 数据库开发技术 作者:咸鱼运维杂谈 时间:2023-01-16 19:40:00 0 删除 编辑

最近有小伙伴后台跟我说,临近毕业,写毕业论文需要上知网查找大量的文献


但是一篇一篇看文献信息以及文献摘要又有点麻烦,能不能让我写一个爬虫去批量获取文献相关信息


我一听好家伙,这位小伙伴是懂偷懒的,想起当初我写毕业论文的时候也是饱经查阅文献的折磨,深知那种滋味


但是知网作为国内知名的文献数据库之一,有着极其复杂的反爬虫机制,例如动态JS、iframe、验证码等等,不是说想爬就能爬的


像我之前采用 requests 模拟请求的方法来爬取的话难度很大,一个不小心就有可能被封 IP


本篇文章就主要介绍该如何使用 Selenium来巧爬知网




初识 selenium


selenium 是一个自动化测试工具,可以用来进行 web 自动化测试


selenium 本质是通过驱动浏览器,完全模拟浏览器的操作,比如跳转、输入、点击、下拉等,来拿到网页渲染之后的结果,可支持多种浏览器


爬虫中用到 selenium 主要是为了解决 requests 无法直接执行 JavaScript 代码等问题


下面就来介绍下 selenium 基础用法


声明浏览器对象


Selenium 支持非常多的浏览器,如Chrome、Firefox、Edge 等


我们只要首先下载好相应浏览器的驱动(webdriver)到python主目录中,或者加入环境变量即可


下面是常见浏览器驱动的下载地址

#Firefox浏览器驱动:https://link.zhihu.com/?target=https%3A//github.com/mozilla/geckodriver/releases
#Chrome浏览器驱动:https://registry.npmmirror.com/binary.html?path=chromedriver/
#IE浏览器驱动:IEDriverServerhttps://link.zhihu.com/?target=http%3A//selenium-release.storage.googleapis.com/index.html
#Edge浏览器驱动:MicrosoftWebDriverhttps://link.zhihu.com/?target=https%3A//developer.microsoft.com/en-us/microsoft-edge/tools/webdriver

下载好驱动之后就可以浏览器初始化了

from selenium import webdriver
browser = webdriver.Chrome()browser = webdriver.Firefox()browser = webdriver.Edge()browser = webdriver.Safari()


访问页面


我们可以用 get() 方法来请求一个网页,传入参数链接URL

browser.get('https://blog.csdn.net/s_alted')

查找元素

#根据 id 查找find_element_by_id()
#根据 name 查找find_element_by_name()
#根据 class name 查找find_element_by_class_name()
#根据 Tag name 查找find_element_by_tag_name()
#根据 完整超链接 查找find_element_by_link_text()
#根据 部分超链接 查找find_element_by_partial_link_text()
#根据 xpath 查找find_element_by_xpath()
#根据 css选择器 查找find_element_by_css_selector()


PS:

上面的 element 变成 elements(例如find_elements_by_id)就是找到所有满足的条件,然后返回数据


等待页面加载完成


有显式等待和隐式等待

显式等待使 WebdDriver 等待某个条件成立时继续执行,否则在达到最大时长时抛出超时异常(TimeoutException)

WebDriverWait 类是由 WebDirver 提供的等待方法。在设置时间内,默认每隔一段时间检测一次当前页面元素是否存在,如果超过设置时间检测不到则抛出异常


常用浏览器操作


在找到浏览器相应元素的位置之后,我们就需要进行一些交互动作,例如双击、点击、输入、获取网页源码等等


element = find_element_by_id(ID,id)
element.send_keys(Keys.CONTROL,'c') # 复制
element.send_keys("hello") #传入 hello
element.clear() # 清除输入框
element.click() # 单击元素
element.text # 获取元素文本信息
element.get_attribute('href') # 获取元素属性

有了上面这些基本用法,就可以开始编写代码程序了!




网页分析


知网官网:https://www.cnki.net/


按照这位同学的需求,需要进入到知网官网之后——>点击高级搜索图标



然后在文献来源输入框处输入相关内容,然后点击检索图标





因此可以得到如下步骤:

进入官网点击高级搜索——>在文献来源处输入信息——>点击检索


通过 F12 检查浏览器页面,得到高级搜索图标和输入框以及检索图标元素的 xpath 分别如下:







#高级搜索 xpath/html/body/div[2]/div[2]/div/div[2]/a[1]
#输入框 xpath/html/body/div[2]/div/div[2]/div/div[1]/div[1]/div[2]/dl/dd[3]/div[2]/input
#检索 xpath/html/body/div[2]/div/div[2]/div/div[1]/div[1]/div[2]/div[2]/input


接着我们发现结果页(以搜索管理世界为例)


共找到 8550 条结果,300页,每一页包含 20 条文献条目


每个条目包含题目、作者、来源等信息


通过对当前页面分析,发现每条文献条目的 xpath 是有规律的


#题名/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[1]/td[2]
#作者/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[1]/td[3]

tr[1] 表示本页第一条条目,而 td[2] 中的2-6 分别代表作者、来源、发表时间和数据库


我们在当前页面是无法获取到文献的摘要、关键字等信息,需要进一步点击进入相关文献条目


进入到相关文献页面之后,根据 class name来获取摘要、关键字、是否为CSSCI 这些元素




完成以上知网页面的分析后,我们就可以根据需求开始写代码了!




代码实现


导入所需包

import time from selenium import webdriverfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.common.desired_capabilities import DesiredCapabilities

创建浏览器对象

这里我用的是 Edge 浏览器

# get直接返回,不再等待界面加载完成desired_capabilities = DesiredCapabilities.EDGEdesired_capabilities["pageLoadStrategy"] = "none"
# 设置 Edge 驱动器的环境options = webdriver.EdgeOptions()# 设置 Edge 不加载图片,提高速度options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2})
# 创建一个 Edge 驱动器driver = webdriver.Edge(options=options)

传入 url 参数然后模拟对浏览器进行人为操作

适当的加入 time.sleep() 方法,等待页面加载完成


不然页面还没完全加载就执行下一步操作的话会报错


# 打开页面driver.get("https://kns.cnki.net/kns8/AdvSearch")time.sleep(2)    # 传入关键字WebDriverWait(driver, 100).until(        EC.presence_of_element_located((By.XPATH, '''//*[@id="gradetxt"]/dd[3]/div[2]/input'''))).send_keys(theme)time.sleep(2)    # 点击搜索WebDriverWait(driver, 100).until(        EC.presence_of_element_located((By.XPATH, "/html/body/div[2]/div/div[2]/div/div[1]/div[1]/div[2]/div[2]/input"))).click()time.sleep(3)
# 点击切换中文文献WebDriverWait(driver, 100).until( EC.presence_of_element_located((By.XPATH, "/html/body/div[3]/div[1]/div/div/div/a[1]"))).click()time.sleep(3)


获取总文献数和页数
res_unm = WebDriverWait(driver, 100).until(EC.presence_of_element_located(        (By.XPATH, "/html/body/div[3]/div[2]/div[2]/div[2]/form/div/div[1]/div[1]/span[1]/em"))).text
# 去除千分位的逗号res_unm = int(res_unm.replace(",", '')) page_unm = int(res_unm / 20) + 1print(f"共找到 {res_unm} 条结果, {page_unm} 页。")


对结果页进行解析
def crawl(driver, papers_need, theme):    # 赋值序号, 控制爬取的文章数量    count = 1
# 当爬取数量小于需求时,循环网页页码 while count <= papers_need: # 等待加载完全,休眠3S time.sleep(3)
title_list = WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located((By.CLASS_NAME, "fz14"))) # 循环网页一页中的条目 for i in range(len(title_list)): try: if count % 20 != 0: term = count % 20 # 本页的第几个条目 else: term = 20 title_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[2]" author_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[3]" source_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[4]" date_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[5]" database_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[6]" title = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, title_xpath))).text authors = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, author_xpath))).text source = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, source_xpath))).text date = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, date_xpath))).text database = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.XPATH, database_xpath))).text # 点击条目 title_list[i].click() # 获取driver的句柄 n = driver.window_handles # driver切换至最新生产的页面 driver.switch_to.window(n[-1]) time.sleep(3) # 开始获取页面信息 title = WebDriverWait(driver, 10).until(EC.presence_of_element_located( (By.XPATH, "/html/body/div[2]/div[1]/div[3]/div/div/div[3]/div/h1"))).text authors = WebDriverWait(driver, 10).until(EC.presence_of_element_located( (By.XPATH, "/html/body/div[2]/div[1]/div[3]/div/div/div[3]/div/h3[1]"))).text institute = WebDriverWait(driver, 10).until(EC.presence_of_element_located( (By.XPATH, "/html/body/div[2]/div[1]/div[3]/div/div/div[3]/div/h3[2]"))).text abstract = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CLASS_NAME, "abstract-text"))).text try: keywords = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CLASS_NAME, "keywords"))).text[:-1] cssci = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.XPATH, "/html/body/div[2]/div[1]/div[3]/div/div/div[1]/div[1]/a[2]"))).text except: keywords = '无' cssci = 'NULL' url = driver.current_url
# 写入文件 res = f"{count}\t{title}\t{authors}\t{cssci}\t{institute}\t{date}\t{source}\t{database}\t{keywords}\t{abstract}\t{url}".replace( "\n", "") + "\n" print(res) with open(f'{theme}.tsv', 'a', encoding='gbk') as f: f.write(res) except: print(f" 第{count} 条爬取失败\n") # 跳过本条,接着下一个 continue finally: # 如果有多个窗口,关闭第二个窗口, 切换回主页 n2 = driver.window_handles if len(n2) > 1: driver.close() driver.switch_to.window(n2[0]) # 计数,判断篇数是否超出限制 count += 1 if count == papers_need: break else: # 切换到下一页 WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//a[@id='PageNext']"))).click()


结果展示:



结果是一个以制表符分隔的表格文件(用 excel 打开),其中包含了论文的基本信息,包括:题目、作者、是否 CSSCI、来源、摘要等


完整代码如下:

import timefrom selenium import webdriverfrom selenium.webdriver.support.ui import WebDriverWaitfrom selenium.webdriver.support import expected_conditions as ECfrom selenium.webdriver.common.by import Byfrom selenium.webdriver.common.desired_capabilities import DesiredCapabilities

def open_page(driver, theme): # 打开页面 driver.get("https://kns.cnki.net/kns8/AdvSearch") time.sleep(2) # 传入关键字 WebDriverWait(driver, 100).until( EC.presence_of_element_located((By.XPATH, '''//*[@id="gradetxt"]/dd[3]/div[2]/input'''))).send_keys(theme) time.sleep(2) # 点击搜索 WebDriverWait(driver, 100).until( EC.presence_of_element_located((By.XPATH, "/html/body/div[2]/div/div[2]/div/div[1]/div[1]/div[2]/div[2]/input"))).click() time.sleep(3)
# 点击切换中文文献 WebDriverWait(driver, 100).until( EC.presence_of_element_located((By.XPATH, "/html/body/div[3]/div[1]/div/div/div/a[1]"))).click() time.sleep(3)

# 获取总文献数和页数 res_unm = WebDriverWait(driver, 100).until(EC.presence_of_element_located( (By.XPATH, "/html/body/div[3]/div[2]/div[2]/div[2]/form/div/div[1]/div[1]/span[1]/em"))).text
# 去除千分位里的逗号 res_unm = int(res_unm.replace(",", '')) page_unm = int(res_unm / 20) + 1 print(f"共找到 {res_unm} 条结果, {page_unm} 页。") return res_unm

def crawl(driver, papers_need, theme): # 赋值序号, 控制爬取的文章数量 count = 1
# 当爬取数量小于需求时,循环网页页码 while count <= papers_need: # 等待加载完全,休眠3S time.sleep(3)
title_list = WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located((By.CLASS_NAME, "fz14"))) # 循环网页一页中的条目 for i in range(len(title_list)): try: if count % 20 != 0: term = count % 20 # 本页的第几个条目 else: term = 20 title_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[2]" author_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[3]" source_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[4]" date_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[5]" database_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[6]" title = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, title_xpath))).text authors = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, author_xpath))).text source = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, source_xpath))).text date = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, date_xpath))).text database = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.XPATH, database_xpath))).text # 点击条目 title_list[i].click() # 获取driver的句柄 n = driver.window_handles # driver切换至最新生产的页面 driver.switch_to.window(n[-1]) time.sleep(3) # 开始获取页面信息 title = WebDriverWait(driver, 10).until(EC.presence_of_element_located( (By.XPATH, "/html/body/div[2]/div[1]/div[3]/div/div/div[3]/div/h1"))).text authors = WebDriverWait(driver, 10).until(EC.presence_of_element_located( (By.XPATH, "/html/body/div[2]/div[1]/div[3]/div/div/div[3]/div/h3[1]"))).text institute = WebDriverWait(driver, 10).until(EC.presence_of_element_located( (By.XPATH, "/html/body/div[2]/div[1]/div[3]/div/div/div[3]/div/h3[2]"))).text abstract = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CLASS_NAME, "abstract-text"))).text try: keywords = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CLASS_NAME, "keywords"))).text[:-1] cssci = WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.XPATH, "/html/body/div[2]/div[1]/div[3]/div/div/div[1]/div[1]/a[2]"))).text except: keywords = '无' cssci = 'NULL' url = driver.current_url
# 写入文件 res = f"{count}\t{title}\t{authors}\t{cssci}\t{institute}\t{date}\t{source}\t{database}\t{keywords}\t{abstract}\t{url}".replace( "\n", "") + "\n" print(res) with open(f'{theme}.tsv', 'a', encoding='gbk') as f: f.write(res) except: print(f" 第{count} 条爬取失败\n") # 跳过本条,接着下一个 continue finally: # 如果有多个窗口,关闭第二个窗口, 切换回主页 n2 = driver.window_handles if len(n2) > 1: driver.close() driver.switch_to.window(n2[0]) # 计数,判断篇数是否超出限制 count += 1 if count == papers_need: break else: # 切换到下一页 WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//a[@id='PageNext']"))).click()

def webserver(theme):   # get直接返回,不再等待界面加载完成   desired_capabilities = DesiredCapabilities.EDGE desired_capabilities["pageLoadStrategy"] = "none" # 设置 Edge 驱动器的环境 options = webdriver.EdgeOptions() # 设置 Edge 不加载图片,提高速度 options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2}) # 创建一个 Edge 驱动器 driver = webdriver.Edge(options=options)   # 设置所需篇数   papers_need = 50   res_unm = int(open_page(driver, theme))   # 判断所需是否大于总篇数   papers_need = papers_need if (papers_need <= res_unm) else res_unm   return driver, papers_need, theme

if __name__ == "__main__": # 输入需要搜索的内容 theme = input("请输入你要搜索的期刊名称:") # theme = "管理科学学报" driver, papers_need, theme = webserver(theme) crawl(driver, papers_need, theme) # 关闭浏览器 driver.close()




踩过的坑


网页加载太慢导致元素查找出错

网络并不是可靠的,我在调试程序的时候往往出现网页加载过慢导致元素查找出错

  • 第一步:设置 get 直接返回,不需要等待页面加载完成

desired_capabilities = DesiredCapabilities.EDGEdesired_capabilities["pageLoadStrategy"] = "none"
  • 第二步:

在需要等待网页加载完全之后才能执行下一步骤的地方加上 time.sleep() 方法休眠几秒,既可以等待页面加载,也可以防止爬取太快被封IP


代码逻辑出错导致爬取不了 20 倍页数的第 20 条文献信息

刚开始写的时候,逻辑不够准确,导致第20页、40页、60页(20整数倍)的第20条文献爬取不了


后面加了层判断:

if count % 20 != 0:  term = count % 20  # 本页的第几个条目else:  term = 20 # 本页的第20个条目




感谢阅读,喜欢作者就动动小手[一键三连],这是我写作最大的动力!

原文作者:咸鱼运维杂谈;如有侵权,请联系管理员删除。

上一篇: 2023开工大吉
请登录后发表评论 登录
全部评论
最新的技术文章首先发布在微信公众号:咸鱼运维杂谈 擅长Linux系统维护、开源软件维护、自动化运维等领域,博客主要偏向于学习笔记及经历分享

注册时间:2021-05-29

  • 博文量
    193
  • 访问量
    84255