課程作業需要實現一個課程表,我負責完成學校的教務系統中課程表的導入工作。
需要解決兩個問題,第一個是教務系統訪問課程表所在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