输入关键字 抓取所有网页(一个神器之一selenium五年发表文章五年数量)

优采云 发布时间: 2021-11-24 11:06

  输入关键字 抓取所有网页(一个神器之一selenium五年发表文章五年数量)

  PubMed 是一个提供生物医学论文检索和摘要的数据库,可以免费检索。它是生物学中经常使用的文档搜索网站。最近在学习爬虫,包括urllib库、requests库、xpath表达式、scrapy框架等,刚想爬PubMed,作为一个实践,准备爬取过去5年在PubMed上发表的文章文章的数量根据搜索到的关键词,以此为基础来看看这个研究方向近五年的热度。

  最初的想法是使用scrapy框架进行爬取和存储。于是打开PubMed,开始分析网页组成、源码等,发现NCBI使用的是动态网页,在进行翻页、关键字搜索等操作时,URL并没有发生变化。于是想到了爬虫神器之一的selenium模块,利用selenium模拟搜索、翻页等操作,然后分析源码,得到想要的信息。由于和selenium接触不多,所以暂时在网上搜索了一些功能。

  1、 加载需要的模块

  import urllib

import time

import matplotlib.pyplot as plt

import numpy as np

from lxml import etree

from selenium import webdriver

from selenium.webdriver.support import expected_conditions as EC

from selenium.webdriver.common.by import By

from selenium.webdriver.support.ui import WebDriverWait

from selenium.common.exceptions import TimeoutException

from collections import Counter

  2、使用关键字构造URL

  手动输入几个关键字来分析 URL 的结构。

  PubMed的原链接是``,当你输入搜索'cancer, TCGA, Breast'时,url变成''。所以不难分析,URL由两部分组成,一部分不变,另一部分由我们搜索的关键字串起来的字符串'%2C'组成。因此,对于传入的'keyword',我们可以做如下处理,拼接到搜索后返回的url中

  keyword = '%2C'.join(keyword)

tart_url = 'https://www.ncbi.nlm.nih.gov/pubmed/?term='

url = start_url + keyword

  这样我们就简单的拼接返回的url。

  3、创建浏览器对象

  下一步就是使用selenium模块打开我们的Chrome浏览器,跳转到url界面。这个操作相信大家都不陌生。因为后面会进行模拟点击翻页等操作,所以需要实例化一个WebDriverWait对象,方便后续调用

  browser = webdriver.Chrome()

self.browser.get(url)

wait = WebDriverWait(browser, 10)

  模拟点击网页

  打开网页后,我们需要模拟点击网页。首先,我们需要找到过去五年的文章,所以我们需要模拟点击左边的5years按钮,改变单页显示的文章的数量。变成200,可以减少我们的翻页操作

  

  QQ图片246.png

  我们要等到网页上加载了相应的元素才可以点击,所以我们需要用到前面提到的WebDriverWait对象。代码显示如下

  years =wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#_ds1 > li > ul > li:nth-child(1) > a')))

years.click()

perpage = self.wait.until(EC.element_to_be_clickable((By.XPATH,'//ul[@class="inline_list left display_settings"]/li[3]/a/span[4]')))

perpage.click()

page_200 = self.wait.until(EC.element_to_be_clickable(

(By.CSS_SELECTOR, '#display_settings_menu_ps > fieldset > ul > li:nth-child(6) > label')))

  此处使用了 CSS 选择器和 Xpath 表达式。您使用哪一种取决于您的个人喜好。您可以使用任何您觉得方便的方式。EC.element_to_be_clickable 中的输入是一个元组,第一个元素是声明使用哪个选择器,第二个元素是一个表达式。这个函数的意思是等到你在表达式中传入的元素出现在网页上之后,才会在selenium中出现时才会进行后续的操作。在使用selenium的过程中,有很多地方需要用到presence_of_element_located、text_to_be_present_in_element等类似的操作,有兴趣的可以上网查查。

  解析网页并提取信息

  下一步是解析网页。我使用 lxml 来解析网页并从 Xpath 中提取信息。代码如下

  html = self.browser.page_source

doc = etree.HTML(self.html)

self.art_timeanddoi = self.doc.xpath('//div[@class="rprt"]/div[2]/div/p[@class="details"]/text()')

for i in self.art_timeanddoi:

self.yearlist.append(i[2:6])

for i in self.yearlist:

if re.match('2', i):

continue

else:

self.yearlist.remove(i)

  这里主要是对网页中的年份进行Xpath提取和字符串切片处理,从而获取年份信息并保存到列表中。在实际操作中发现了少量其他不相关的元素。经过仔细排查,发现是网页源代码有问题。因此,遵循常规过程以删除不以“2”开头的元素。最终结果被检查是正确的。

  这里我们提取了单个页面的信息。

  翻页操作

  我们还需要翻页以获取我们想要的所有信息。硒模块仍在使用。

  status = True

def next_page():

try:

self.nextpage = self.wait.until(

EC.element_to_be_clickable((By.XPATH, '//*[@title="Next page of results"]')))

