去年热情高涨的时候心血来潮做了个简易的查询脚本,限于当时技术水平(菜),实现得不是很好,这几天终于想起来填坑了。环境依赖:

brew install python3
pip3 install requests
pip3 install tkinter
pip3 install fuzzywuzzy
pip3 install xlrd

首先,CUP教务处考试安排通知一般是发布在网站的“考试通知”专栏里的。比如:

这样的一个通知,通常内部会有一个考试通知的xls表格文件。

打开以后:

每次考试通知的格式都是一致的。

基于此,思路就来了,先输入考试通知发布网页的地址,然后程序自动获取到文件的下载地址,再自动将文件下载下来,得到考试安排信息。

代码:

def get_one_page(url, headers):
    try:
        response = requests.get(url, headers=headers)
        if response.status_code == 200:
            response.encoding = response.apparent_encoding
            return response.text
        return None
    except RequestException:
        return None


def countPoint(UrlPart):
    cnt = 0
    for i in UrlPart:
        if i != \'.\':
            break
        cnt += 1
    return cnt


def getNewXls(url):
    html = get_one_page(url, headers=headers)
    if not html:
        return False
    als = re.findall(\'<a.*?href="(.*?)"\', html, re.S)
    for a in als:
        if a.endswith(\'xls\'):
            cnt = countPoint(a)
            url = \'/\'.join(url.split(\'/\')[:-cnt]) + a[cnt:]
            break
    content = requests.get(url, headers).content
    with open(\'content.xls\', \'wb\') as f:
        f.write(content)
    return True

在得到考试安排信息后,我分析可以通过“课程&教师&班级”三种条件可以比较精确的搜索到要查询的考试安排。

通过这三个列名,可以建立一个简易的搜索字典:


  data = {\’课程名\’: {}, \’上课老师\’: {}, \’主修班级\’: {}}

def init():
    xls = xlrd.open_workbook(\'content.xls\')
    global name_col, teacher_col, sc_col, sheet
    sheet = xls.sheet_by_index(0)
    keys = sheet.row_values(0)
    for i in range(len(keys)):
        if keys[i] == \'课程名\':
            name_col = i
        elif keys[i] == \'上课教师\':
            teacher_col = i
        elif keys[i] == \'主修班级\':
            sc_col = i
    if not name_col or not teacher_col or not sc_col:
        exit(\'Unknown xls layout\')
    ls = sheet.col_values(name_col)
    for i in range(1, len(ls)):
        if ls[i] not in data[\'课程名\']:
            data[\'课程名\'][ls[i]] = set()
        data[\'课程名\'][ls[i]].add(i)
    ls = sheet.col_values(teacher_col)
    for i in range(1, len(ls)):
        if ls[i] not in data[\'上课老师\']:
            data[\'上课老师\'][ls[i]] = set()
        data[\'上课老师\'][ls[i]].add(i)
    ls = sheet.col_values(sc_col)
    for i in range(1, len(ls)):
        cls = ls[i].split(\',\')
        for cl in cls:
            if cl not in data[\'主修班级\']:
                data[\'主修班级\'][cl] = set()
            data[\'主修班级\'][cl].add(i)

而考虑查询方便,必然不可能让用户(我)每次都输入精准的信息才能查到结果,这太不酷了。

所以我考虑间隔匹配+模糊匹配的方式来得到搜索结果。

间隔匹配:

def fm(string, ls):
    res = []
    match = \'.*?\'.join([i for i in string])
    for i in ls:
        if re.findall(match, i):
            res.append((i, 100))
    return res

模糊匹配:(这里使用了一个叫fuzzywuzzy的第三方库,只有间隔匹配失败后才会使用模糊匹配)

res = fm(aim, data[keys[i]].keys())
if not res:
     res = process.extract(aim, data[keys[i]].keys(), limit=3)

那么如果用户提供了多个搜索条件怎么处理呢?答案是利用集合的并交运算来处理。

比如搜索表达式: xx&yy&zz。显然我们通过搜索算法可以得到三个独立集合分别为xx,yy和zz的结果,那么对这三个集合取交即可得到正解。

def search(exp):
    if not pre_check():
        return None
    keys = [\'课程名\', \'上课老师\', \'主修班级\']
    res_set = set()
    flag = False
    for i in range(len(exp)):
        if i < 3:
            aim = exp[i].strip()
            if not aim:
                continue
            res = fm(aim, data[keys[i]].keys())
            if not res:
                res = process.extract(aim, data[keys[i]].keys(), limit=3)
            ts = set()
            for mth in res:
                if mth[1]:
                    ts = ts.union(data[keys[i]][mth[0]])
            if flag:
                res_set = res_set.intersection(ts)
            else:
                res_set = res_set.union(ts)
                flag = True
        else:
            break
    res = \'\'
    for line_num in res_set:
        line = sheet.row_values(line_num)
        res += \'-\' * 50 + \'\n\'
        res += \'课程名称:\' + line[name_col] + \'\n\'
        res += \'授课教师:\' + line[teacher_col].replace(\'\n\', \',\') + \'\n\'
        cls = line[sc_col].split(\',\')
        linkstr = \'\'
        for i in range(len(cls)):
            linkstr += cls[i]
            if i + 1 == len(cls):
                break
            elif (i + 1) % 5 == 0:
                linkstr += \'\n\' + \' \' * 9
            else:
                linkstr += \',\'
        res += \'主修班级:\' + linkstr + \'\n\'
        day = "%04d年%02d月%02d日" % xldate_as_tuple(line[4], 0)[:3]
        res += \'考试时间:\' + day + \'(周%s) \' % line[2] + line[5] + \'\n\'
        res += \'考试地点:\' + line[-1].replace(\'\n\', \',\') + \'\n\'
    return res

到这,脚本的硬核部分就结束了~

然后我们基于这份代码,撸一个GUI出来。

大功告成~!

GitHub开源地址:https://github.com/Rhythmicc/CUP_EXAM

 

版权声明:本文为Rhythm-原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/Rhythm-/p/11273708.html