def http_post(url: str, data: dict): """ 发送 post 请求 :param url: :param data: :return: """ http_session = session() http_session.headers = {'Cookie': g.bus_user.bus_session, 'user-agent': get_user_agent()} try: # 发送请求 response = http_session.post(url, data=data, allow_redirects=False) except OSError: raise NFUError('学校车票系统错误,请稍后再试') try: # 处理返回的数据 response = loads(response.text) except decoder.JSONDecodeError: if response.status_code == 200: raise NFUError('学校车票系统错误,请稍后再试') if g.refresh: __refresh() g.refresh = False return http_post(url, data) return response
def get_jw_token(student_id: int, password: str = '', count: int = 0) -> str: """ 登陆教务系统 :param count: :param student_id: 学号 :param password: 密码,默认为空字符串 :return token: """ url = 'http://ecampus.nfu.edu.cn:2929/jw-privilegei/User/r-login' data = {'username': student_id, 'password': password, 'rd': ''} try: response = post(url, data=data, timeout=10) token = loads(response.text)['msg'] except (OSError, decoder.JSONDecodeError): if count >= 5: raise NFUError('教务系统登录接口错误,请稍后再试') else: return get_jw_token(student_id, password, count + 1) if not token: raise NFUError('学号或密码错误!') return token
def get_total_achievement_point(token: str, count: int = 0) -> dict: """ 获取学分、成绩的总体情况 :param count: :param token: :return: """ url = 'http://ecampus.nfu.edu.cn:2929/jw-amsi/AmsJxbXsZgcj/listXs' data = { 'deleted': False, 'pageSize': 65535, 'id': get_actual_id(token), 'jwloginToken': token } try: response = post(url, data=data, timeout=10) data = loads(response.text)['msg']['list'][0] except (OSError, KeyError, decoder.JSONDecodeError): if count >= 5: raise NFUError('教务系统绩点接口错误,请稍后再试') return get_total_achievement_point(token, count + 1) if not data: if count >= 5: raise NFUError('没有获取到数据,请稍后再试') return get_total_achievement_point(token, count + 1) return { 'selected_credit': data['yxxf'], 'get_credit': data['yhdxf'], 'average_achievement': data['avg'], 'average_achievement_point': data['avgJd'] }
def get_student_name(student_id: int, password: str) -> str: """ 与教务系统校对账号密码,并获取学生姓名 - 字段说明 - username 学号 - password 密码 - rd 随机数,可以随机,也可以不填,我也不知道来干嘛的 :param student_id: 学号 :param password: 教务系统的密码 :return name: """ token = get_jw_token(student_id, password) url = 'http://ecampus.nfu.edu.cn:2929/jw-privilegei/User/r-getMyself' data = {'jwloginToken': token} try: response = post(url, data=data, timeout=10) name = loads(response.text)['msg']['name'] except (OSError, KeyError): raise NFUError('实名验证错误,请稍后再试') if not name: raise NFUError('没有获取到数据,请稍后再试') return name
def get_ticket_ids(order_id: int) -> dict: """ 因为一个订单里面可能有多张车票,所以我们爬取一下车票号 :param order_id: 订单id :return: """ url = 'http://nfuedu.zftcloud.com/campusbus_index/order/refund_ticket.html' params = {'order_id': order_id} response = http_get(url, params) route, date = get_route(response.text) ticket_list = [] ticket_data = findall(r'<span class="title_name title_w">.+\n.+\n.+\n.+\n.+', response.text) try: price = search(r'<span class="fare">\d+</span>', response.text).group()[19:-7] except AttributeError: raise NFUError('学校车票系统错误,请稍后再试') for ticket in ticket_data: try: name = search(r'w">.+<s', ticket).group()[3:-9] phone = search(r'<span class="title_iphone">\d+</span>', ticket).group()[27:-7] except AttributeError: raise NFUError('学校车票系统错误,请稍后再试') try: ticket_id = search(r', \d+', ticket).group()[2:] except AttributeError: ticket_list.append({ 'state': '已退票', 'name': name, 'phone': phone }) else: ticket_list.append({ 'state': f'{price}¥', 'name': name, 'phone': phone, 'ticketId': ticket_id }) return { 'route': route, 'date': date, 'passengerList': ticket_list }
def validate_token(token: str, token_type: str = 'ACCESS_TOKEN') -> dict: """ 验证令牌 :param token: 令牌 :param token_type: 令牌类型 """ s = TimedJSONWebSignatureSerializer(getenv(token_type)) try: data = s.loads(token) except SignatureExpired: raise NFUError('签名已过期', code='1001') except BadSignature: raise NFUError('签名错误', code='1001') else: return data
def get_token() -> str: """ 从请求头获取token :return: """ try: token_type, token = request.headers['Authorization'].split(None, 1) except (KeyError, ValueError): raise NFUError('请重新登录', code='1001') if token == 'null' or token_type.lower() != 'bearer': raise NFUError('请重新登录', code='1001') return token
def get_profile(student_id: int, token: str, count: int = 0): grade = int(f'20{str(student_id)[:2]}') student_data = get_student_data(token) url = 'http://ecampus.nfu.edu.cn:2929/jw-srsi/SrsFjflStudent/r-getZyfxRecByJbzlId' data = {'id': student_data['actualId'], 'jwloginToken': token} try: response = post(url, data=data, timeout=10) data = loads(response.text) except (OSError, decoder.JSONDecodeError): if count >= 5: raise NFUError('教务系统专业接口繁忙') return get_profile(student_id, token, count + 1) try: profile = data['msg'] except KeyError: return { 'grade': grade, 'college_id': student_data['xyid'], 'profession_id': student_data['zyid'], 'direction': '未分专业方向' } else: return { 'grade': grade, 'college_id': student_data['xyid'], 'profession_id': student_data['zyid'], 'direction': profile['zyfxmc'] }
def get_pay_order(order_id: int, ) -> dict: """ 获取订单的支付数据 :param order_id: :return: """ url = f'http://nfuedu.zftcloud.com/campusbus_index/order/notpay_order/order_id/{order_id}.html' response = http_get(url) route, date = get_route(response.text) try: names = findall(r'<span class="title_name title_w">\D+</span>', response.text) phones = findall(r'<span class="title_iphone">\d+</span>', response.text) trade_no = search(r'var tradeNo = .+', response.text).group()[15:-2] price = search(r'¥<span>\d+</span>', response.text).group()[7:-7] except AttributeError: raise NFUError('学校车票系统错误,请稍后再试') # 把乘客信息处理成一个列表 passengers = [] for i, name in enumerate(names): passengers.append({ 'name': name[33:-7], 'phone': phones[i][27:-7] }) return { 'route': route, 'date': date, 'passengers': passengers, 'price': price, 'alipayUrl': get_alipay_url(trade_no), 'alipayQrUrl': f"{getenv('API_URL')}/school-bus-pro/alipay/qrcode?tradeNo={trade_no}" }
def __refresh() -> None: """ 刷新 车票session :return: """ http_session = session() http_session.headers = {'user-agent': get_user_agent()} url = 'http://nfuedu.zftcloud.com/campusbus_index/ticket_checked/index' params = { 'school_code': 11557, 'alipay_user_id': g.bus_user.alipay_user_id, 'idcard': g.bus_user.id_card, 'avatar': g.bus_user.avatar, 'phone': '', 'real_name': g.user.name } try: # 发送请求 http_session.get(url, params=params) except OSError: raise NFUError('学校车票系统错误,请稍后再试') # 把新的session写入MySql g.bus_user.bus_session = f"PHPSESSID={http_session.cookies['PHPSESSID']}" db.session.add(g.bus_user) db.session.commit()
def get_bus_schedule(route_id: int, date: list) -> dict: """ 若日期在车票预售期内,获取班车时刻表。 - 字段说明 - route_id 路线id:南苑 -> 河堤公园:21, 河堤公园 -> 南苑:22, 南苑 -> 中大南校区:13, 中大南校区 -> 南苑:14 - time 乘车日期 :param route_id: 校车路线 :param date: 乘车日期 :return: """ url = 'http://nfuedu.zftcloud.com/campusbus_index/ticket/show_schedule.html' params = { 'route_id': route_id, 'time': date } response = http_get(url, params) try: data = loads(search(r'var msg = .+', response.text).group()[10:-1]) except (AttributeError, decoder.JSONDecodeError): raise NFUError('学校车票系统错误,请稍后再试') else: return data
def get_achievement_list(token: str, school_year: int, semester: int, count: int = 0) -> list: """ 获取成绩单 :param count: :param token: :param school_year: :param semester: :return: """ url = 'http://ecampus.nfu.edu.cn:2929/jw-amsi/AmsJxbXsZgcj/r-list' data = { 'deleted': False, 'pg': 1, 'pageSize': 50, 'kkxn': school_year, 'xnxq': semester, 'jwloginToken': token } try: response = post(url, data=data, timeout=10) except OSError: if count >= 5: raise NFUError('教务系统成绩接口错误,请稍后再试') else: return get_achievement_list(token, school_year, semester, count + 1) try: course = loads(response.text)['msg'] except (KeyError, decoder.JSONDecodeError): if count >= 5: raise NFUError('教务系统错误,请稍后再试') else: return get_achievement_list(token, school_year, semester, count + 1) try: course = course['list'] except KeyError: raise NFUError(course) return course
def __set_wechat_pay(self, json_data, signature) -> tuple: """ 设置支付方式为微信支付 返回 json、signature 为向支付页面 post 的 body 只要设置好创建订单数据的 cookie,并重定向回安心付支付接口,即可调用安心付支付接口 学校安心付接口为 post 提交,但后端并无验证,故 body 数据在 url 带上并重定向即可 - 字段说明 - json json_data - signature signature - payChannel 设置为微信支付 :param json_data: ready_pay 返回的订单数据 :param signature: ready_pay 返回的数字签名 """ url = 'http://nfu.zhihuianxin.net/paycenter/gateway_web' data = {'json': json_data, 'signature': signature} header = {'Referer': 'http://nfu.zhihuianxin.net/electric/pay/doPay'} try: # 向安心付接口 post 订单数据,无需返回值 self.__http_session.post(url, data=data, headers=header) except OSError: raise NFUError('与安心付服务器连接超时,请稍后再试') url = 'http://nfu.zhihuianxin.net/paycenter/payGateway_web' data = {'payChannel': 'WxPay'} header = { 'Referer': 'http://nfu.zhihuianxin.net/paycenter/gateway_web' } try: response = self.__http_session.post(url, data=data, headers=header) except OSError: raise NFUError('与安心付服务器连接超时,请稍后再试') try: json_data = search(r'name="json" value=.+', response.text).group()[19:-5] signature = search(r'name="signature" value=.+', response.text).group()[24:-5] except AttributeError: raise NFUError('与安心付服务器连接超时,请稍后再试') return json_data, signature
def verification_code(code: int) -> None: """ 验证验证码 :return: """ r = Redis(host='localhost', password=getenv('REDIS_PASSWORD'), port=6379) try: if int(r.get(g.user.id)) == code: # 删除缓存中的数据 r.delete(g.user.id) else: raise NFUError('验证码错误', code='2001') except TypeError: raise NFUError('验证码错误', code='2001')
def __ready_pay(self) -> tuple: """ 准备支付,获取智能电表签名 - 字段说明 - amt 充值金额 - custNo 学生学号 - custName 学生姓名 - roomId 房间号 - areaName 学校名字,写死南方学院就行 - architectureName 宿舍楼 - floorName 楼层 - roomName 楼号 """ url = 'http://nfu.zhihuianxin.net/electric/pay/doPay' data = { 'amt': self.amount, 'custNo': self.user_id, 'custName': self.name, 'roomId': self.room_id, 'areaName': '南方学院', 'architectureName': self.building, 'floorName': self.floor, 'roomName': self.room } try: response = self.__http_session.post(url, data=data, timeout=1) except OSError: raise NFUError('与安心付服务器连接超时,请稍后再试') try: # 尝试获取签名 json_data = search(r'name="json" value=.+', response.text).group()[19:-4] signature = search(r'name="signature" value=.+', response.text).group()[24:-4] except AttributeError: raise NFUError('与安心付服务器连接超时,请稍后再试') return json_data, signature
def get_passenger_data() -> list: """ 获取乘车人数据 :return: """ url = 'http://nfuedu.zftcloud.com/campusbus_index/my/passenger_puls.html' response = http_get(url) try: passenger = loads(search(r'var passenger = .+', response.text).group()[16:-1]) except (AttributeError, decoder.JSONDecodeError): raise NFUError('学校车票系统错误,请稍后再试') else: return passenger
def get_electric_log(room_id: int, page_index: int) -> dict: """ 电费充值记录 :param room_id: :param page_index: :return: """ url = 'http://nfu.zhihuianxin.net/electric/order/page' data = {'ammeterId': room_id, 'pageIndex': page_index, 'pageSize': 10} try: response = post(url, data=data, timeout=1) electric_create = loads(response.text)['data'] except (OSError, KeyError, decoder.JSONDecodeError): raise NFUError('安心付服务器错误') return electric_create
def return_ticket(order_id: int, ticket_id: int) -> str: """ 退票 :param order_id: 订单id :param ticket_id: 车票id :return: """ url = 'http://nfuedu.zftcloud.com/campusbus_index/order/refund_ticket.html' data = {'order_id': order_id, 'ticket_id': ticket_id} response = http_post(url, data) if response['code'] != '0000': raise NFUError(response['desc']) return response['desc']
def get_route(response): """ 从html中获取路径数据 :param response: :return: """ try: route = '{} -> {}'.format( search(r'<span class="site_from">.+</span>', response).group()[24:-7], search(r'<span class="site_to">.+</span>', response).group()[22:-7] ) date = '{} {}'.format( search(r'<span class="time_go">\S+</span>', response).group()[22:-7], search(r'<span class="time_day">\S+</span>', response).group()[23:-7] ) except AttributeError: raise NFUError('学校车票系统错误,请稍后再试') return route, date
def get_student_data(token: str, count: int = 0) -> dict: """ 获取学生信息 :param token: :param count: :return: """ url = 'http://ecampus.nfu.edu.cn:2929/jw-privilegei/User/r-getMyself' data = {'jwloginToken': token} try: response = post(url, data=data, timeout=10) data = loads(response.text)['msg'] except (OSError, KeyError, decoder.JSONDecodeError): if count >= 5: raise NFUError('教务系统专业接口繁忙') return get_student_data(token, count + 1) return data
def create_order(passenger_ids: str, connect_id: int, schedule_id: int, date: str, take_station: str) -> dict: """ 创建车票订单 支付宝H5开发文档,支付篇: https://myjsapi.alipay.com/alipayjsapi/util/pay/tradepay.html :param passenger_ids: 乘客id :param connect_id: 联系人id :param schedule_id: 班车id :param date: 日期 :param take_station: 乘车车站 :return: 错误信息,或订单数据 """ url = 'http://nfuedu.zftcloud.com/campusbus_index/order/create_order.html' data = { 'passenger_ids': passenger_ids, 'connect_id': connect_id, 'schedule_id': schedule_id, 'date': date, 'take_station': take_station, 'seat_num': '' # 此数据为座位号,抢票要什么座位要求,直接为空 } response = http_post(url, data) if not response['code'] == '10000': raise NFUError(response['desc'], code=response['code']) return { 'tradeNo': response['trade_no'], 'outTradeNo': response['out_trade_no'], 'orderId': response['order_id'] }
def http_get(url: str, params=None): """ 发送 get 请求 :param url: :param params: :return: """ http_session = session() http_session.headers = {'Cookie': g.bus_user.bus_session, 'user-agent': get_user_agent()} try: # 发送请求 response = http_session.get(url, params=params, allow_redirects=False) if response.status_code != 200 and g.refresh: __refresh() # 防止递归死循环 g.refresh = False return http_get(url, params) except OSError: raise NFUError('学校车票系统错误,请稍后再试') return response
def get_ticket_data(order_id: int) -> tuple: """ 获取电子票的数据 - 字段说明 road_from 始发车站 road_to 终点站 year 乘车年份 week 乘车星期 time 发车时间 bus_id 班车号 take_station 乘车车站 :param order_id: 订单id :return bus_data: 班车数据,同个订单,统一即可 :return ticket: 车票数据 :return javascript: 处理车票的js,动态生成js,实在是太骚了。 """ url = 'http://nfuedu.zftcloud.com/campusbus_index/order/ticket.html' params = {'order_id': order_id} response = http_get(url, params) try: bus_data = { 'roadFrom': search(r'<span class="road_from">.+', response.text).group()[:-1], 'roadTo': search(r'<span class="road_to">.+', response.text).group()[:-1], 'year': search(r'<span class="data_y">.+', response.text).group()[:-1], 'week': search(r'<span class="data_week">.+', response.text).group()[:-1], 'time': search(r'<span class="data_hm">.+', response.text).group()[:-1], 'busId': search(r'<div class="data_bc">.+', response.text).group()[:-1], 'takeStation': search(r'上车点:.+', response.text).group()[:-5] } javascript = search(r'<script>.+</script>', response.text, S).group() except AttributeError: raise NFUError('学校车票系统错误,请稍后再试') ticket_ids = findall(r'<p class="erwei_num">电子票号:.+', response.text) passengers = findall( r'<p class="erwei_num erwei_c"..style="text-align: center;text-indent:0.2.+', response.text) seats = findall( r'<p class="erwei_num erwei_c" style="text-align: center;text-indent:.5rem;">座.+', response.text) ticket = [] for i, ticket_id in enumerate(ticket_ids): ticket.append({ 'ticketId': ticket_id[:-1], 'passenger': passengers[i][:-1], 'seat': seats[i][:-1] }) return bus_data, ticket, javascript
def get_class_schedule(token: str, school_year: int, semester: int, count: int = 0) -> list: """ 向教务系统请求课程表数据 :param count: :param token: :param school_year: :param semester: :return: """ course_data = [] url = 'http://ecampus.nfu.edu.cn:2929/jw-cssi/CssStudent/r-listJxb' data = {'xn': school_year, 'xq': semester, 'jwloginToken': token} try: response = post(url, data=data, timeout=10) course_list = loads(response.text)['msg'] except (OSError, KeyError, decoder.JSONDecodeError): if count >= 5: raise NFUError('教务系统课程表接口错误,请稍后再试') else: return get_class_schedule(token, school_year, semester, count + 1) # 判断获取的数据是否是列表,如果不是列表,可能教务系统又炸了 if not isinstance(course_list, list): if count >= 5: raise NFUError('教务系统错误,请稍后再试') else: return get_class_schedule(token, school_year, semester, count + 1) for course in course_list: # 循环所有课程 for merge in course['kbMergeList']: # 课程可能有不同上课时间,循环取出 teacher = [] for teacher_list in merge['teacherList']: teacher.append(teacher_list['xm']) try: course_data.append({ 'course_name': course['name'], 'subdivision_type': course['l3mc'], 'course_id': course['pkbdm'], 'credit': float(course['kcxf']), 'teacher': teacher, 'classroom': merge['classroomList'][0]['jsmc'], 'weekday': merge['xq'], 'start_node': merge['qsj'], 'end_node': merge['jsj'], 'start_week': merge['qsz'], 'end_week': merge['jsz'] }) except IndexError: course_data.append({ 'course_name': course['name'], 'subdivision_type': course['l3mc'], 'course_id': course['pkbdm'], 'credit': float(course['kcxf']), 'teacher': teacher, 'classroom': '未分配教室', 'weekday': merge['xq'], 'start_node': merge['qsj'], 'end_node': merge['jsj'], 'start_week': merge['qsz'], 'end_week': merge['jsz'] }) course_data.sort(key=lambda x: x['course_id']) return course_data