0


Python爬虫之基于 selenium 实现文献信息获取

目录

最近有小伙伴后台跟我说,临近毕业,写毕业论文需要上知网查找大量的文献,但是一篇一篇看文献信息以及文献摘要又有点麻烦,能不能让我写一个爬虫去批量获取文献相关信息

我一听好家伙,当初我写毕业论文的时候也是饱经查阅文献的折磨,深知那种痛苦

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

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

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

初识 selenium

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

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

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

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

声明浏览器对象

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

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

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

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

  1. from selenium import webdriver
  2. browser = webdriver.Chrome()
  3. browser = webdriver.Firefox()
  4. browser = webdriver.Edge()
  5. browser = webdriver.Safari()

访问页面

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

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

查找元素

  1. #根据 id 查找
  2. find_element_by_id()#根据 name 查找
  3. find_element_by_name()#根据 class name 查找
  4. find_element_by_class_name()#根据 Tag name 查找
  5. find_element_by_tag_name()#根据 完整超链接 查找
  6. find_element_by_link_text()#根据 部分超链接 查找
  7. find_element_by_partial_link_text()#根据 xpath 查找
  8. find_element_by_xpath()#根据 css选择器 查找
  9. find_element_by_css_selector()

PS:

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

等待页面加载完成

有显式等待和隐式等待

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

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

常用浏览器操作

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

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

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

网页分析

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

按照这位同学的需求,需要进入到知网官网之后——>点击高级搜索图标
在这里插入图片描述
然后在文献来源输入框处输入相关内容,然后点击检索图标
在这里插入图片描述
在这里插入图片描述
因此可以得到如下步骤:

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

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

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

接着我们发现结果页(以搜索管理世界为例)在这里插入图片描述
共找到 8550 条结果,300页,每一页包含 20 条文献条目

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

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

  1. #题名/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 这些元素

在这里插入图片描述
在这里插入图片描述
完成以上知网页面的分析后,我们就可以根据需求开始写代码了!

代码实现

导入所需包

  1. import time
  2. from selenium import webdriver
  3. from selenium.webdriver.support.ui import WebDriverWait
  4. from selenium.webdriver.support import expected_conditions as EC
  5. from selenium.webdriver.common.by import By
  6. from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

创建浏览器对象