except TimeoutException:

status = False

while True:

if status:

next_page()

else:

break

  这里我首先定义了一个函数来判断是否可以翻页,使用异常捕获,因为翻页动作要停在最后一页,不能再次点击翻页元素。因此,在进行点击操作时应先判断。如果无法点击,会因为超时而抛出异常。next_page 函数中的状态变量记录了“下一页”按钮是否可以被点击。接下来是一个 while True 循环。只有 next_page 变为 False 时,翻页动作才会停止。在循环中,我们继续解析网页来提取信息,就可以得到我们想要的所有信息。

  可视化

  爬取了所有年份信息的列表后,我们还需要进一步的可视化处理来直观的判断目标话题的研究趋势,所以使用matplotlib进行可视化操作,简单的画一个折线图。代码显示如下:

   def plot_curve(yearlist):

counter = Counter(yearlist)

dic = dict(counter)

keys = sorted(list(dic.keys()))

curcount = 0

y = []

temp = [int(i) for i in keys]

for i in range(min(temp), max(temp)+1):

if str(i) in keys:

curcount += self.dic[str(i)]

y.append(self.curcount)

else:

y.append(self.curcount)

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

plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签

plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号

plt.xlabel('年份')

plt.ylabel('近五年文章数量')

plt.plot(np.arange(min(temp), max(temp)+1), np.array(y), 'r', marker='+', linewidth=2)

plt.show()

  最后贴出完整代码:

  import re

import urllib

import time

import numpy as np

from lxml import etree

from selenium import webdriver

from selenium.webdriver.support import expected_conditions as EC

from selenium.webdriver.common.by import By

from selenium.webdriver.support.ui import WebDriverWait

from selenium.common.exceptions import TimeoutException

from collections import Counter

import matplotlib.pyplot as plt

class NcbiInfo(object):

browser = webdriver.Chrome()

start_url = 'https://www.ncbi.nlm.nih.gov/pubmed/?term='

wait = WebDriverWait(browser, 10)

def __init__(self, keywordlist):

self.temp = [urllib.parse.quote(i) for i in keywordlist]

self.keyword = '%2C'.join(self.temp)

self.title = ' AND '.join(self.temp)

self.url = NcbiInfo.start_url + self.keyword

self.headers = {

'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'}

self.file = open('information.txt', 'w')

self.status = True

self.yearlist = []

def click_yearandabstract(self, ):

self.browser.get(self.url)

years = self.wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, '#_ds1 > li > ul > li:nth-child(1) > a')))

years.click()

perpage = self.wait.until(EC.element_to_be_clickable((By.XPATH, '//ul[@class="inline_list left display_settings"]/li[3]/a/span[4]')))

perpage.click()

page_200 = self.wait.until(EC.element_to_be_clickable(

(By.CSS_SELECTOR, '#display_settings_menu_ps > fieldset > ul > li:nth-child(6) > label')))

page_200.click()

def get_response(self):

self.html = self.browser.page_source

self.doc = etree.HTML(self.html)

def get_numof_article(self):

article_count_ = self.doc.xpath('//*[@id="maincontent"]/div/div[3]/div[1]/h3/text()')[0]

if 'of' not in article_count_:

print(article_count_.split(': ')[1])

else:

print(article_count_.split('of ')[1])

def get_info(self):

self.art_timeanddoi = self.doc.xpath('//div[@class="rprt"]/div[2]/div/p[@class="details"]/text()')

for i in self.art_timeanddoi:

self.yearlist.append(i[2:6])

for i in self.yearlist:

if re.match('2', i):

continue

else:

self.yearlist.remove(i)

def next_page(self):

try:

self.nextpage = self.wait.until(

EC.element_to_be_clickable((By.XPATH, '//*[@title="Next page of results"]')))

except TimeoutException:

self.status = False

def plot_curve(self):

self.counter = Counter(self.yearlist)

self.dic = dict(self.counter)

self.keys = sorted(list(self.dic.keys()))

self.curcount = 0

self.y = []

temp = [int(i) for i in self.keys]

for i in range(min(temp), max(temp)+1):

if str(i) in self.keys:

self.curcount += self.dic[str(i)]

self.y.append(self.curcount)

else:

self.y.append(self.curcount)

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

plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签

plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号

plt.xlabel('年份')

plt.ylabel('近五年文章数量')

plt.title(self.title)

plt.plot(np.arange(min(temp), max(temp)+1), np.array(self.y), 'r', marker='+', linewidth=2)

plt.show()

def main(self):

self.click_yearandabstract()

time.sleep(3)

self.get_response()

self.get_numof_article()

while True:

self.get_info()

self.next_page()

if self.status:

self.nextpage.click()

self.get_response()

else:

break

self.plot_curve()

if __name__ == '__main__':

a = NcbiInfo(['TCGA', 'breast', 'cancer'])

a.main()

  结果如下:

  

  图片.png

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线