avatar

目录
使用Selenium+Beautiful Soup爬取杭电学生成绩

前言

持续摸鱼的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解释器,为用户灵活地提供不同的解析策略或强劲的速度。

那么在简单了解了他们是干嘛的后,我们就事不宜迟赶快爬一下吧

问题分析

获取到成绩页面需要许多步骤:

  1. 需要登录自己的账号
  2. 在登录以后,出现的页面只是个中转页面,还需要继续操作,才能到达成绩查询系统
  3. 在登录到成绩查询系统后,还需要通过获取悬浮菜单,点击相关按钮,才能查看成绩
  4. 成绩单是位于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()
文章作者: f1rry
文章链接: http://yoursite.com/2018/10/31/使用Selenium-Beautifulsoup爬取杭电学生成绩/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 F1rry's blog