学校教务系统的课程表爬取

课程作业需要实现一个课程表,我负责完成学校的教务系统中课程表的导入工作。

需要解决两个问题,第一个是教务系统访问课程表所在url时,会被告知需要先加载某框架,这让我很困扰,不知道如何用urlopen去解决这个问题;第二个问题是,不同的课程对应的课时是不一样的,意味着显示的时候rowspan值是不一样的,需要重新进行解析。

第一个问题采用selenium库,然后使用Chrome的headless模式就可以解决模拟登陆并获取课程表的html。
这里面课程表所在页面的url不是固定的,而是存在于MenuFrame的一段js代码中,因此采用js2xml对js代码格式化,然后使用xpath提取其中的url。
代码如下:

def login(login_driver, username, password):
    login_driver.get('http://yjxt.bupt.edu.cn')
    username = login_driver.find_element(By.ID, 'username')
    username.send_keys(username)

    password = login_driver.find_element(By.ID, 'password')
    password.send_keys(password)

    button = login_driver.find_element_by_class_name('btn-lg')
    button.click()


def get_payload(get_payload_driver):
    # 课程表所在页面的url不是固定的,需要从js代码中提取
    get_payload_driver.switch_to.frame('MenuFrame')
    source = get_payload_driver.page_source

    soup = BeautifulSoup(source, 'html.parser')
    script = soup.select('body form script')[1].string

    # 使用js2xml格式化之后再使用xpath提取js代码中附加的url
    script_text = js2xml.parse(script, debug=False)

    for x in script_text.xpath("//object/property[@name = 'url']/string/text()"):
        if x[:21] == 'Course/StuCourseQuery':
            return x


def get_course_html(get_course_html_driver, payload):
    get_course_html_driver.get('http://yjxt.bupt.edu.cn/Gstudent/' + payload)
    return get_course_html_driver.page_source

在获取课程表页面的html之后就需要去解析课程表,课程表中每个课程占据的单元格是不定的,比如数学课占据了周一上午的第一节和第二节课,那么在遍历的过程中是无法遍历到周一上午第二节课这个单元格的,实际获取的单元格是周二上午第二节课,因此需要获得下一个单元格的实际位置用于同步,这里维护了一个二维数组用于模拟课程表,在遍历的过程中对其染色,比如遍历到了周一上午第一节课,会同时将第二节课也染色,这样就可以根据next_pic()函数获取实际的位置。代码如下:

def next_pic(pic, x, y):
    for i in range(x, 11):
        if i == x:
            for j in range(y, 9):
                if pic[i][j] == 0 and j != y:
                    return i, j
        else:
            for j in range(9):
                if pic[i][j] == 0:
                    return i, j


def analysis_html(html):
    # 解析html

    # 获取table
    soup = BeautifulSoup(html, 'html.parser')
    table = soup.find(id='contentParent_dgData')

    # 解析table
    pic = [[0 for i in range(9)] for i in range(11)]
    row = 0
    column = 0

    for x in table.find_all('td'):
        if x.string != '\n':
            try:
                rowspan = int(x['rowspan'])
            except AttributeError as e:
                rowspan = 1

            for i in range(rowspan):
                pic[row + i][column] = 1

            if x.string.strip().encode('utf-8') in EnumA:
                row, column = next_pic(row, column)
                continue
            else:
                print(x.string.strip().encode('gbk'), '周'.decode('utf-8').encode('gbk'),
                      column, row + 1, '-', row + rowspan)
        else:
            row, column = next_pic(row, column)

完整的代码见github

posted @ 2020-03-11 16:15  go2sleep  阅读(1612)  评论(0编辑  收藏  举报