go2sleep

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

需要解决两个问题,第一个是教务系统访问课程表所在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

分类:

技术点:

相关文章: