前言
持续摸鱼的f1rry开始学习了爬虫,在学习了爬虫的基本知识后,准备实践一波,于是乎准备写一个爬虫爬取自己的成绩。在编写过程中发现Selenium是真滴好用,真滴强大,于是乎写下这篇,记录以下其常见用法。
简介
Selenium
Selenium 是什么?一句话,自动化测试工具。它支持各种浏览器,包括 Chrome,Safari,Firefox 等主流界面式浏览器,如果你在这些浏览器里面安装一个 Selenium 的插件,那么便可以方便地实现Web界面的测试。换句话说叫 Selenium 支持这些浏览器驱动。
Beautiful Soup
Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。
Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。你不需要考虑编码方式,除非文档没有指定一个编码方式,这时,Beautiful Soup就不能自动识别编码方式了。然后,你仅仅需要说明一下原始编码方式就可以了。
Beautiful Soup已成为和lxml、html6lib一样出色的python解释器,为用户灵活地提供不同的解析策略或强劲的速度。
那么在简单了解了他们是干嘛的后,我们就事不宜迟赶快爬一下吧
问题分析
获取到成绩页面需要许多步骤:
- 需要登录自己的账号
- 在登录以后,出现的页面只是个中转页面,还需要继续操作,才能到达成绩查询系统
- 在登录到成绩查询系统后,还需要通过获取悬浮菜单,点击相关按钮,才能查看成绩
- 成绩单是位于iframe框架内,需要定位到框架内才能爬取相关数据
在获取到源码后,就需要爬取有用数据了,这里我用的是Beautiful Soup,其实我只是想练练手,估计是我太菜了的缘故,感觉Beautiful Soup对我并不友好233
逐步解决:
登录自己的账号:
这个比较简单,其实可以直接post数据过去,不需要用Selenium,但出于强迫症,于是也用Selenium。对于Slenium,其实只要定位到username和password的输入框并模拟输入即可。
Selenium有多种定位元素的方法:
选取单个元素:
find_element_by_id //通过id属性来查找
find_element_by_name //通过name属性来查找
find_element_by_xpath //通过xpath路径来查找
find_element_by_link_text //通过文本链接中的文本来查找
find_element_by_partial_link_text //跟上面一个类似,只是选取文本片段
find_element_by_tag_name //根据标签名称查找
find_element_by_class_name //通过类名来查找
find_element_by_css_selector //css定位可以分为四类:id、class、其他属性、路径。
选取多个元素:
find_elements_by_name //同上
find_elements_by_xpath
find_elements_by_link_text
find_elements_by_partial_link_text
find_elements_by_tag_name
find_elements_by_class_name
find_elements_by_css_selecto
以下是相关代码片段:
driver.get(self.url)
elem = driver.find_element_by_id("username")
elem.send_keys(username)
elem = driver.find_element_by_id("password")
elem.send_keys(password)
elem.send_keys(Keys.ENTER)
这里的elem.send_keys(Keys.ENTER)模拟的是输入完后的回车,这样就省去了再定位登录按钮并执行点击操作的部分了
从中转页面转到成绩查询系统:
这里就需要用到Slenium,首先我们需要定位到“学生系统”这一按钮(这里我们运用xpath来定位),然后再执行点击操作,还是比较简单的,以下是相关代码片段:
time.sleep(1)
elem = driver.find_element_by_xpath('//*[@appid="1142"]')
elem.click()
PS:由于在查询的时候会偶尔出现定位不到元素的情况,所以这里使用time.sleep()来是页面能完全加载完毕,以便更好的查找元素
获取悬浮菜单并进入到查询功能
在这之前,需要认识到在进行完之前一部后,浏览器会新建一个窗口,并在其中加载,而我们此时仍会在原标签页中定位元素,所以如果我们不切换窗口,我们将无法定位到“成绩查询系统”页面中的任何元素。所以在做获取悬浮菜单之前,我们需要切换窗口。那么怎么来切换窗口呢?以下是相关代码:
#获取所有的窗口页面
handles = driver.window_handles
#切换到第二个窗口
driver.switch_to_window(handles[1])
那么在切换窗口后,我们就开始获取悬浮菜单的操作了,要获取悬浮菜单,我们需要模拟鼠标停留在某个菜单栏的操作,以便获取其中的“信息查询”这一栏,然后模拟点击操作附上相关代码:
#定位相应的菜单栏
elem = driver.find_element_by_xpath('//*[@id="headDiv"]/ul/li[6]')
#模拟悬浮操作
ActionChains(driver).move_to_element(elem).perform()
#运用xpath查询该菜单栏下的“信息查询”这一项
elem=driver.find_element_by_xpath('//[@id="headDiv"]/ul/li[6]/ul/li[4]/a')
elem.click()
PS:这里需要引入以下库:
from selenium.webdriver.common.action_chains import ActionChains
进入框架内部并执行相应操作
在执行完点击操作后,我们会看到相应的成绩查询页,但蛋疼的是,他在iframe框架内,只有进入该框架内,我们才能定位其中的元素,要进入框架内部,我们可以执行以下代码:
self.driver.switch_to.frame("iframeautoheight")
PS:这里可以传入id、name、index以及selenium的WebElement对象以完成该操作
而进入框架内部后,由于我们是通过XX学年来查询成绩的,所以我们需要先定位到有着各个学年的下拉菜单。然后再定位下拉菜单中的选项。
对于如何定位下拉菜单中的选项。我们可以使用如下相关代码:
#获取下拉菜单中的所有选项
select=Select(driver.find_element_by_id("ddlxn"))
#通过文字来选择相应选项
select.select_by_visible_text(studyYear)
#获取“查询按钮”元素
elem = driver.find_element_by_id("btnCx")
#模拟点击查询
elem.click()
PS:除了可以根据文字来选择相应选项,还可以根据值、索引来选择对于的函数是
select.select_by_index(index)
select.select_by_value(value)
PPS:需要引入以下库:
from selenium.webdriver.support.ui import Select
这样我们就抵达了该学年的成绩这一页了,我们只需用driver.page_source就可以返回该页的源代码了。
爬取有用数据
在获得了源代码后,我们就需要爬取有用数据–成绩了。
Beautiful Soup 提供了很多查询元素的方法,我们这里用到的方法是 soup.select(),返回类型是 list。
Beautiful Soup提供了两种查询子节点的属性,一个是soup.children,他返回的是所有子节点的list 生成器对象,一个是soup.contents,他返回的包含所有直接子节点的列表。
再记一点比较坑的地方:
soup.contents里面的元素类型是NavigableString,他不能直接用操作string类型的函数来操作,如果真想这样,必须先转换,代码如下:
content = ""
content = content.join(soup.contents[i])
下面获取有用数据
相关代码如下:
def getGrade(page):
soup = BeautifulSoup(page)
i=1
j=1
listheads = soup.select('table[class="datelist"]')[0].contents[1]
len1=len(listheads)-1
len2=len(listheads.contents[1])-1
gradeList=[]
while i<len1:
gradeList.append([])
while j<len2:
content = ""
content = content.join(listheads.contents[i].contents[j])
content = content.replace(u'\xa0', u' ')
j=j+1
gradeList[i-1].append(content.encode('utf-8'))
i=i+1
j=1
return gradeList
成型
代码如下:
# -*- coding:utf-8 -*-
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import Select
from bs4 import BeautifulSoup
import re
import time
class Grade:
def __init__(self,username,password,studyYear):
self.url='http://cas.hdu.edu.cn/cas/login?service=http%3A%2F%2Fi.hdu.edu.cn%2Fdcp%2Findex.jsp'
self.username=username
self.password=password
self.studyYear=studyYear
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--headless')
self.driver = webdriver.Chrome(chrome_options=chrome_options)
def getGradePage(self):
self.driver.get(self.url)
elem = self.driver.find_element_by_id("username")
elem.send_keys(self.username)
elem = self.driver.find_element_by_id("password")
elem.send_keys(self.password)
elem.send_keys(Keys.ENTER)
#使页面全部加载完成
time.sleep(1)
elem = self.driver.find_element_by_xpath('//*[@appid="1142"]')
elem.click()
#跳转到第二个窗口
handles = self.driver.window_handles
self.driver.switch_to_window(handles[1])
elem = self.driver.find_element_by_xpath('//*[@id="headDiv"]/ul/li[6]')
#使悬浮菜单显现
ActionChains(self.driver).move_to_element(elem).perform()
elem = self.driver.find_element_by_xpath('//*[@id="headDiv"]/ul/li[6]/ul/li[4]/a')
elem.click()
#跳转到框架内部
self.driver.switch_to.frame("iframeautoheight")
select=Select(self.driver.find_element_by_id("ddlxn"))
select.select_by_visible_text(self.studyYear)
elem = self.driver.find_element_by_id("btnCx")
elem.click()
return self.driver.page_source
def getGrade(self,page):
soup = BeautifulSoup(page)
i=1
j=1
listheads = soup.select('table[class="datelist"]')[0].contents[1]
len1=len(listheads)-1
len2=len(listheads.contents[1])-1
gradeList=[]
while i<len1:
gradeList.append([])
while j<len2:
content = ""
content = content.join(listheads.contents[i].contents[j])
content = content.replace(u'\xa0', u' ')
j=j+1
gradeList[i-1].append(content.encode('utf-8'))
i=i+1
j=1
return gradeList
def printGrade(self,gradeList):
len1=len(gradeList)
len2=len(gradeList[0])
i=j=0
print u"-----------------------------------------------------------------------------------------------------------------"
while i<len1:
elems=gradeList[i]
strs="|"
j=0
while j<len2:
elem=elems[j].decode('utf-8').ljust(10,' ')
strs=strs+elem+"|"
j=j+1
i=i+1
print strs
print u"-----------------------------------------------------------------------------------------------------------------"
def start(self):
page=self.getGradePage()
gradeList=self.getGrade(page)
self.printGrade(gradeList)
username=raw_input('please input your username:')
password=raw_input('please input your password:')
studyYear=raw_input('please input your studyyear:')
a=Grade(username,password,studyYear)
print a.start()