Python项目:扇贝网小组查卡助手
扇贝网是一个非常棒的英语学习网站,大家还可以加入一些小组,一起交流学习、共同进步。但是,小组管理起来非常辛苦,尤其是在0点前踢出不打卡的成员,因此考虑利用程序来实现小组查卡自动化。
登录
操作 | 扇贝网登录 | |
URL | http://www.shanbay.com/accounts/login/ | |
方式 | POST | |
数据 | csrfmiddlewaretoken | CSRF令牌 |
username | 用户名 | |
password | 密码 |
CSRF令牌存在于Cookie中,我们需要先以GET方式访问该URL,就能取到CSRF令牌了。
# -*- coding: utf-8 -*- import requests class Shanbay(): def __init__(self, username, password): self.request = requests.Session() self.username = username self.password = password def login(self): url = 'http://www.shanbay.com/accounts/login/' r = self.request.get(url) csrftoken = r.cookies['csrftoken'] data = { 'csrfmiddlewaretoken': csrftoken, 'username': self.username, 'password': self.password, } return self.request.post(url, data=data).ok
成员管理
如果我们获取小组管理后台所有组员的信息,比较费时间。考虑实际需求,不妨仅获取当天未打卡的组员的信息,这样能大大提高查卡效率。
踢人需要data-id,这个在小组管理后台页面就能获取到。但是,如果我们想发站内短信,就需要username,而username在小组管理后台页面里是没有的,这个需要查看个人打卡日记。
从个人打卡日记不仅能看到username,还能看到该贝友入组后最近已连续有多少天未打卡(这往往也是组规限定的内容)等等。
操作 | 踢人 | |
URL | http://www.shanbay.com/api/v1/team/member/ | |
方式 | PUT | |
数据 | action | 动作('dispel') |
ids | data-id |
# -*- coding: utf-8 -*- from bs4 import BeautifulSoup from Journal import Journal import re class Domain(): def __init__(self, shanbay): self.shanbay = shanbay self.request = shanbay.request def get_not_checked_members(self): ''' data_id : 踢人时需要data_id role : 身份标识 nickname : 昵称 user_id : 发短信时需要user_id username : 用户名 points : 贡献值 days : 组龄 rate : 打卡率 checked_yesterday: 昨天是否打卡 checked : 今天是否打卡 off_dyas : 入组后最近连续未打卡天数 ''' members = [] for page in range(1, 48): html = self.request.get('http://www.shanbay.com/team/manage/?t=checkin_today&page=%d' % page).text soup = BeautifulSoup(html, 'html5lib') for member in soup.find_all('tr', class_='member'): checked = member.find_all(class_='checked')[1].find('span').text.strip() == '已打卡' if checked: break days = int(member.find(class_='days').text) user_id = re.findall('\d+', member.find(class_='user').find('a')['href'])[0] user = Journal(shanbay=self.shanbay, user_id=user_id) checked_yesterday = member.find_all(class_='checked')[0].find('span').text.strip() == '已打卡' if checked_yesterday: off_days = 1 else: off_days = user.get_off_days(days) data = { 'data_id':member['data-id'], 'role':member['role'], 'nickname':member.find(class_='user').find('a').text, 'user_id':user_id, 'username':user.get_username(), 'points':int(member.find(class_='points').text), 'days':days, 'rate':float(member.find(class_='rate').find('span').text[:-2]), 'checked_yesterday':checked_yesterday, 'checked':checked, 'off_dyas':off_days } members.append(data) else: continue break return members def dismiss(self, data_ids): url = 'http://www.shanbay.com/api/v1/team/member/' data = { 'action': 'dispel', } data['ids'] = ','.join(map(str, data_ids)) r = self.request.put(url, data=data) return r.json()['msg'] == "SUCCESS"
(这里用到了Python跳出两层循环的技巧*^_^*)
打卡日记
通过打卡日记,我们可以获得一些基本信息,例如:用户名、最近连续未打卡天数等。
# -*- coding: utf-8 -*- from bs4 import BeautifulSoup import re import datetime import time class Journal(): def __init__(self, shanbay, user_id): self.shanbay = shanbay self.request = shanbay.request self.user_id = user_id self.soup = self.__get_journal_soup() def __get_journal_soup(self): html = self.request.get('http://www.shanbay.com/checkin/user/%s/' % self.user_id).text return BeautifulSoup(html) def get_username(self): return re.findall(u'(\w+)\s*的日记', self.soup.find_all(class_='page-header')[0].find('h2').text)[0] def get_off_days(self, days=0): pass
站内短信
操作 | 发送站内短信 | |
URL | http://www.shanbay.com/api/v1/message/ | |
方式 | POST | |
数据 | recipient | 收件人(username) |
subject | 标题 | |
body | 内容 | |
csrfmiddlewaretoken | CSRF令牌 |
# -*- coding: utf-8 -*- class Message(): def __init__(self, shanbay): self.shanbay = shanbay self.request = shanbay.request def send_msg(self,recipient, subject, body): url = 'http://www.shanbay.com/api/v1/message/' data = { 'recipient': recipient, 'subject': subject, 'body': body, 'csrfmiddlewaretoken': self.request.cookies['csrftoken'] } return self.request.post(url, data=data).ok
小组管理
操作 | 设定加组条件 | |
URL | http://www.shanbay.com/team/setqualification/{team_id} | |
方式 | POST | |
数据 | value | 天数 |
kind | 类型 | |
condition | 条件 | |
team | 小组id | |
csrfmiddlewaretoken | CSRF令牌 |
若需要在小组发帖或回帖,需要forum_id而不是小组id,而forum_id可以通过小组主页找到。
操作 | 发帖 | |
URL | http://www.shanbay.com/api/v1/forum/{forum_id}/thread/ | |
方式 | post | |
数据 | title | 标题 |
body | 内容 | |
csrfmiddlewaretoken | CSRF令牌 |
操作 | 回帖 | |
URL | http://www.shanbay.com/api/v1/forum/thread/{post_id}/post/ | |
方式 | POST | |
数据 | body | 内容 |
csrfmiddlewaretoken | CSRF令牌 |
# -*- coding: utf-8 -*- from bs4 import BeautifulSoup class Team(): def __init__(self, shanbay, team_id): self.shanbay = shanbay self.request = shanbay.request self.team_id = team_id self.forum_id = self.__get_forum_id() def set_join_limit(self, days, kind=2, condition='>='): url = 'http://www.shanbay.com/team/setqualification/%s' % self.team_id data = { 'value': days, 'kind': kind, 'condition': condition, 'team': self.team_id, 'csrfmiddlewaretoken': self.request.cookies['csrftoken'] } r = self.request.post(url, data=data) return 'http://www.shanbay.com/referral/invite/?kind=team' == r.url def __get_forum_id(self): html = self.request.get('http://www.shanbay.com/team/detail/%s/' % str(self.teamId)).text soup = BeautifulSoup(html) return soup.find(id='forum_id')['value'] def new_post(self, title, content): url = 'http://www.shanbay.com/api/v1/forum/%s/thread/' % self.forum_id data = { 'title': title, 'body': content, 'csrfmiddlewaretoken': self.request.cookies['csrftoken'] } return self.request.post(url, data=data).json() def reply_post(self, post_id, content): url = 'http://www.shanbay.com/api/v1/forum/thread/%s/post/' % post_id data = { 'body': content, 'csrfmiddlewaretoken': self.request.cookies.get('csrftoken') } return self.request.post(url, data=data).json()