这里我用的是 Edge 浏览器

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

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

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

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

  1. # 打开页面
  2. driver.get("https://kns.cnki.net/kns8/AdvSearch")
  3. time.sleep(2)# 传入关键字
  4. WebDriverWait(driver,100).until(
  5. EC.presence_of_element_located((By.XPATH,'''//*[@id="gradetxt"]/dd[3]/div[2]/input'''))).send_keys(theme)
  6. time.sleep(2)# 点击搜索
  7. WebDriverWait(driver,100).until(
  8. 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()
  9. time.sleep(3)# 点击切换中文文献
  10. WebDriverWait(driver,100).until(
  11. EC.presence_of_element_located((By.XPATH,"/html/body/div[3]/div[1]/div/div/div/a[1]"))).click()
  12. time.sleep(3)

获取总文献数和页数

  1. 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
  2. # 去除千分位的逗号
  3. res_unm =int(res_unm.replace(",",''))
  4. page_unm =int(res_unm /20)+1print(f"共找到 {res_unm} 条结果, {page_unm} 页。")

对结果页进行解析

  1. defcrawl(driver, papers_need, theme):# 赋值序号, 控制爬取的文章数量
  2. count =1# 当爬取数量小于需求时,循环网页页码while count <= papers_need:# 等待加载完全,休眠3S
  3. time.sleep(3)
  4. title_list = WebDriverWait(driver,10).until(EC.presence_of_all_elements_located((By.CLASS_NAME,"fz14")))# 循环网页一页中的条目for i inrange(len(title_list)):try:if count %20!=0:
  5. term = count %20# 本页的第几个条目else:
  6. term =20
  7. title_xpath =f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[2]"
  8. author_xpath =f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[3]"
  9. source_xpath =f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[4]"
  10. date_xpath =f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[5]"
  11. database_xpath =f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[6]"
  12. title = WebDriverWait(driver,10).until(EC.presence_of_element_located((By.XPATH, title_xpath))).text
  13. authors = WebDriverWait(driver,10).until(EC.presence_of_element_located((By.XPATH, author_xpath))).text
  14. source = WebDriverWait(driver,10).until(EC.presence_of_element_located((By.XPATH, source_xpath))).text
  15. date = WebDriverWait(driver,10).until(EC.presence_of_element_located((By.XPATH, date_xpath))).text
  16. database = WebDriverWait(driver,10).until(
  17. EC.presence_of_element_located((By.XPATH, database_xpath))).text
  18. # 点击条目
  19. title_list[i].click()# 获取driver的句柄
  20. n = driver.window_handles
  21. # driver切换至最新生产的页面
  22. driver.switch_to.window(n[-1])
  23. time.sleep(3)# 开始获取页面信息
  24. 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
  25. 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
  26. 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
  27. abstract = WebDriverWait(driver,10).until(
  28. EC.presence_of_element_located((By.CLASS_NAME,"abstract-text"))).text
  29. try:
  30. keywords = WebDriverWait(driver,10).until(
  31. EC.presence_of_element_located((By.CLASS_NAME,"keywords"))).text[:-1]
  32. cssci = WebDriverWait(driver,10).until(
  33. EC.presence_of_element_located((By.XPATH,"/html/body/div[2]/div[1]/div[3]/div/div/div[1]/div[1]/a[2]"))).text
  34. except:
  35. keywords ='无'
  36. cssci ='NULL'
  37. url = driver.current_url
  38. # 写入文件
  39. 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)withopen(f'{theme}.tsv','a', encoding='gbk')as f:
  40. f.write(res)except:print(f" 第{count} 条爬取失败\n")# 跳过本条,接着下一个continuefinally:# 如果有多个窗口,关闭第二个窗口, 切换回主页
  41. n2 = driver.window_handles
  42. iflen(n2)>1:
  43. driver.close()
  44. driver.switch_to.window(n2[0])# 计数,判断篇数是否超出限制
  45. count +=1if count == papers_need:breakelse:# 切换到下一页
  46. WebDriverWait(driver,10).until(EC.presence_of_element_located((By.XPATH,"//a[@id='PageNext']"))).click()

结果展示:在这里插入图片描述
结果是一个以制表符分隔的表格文件(用 excel 打开),其中包含了论文的基本信息,包括:题目、作者、是否 CSSCI、来源、摘要

