一、环境搭建
首先下载安装selenium包,推荐直接使用pip
之后还要下载对应浏览器的驱动(driver),这里使用的是chrome浏览器,注意驱动与浏览器的版本要相对应。下载的驱动直接复制到python和chrome的安装目录下。
python+selenium的环境搭建教程很多,这里不做赘述。
二、观察一下
我们以:
http://ieeexplore.ieee.org/search/searchresult.jsp?reload=true&queryText=SLAM&ranges=2014_2018_Year&sortType=desc_p_Citation_Count
为目标页面,爬取其中pdf。
手动下载的话,点击pdf图标就可以进入下载页面。
我们先随便进入一个下载页面,暗中观察:
发现这个url后面有一串奇怪的参数,这个参数值和文件名一样。再回到上一页,右键→检查元素:
发现这篇文章的pdf的图标是一个a标签,标签里也有这串数字。
再观察一下其他论文,发现它们的下载页面url只是替换了这个参数,而url的前半部分都是相同的:
'http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber='
也就是说,这个参数是论文的一个编号,只要拿到每篇论文的这个编号,再和以上字符串拼接,就能得到下载页面的url。
而每篇论文的pdf图标a标签里,就有一个data-artnum属性,属性值就是我们要的论文编号。
那么只要爬出每个data-artnum的值就行了
三、ok,开始爬虫
我们在编辑器里写下这样的代码:
from selenium import webdriver
import time
browser = webdriver.Chrome() # 驱动chrome
url = "http://ieeexplore.ieee.org/search/searchresult.jsp?queryText=SLAM&ranges=2014_2018_Year&sortType=desc_p_Citation_Count"
browser.get(url) # 跳转到目标页面
time.sleep(5) #象征性地等一下
link_list = browser.find_elements_by_xpath("//*[@data-artnum]") #使用selenium的xpath定位到每个具有data-artnum元素
for link in link_list:
ele_num = link.get_attribute('data-artnum')
print(ele_num)
运行一下,chrome自动启动并跳转到了ieeexplore,控制台也打出了一堆数字:
诶,不对啊,怎么每个编号都重复了一次,而且只爬出了10个编号,第一页明明有25篇论文呢。
再去目标页面检查一下元素:
原来pdf图标前面还有个html图标,里面也有data-artnum属性,爬虫的时候把html图标的这个属性也给爬进来了。
这个问题解决起来很容易,我们只要在结果中做一下去重,或者加一些其他的判别条件,不爬html图标就好了。
那什么会少15篇论文呢?
观察了一下滚动条,终于发现
滚动条向下滚动的时候,变短了!
这就说明,后面的论文是一边滚动一边动态加载的,也就是采用的“懒加载”的方式。
我们用selenium打开页面后,没有去做拖动滚动条的操作,后面的论文当然不会加载啦。
要解决这个问题,也很简单,我们可以在页面打开后,在time.sleep的时间里手动把滚动条滚动到底。
当然不推荐这么做,要是我爬60页,每刷新一次页面,都要自己滚来滚去的,那也太傻了。我们还是用selenium执行js操作来控制滚动条。
在代码中加上这么几句(加在打开页面后和使用xpath定位之前):
time.sleep(5)
js = 'window.scrollTo(0, document.body.scrollHeight);'
browser.execute_script(js)
time.sleep(5)
browser.execute_script(js)
这里要执行两次滚动操作,第一次滚动加载出20条内容,第二次滚动加载出全部25条。
这次就没问题了,已经能跑出第一页的所有论文的编号了。
有了编号还要下载,于是稍微修改一下代码,变成这样:
# -*- coding: utf-8 -*-
import requests
from bs4 import BeautifulSoup
from selenium import webdriver
import time
# 执行函数
def work(browser,url):
browser.get(url)
ele_nums = []
time.sleep(10)
js = 'window.scrollTo(0, document.body.scrollHeight);'
browser.execute_script(js)
time.sleep(5)
browser.execute_script(js)
try:
for link in browser.find_elements_by_xpath("//*[@data-artnum]"):
if isContainClass(link.get_attribute('className'),'icon-pdf'):
ele_num = link.get_attribute('data-artnum')
ele_nums.append(ele_num)
return ele_nums
except:
print("failure")
#用于判断某元素是否具有某class
def isContainClass(allClass,targetClass):
#解析allClass,判断是否包含targetClass
classArr = allClass.split(' ')
result = False
for str in classArr:
if str == targetClass:
result = True
break
return result
def getHtml(url):
# Mozilla/5.0 (Windows NT 10.0; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0'}
try:
response = requests.get(url,timeout=40,headers=headers)
response.raise_for_status()
response.encoding = response.apparent_encoding
return response.text
except:
import traceback
traceback.print_exc()
def getSoup(html):
soup = BeautifulSoup(html,'html.parser')
print(soup.body.find_all('a',attrs={'class':r'icon-pdf'}))
def downloadPaper(url):
try:
soup = BeautifulSoup(getHtml(url), 'html.parser')
result = soup.body.find_all('iframe')
downloadUrl = result[-1].attrs['src'].split('?')[0]
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:57.0) Gecko/20100101 Firefox/57.0'}
response = requests.get(downloadUrl, timeout=80, headers=headers)
fname = downloadUrl[-12:]
print(fname)
with open(fname,'ab+') as f:
print('start download file ',fname)
f.write(response.content)
except:
import traceback
with open('errorLog','ab+') as f:
traceback.print_exc(file=f)
if __name__ == '__main__':
url = 'http://ieeexplore.ieee.org/search/searchresult.jsp'+\
'?queryText=SLAM&ranges=2014_2018_Year&sortType=desc_p_Citation_Count'
baseUrl = 'http://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber='
maxPageNumber = 3
browser = webdriver.Chrome()
if maxPageNumber >= 1:
eleNums = work(browser,url)
eleNums = list(set(eleNums))
for eleNum in eleNums:
newUrl = baseUrl+str(eleNum)
downloadPaper(newUrl)
else:
for i in range(2,maxPageNumber+1):
url = url+'&pageNumber='+str(i)
eleNums = work(browser,url)
eleNums = list(set(eleNums))
for eleNum in eleNums:
newUrl = baseUrl + str(eleNum)
downloadPaper(newUrl)
就可以爬取指定页数的论文编号,并下载了!