(点击
上方蓝字
,快速关注我们)
编译:伯乐在线 - 飞哥的咖啡
如有好文章投稿,请点击 → 这里了解详情
大学生都知道那种选课时无课可选的痛苦,而我所在的大学甚至对大部分课程都不提供候补系统。我们每天不得不多次登录查看选课网站。这种机械操作似乎是计算机擅长的事,所以我着手用一些学过的 Python 知识和 Twilio API 来实现选课自动化。
开始阶段
由于大学的课程注册系统需要密码登录,我们打算使用自建的简化版网站(http://courses.project.samueltaylor.org/)。出于演示的目的,CS 101 课程的空余名额将以 1 分钟 1 次的频率在 0 和 1 之间切换。
本项目中我们打算使用一些库来帮助我们。假设你已经安装了 pip,使用下面的 pip 命令来安装需要的库:
pip install requests==2.17.3 beautifulsoup4==4.6.0 redis==2.10.5 twilio==6.3.0 Flask==0.12.2
随着项目的深入,我们会仔细研究用到的每一个库。
抓取注册系统
我们需要写一个程序来帮助我们确定指定的课程是否有空余名额。这里我们使用网页抓取技术来实现,它将从网络上下载网页并寻找到重要的字段(课程名额)。Requests 和 BeautifulSoup 是简化这个过程的两个非常流行的库:Requests 让获取网页变得更加简单,而 BeautifulSoup 帮我们找到网页中我们需要的部分。
# scraper.py
import
requests
from
bs4
import
BeautifulSoup
URL
=
'http://courses.project.samueltaylor.org/'
COURSE_NUM_NDX
=
0
SEATS_NDX
=
1
def
get_open_seats
()
:
r
=
requests
.
get
(
URL
)
soup
=
BeautifulSoup
(
r
.
text
,
'html.parser'
)
courses
=
{}
for
row
in
soup
.
find_all
(
'tr'
)
:
cols
=
[
e
.
text
for
e
in
row
.
find_all
(
'td'
)]
if
cols
:
courses
[
cols
[
COURSE_NUM_NDX
]]
=
int
(
cols
[
SEATS_NDX
])
return
courses
这里的关键是 get_open_seats 函数。此函数中,我们使用 requests.get 下载网页的 HTML 源码,然后使用 BeautifulSoup 解析它。我们使用 find_all(‘tr’) 获得表内的所有行,通过更新课程词典以显示指定课程的剩余名额。find_all 具有极其强大的功能,如果你对它感兴趣并想要深入了解,你可以查看官方文档。最后,我们返回课程词典,这样程序就能看到指定课程还有多少空余名额(比如,courses[‘CS 101’]是 CS 101 的空余名额)。
好极了,现在我们可以判断课程是否有空位了。Python 解释器是检验函数的好办法。将这段代码保存在文件中,并命名为 scraper.py,然后运行脚本并切入交互模式看看函数的功能:
$
python
-
i
scraper
.
py
>>>
get_open_seats
()
{
'CS 101'
:
1
,
'CS 201'
:
0
}
尽管一切顺利,但我们还没有解决这个问题,我们还需要在空余名额出现时,想办法通知用户。该是 Twilio SMS 出场的时候了!
通过 SMS 获取更新
构建用户界面时,我们希望化繁为简。本程序中,用户希望在课程座位开放时收到通知。最简单的解决办法是分享课程编号,我们通过建立和处理 webhook 实现订阅功能。我选择使用 Redis(提供可以从多个进程访问的数据结构的工具)来存储订阅。
# sms_handler.py
from
flask
import
Flask
,
request
import
redis
twilio_account_sid
=
'ACXXXXX'
redis_client
=
redis
.
StrictRedis
(
host
=
'localhost'
,
port
=
6379
,
db
=
0
)
app
=
Flask
(
__name__
)
@
app
.
route
(
'/sms'
,
methods
=
[
'POST'
])
def
handle_sms
()
:
user
=
request
.
form
[
'From'
]
course
=
request
.
form
[
'Body'
].
strip
().
upper
()
redis_client
.
sadd
(
course
,
user
.
encode
(
'utf-8'
))
if
__name__
==
'__main__'
:
app
.
run
(
debug
=
True
)
现在我们使用一个叫做 Flask 的 Python 网络框架来创建小型服务,用于处理 SMS 信息。完成一些初始的设置后,我们设置 handle_sms 函数用于处理 /sms 端点的请求。利用这个函数,我们抓取用户的手机号以及他们寻找的课程,并将其存储在以课程命名的集合中。
做到获取订阅这步,一切还算顺利,但有一个明显的问题:用户界面太烂,它不能给用户提供反馈。我们想要回复用户,通知我们是否能够立刻服务他们的要求。要做到这一点,我们将提供一个 TwiML 响应,额外需要的代码在下方高亮显示:
# sms_handler.py
from
flask
import
Flask
,
request
import
redis
from
twilio
.
twiml
.
messaging_response
import
MessagingResponse
twilio_account_sid
=
'ACXXXXX'
my_number
=
'+1XXXXXXXXXX'
valid_courses
=
{
'CS 101'
,
'CS 201'
}
redis_client
=
redis
.
StrictRedis
(
host
=
'localhost'
,
port
=
6379
,
db
=
0