完整代码如下:

  1. import time
  2. from selenium import webdriver
  3. from selenium.webdriver.support.ui import WebDriverWait
  4. from selenium.webdriver.support import expected_conditions as EC
  5. from selenium.webdriver.common.by import By
  6. from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
  7. defopen_page(driver, theme):# 打开页面
  8. driver.get("https://kns.cnki.net/kns8/AdvSearch")
  9. time.sleep(2)# 传入关键字
  10. WebDriverWait(driver,100).until(
  11. EC.presence_of_element_located((By.XPATH,'''//*[@id="gradetxt"]/dd[3]/div[2]/input'''))).send_keys(theme)
  12. time.sleep(2)# 点击搜索
  13. WebDriverWait(driver,100).until(
  14. 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()
  15. time.sleep(3)# 点击切换中文文献
  16. WebDriverWait(driver,100).until(
  17. EC.presence_of_element_located((By.XPATH,"/html/body/div[3]/div[1]/div/div/div/a[1]"))).click()
  18. time.sleep(3)# 获取总文献数和页数
  19. 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
  20. # 去除千分位里的逗号
  21. res_unm =int(res_unm.replace(",",''))
  22. page_unm =int(res_unm /20)+1print(f"共找到 {res_unm} 条结果, {page_unm} 页。")return res_unm
  23. defcrawl(driver, papers_need, theme):# 赋值序号, 控制爬取的文章数量
  24. count =1# 当爬取数量小于需求时,循环网页页码while count <= papers_need:# 等待加载完全,休眠3S
  25. time.sleep(3)
  26. title_list = WebDriverWait(driver,10).until(EC.presence_of_all_elements_located((By.CLASS_NAME,"fz14")))# 循环网页一页中的条目for i inrange(len(title_list)):try:if count %20!=0:
  27. term = count %20# 本页的第几个条目else:
  28. term =20
  29. title_xpath =f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[2]"
  30. author_xpath =f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[3]"
  31. source_xpath =f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[4]"
  32. date_xpath =f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[5]"
  33. database_xpath =f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[6]"
  34. title = WebDriverWait(driver,10).until(EC.presence_of_element_located((By.XPATH, title_xpath))).text
  35. authors = WebDriverWait(driver,10).until(EC.presence_of_element_located((By.XPATH, author_xpath))).text
  36. source = WebDriverWait(driver,10).until(EC.presence_of_element_located((By.XPATH, source_xpath))).text
  37. date = WebDriverWait(driver,10).until(EC.presence_of_element_located((By.XPATH, date_xpath))).text
  38. database = WebDriverWait(driver,10).until(
  39. EC.presence_of_element_located((By.XPATH, database_xpath))).text
  40. # 点击条目
  41. title_list[i].click()# 获取driver的句柄
  42. n = driver.window_handles
  43. # driver切换至最新生产的页面
  44. driver.switch_to.window(n[-1])
  45. time.sleep(3)# 开始获取页面信息
  46. 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
  47. 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
  48. 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
  49. abstract = WebDriverWait(driver,10).until(
  50. EC.presence_of_element_located((By.CLASS_NAME,"abstract-text"))).text
  51. try:
  52. keywords = WebDriverWait(driver,10).until(
  53. EC.presence_of_element_located((By.CLASS_NAME,"keywords"))).text[:-1]
  54. cssci = WebDriverWait(driver,10).until(
  55. EC.presence_of_element_located((By.XPATH,"/html/body/div[2]/div[1]/div[3]/div/div/div[1]/div[1]/a[2]"))).text
  56. except:
  57. keywords ='无'
  58. cssci ='NULL'
  59. url = driver.current_url
  60. # 写入文件
  61. 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)withopen(f'{theme}.tsv','a', encoding='gbk')as f:
  62. f.write(res)except:print(f" 第{count} 条爬取失败\n")# 跳过本条,接着下一个continuefinally:# 如果有多个窗口,关闭第二个窗口, 切换回主页
  63. n2 = driver.window_handles
  64. iflen(n2)>1:
  65. driver.close()
  66. driver.switch_to.window(n2[0])# 计数,判断篇数是否超出限制
  67. count +=1if count == papers_need:breakelse:# 切换到下一页
  68. WebDriverWait(driver,10).until(EC.presence_of_element_located((By.XPATH,"//a[@id='PageNext']"))).click()defwebserver(theme):# get直接返回,不再等待界面加载完成
  69. desired_capabilities = DesiredCapabilities.EDGE
  70. desired_capabilities["pageLoadStrategy"]="none"# 设置 Edge 驱动器的环境
  71. options = webdriver.EdgeOptions()# 设置 Edge 不加载图片,提高速度
  72. options.add_experimental_option("prefs",{"profile.managed_default_content_settings.images":2})# 创建一个 Edge 驱动器
  73. driver = webdriver.Edge(options=options)# 设置所需篇数
  74. papers_need =50
  75. res_unm =int(open_page(driver, theme))# 判断所需是否大于总篇数
  76. papers_need = papers_need if(papers_need <= res_unm)else res_unm
  77. return driver, papers_need, theme
  78. if __name__ =="__main__":# 输入需要搜索的内容
  79. theme =input("请输入你要搜索的期刊名称:")# theme = "管理科学学报"
  80. driver, papers_need, theme = webserver(theme)
  81. crawl(driver, papers_need, theme)# 关闭浏览器
  82. driver.close()

踩过的坑

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

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

  • 第一步:设置 get 直接返回,不需要等待页面加载完成
  1. desired_capabilities = DesiredCapabilities.EDGE
  2. desired_capabilities["pageLoadStrategy"] = "none"
  • 第二步:

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

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

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

后面加了层判断:

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

本文转载自: https://blog.csdn.net/s_alted/article/details/128711740
版权归原作者 咸鱼爱搞机 所有, 如有侵权,请联系我们删除。

“Python爬虫之基于 selenium 实现文献信息获取”的评论:

还没有评论