我用 Python 和 Twilio 實現自動化選課
(點擊
上方藍字
,快速關注我們)
編譯:伯樂在線 - 飛哥的咖啡
如有好文章投稿,請點擊 → 這裡了解詳情
大學生都知道那種選課時無課可選的痛苦,而我所在的大學甚至對大部分課程都不提供候補系統。我們每天不得不多次登錄查看選課網站。這種機械操作似乎是計算機擅長的事,所以我著手用一些學過的 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
)
app
=
Flask
(
__name__
)
def
respond
(
user
,
body
)
:
response
=
MessagingResponse
()
response
.
message
(
body
=
body
)
return
str
(
response
)
@
app
.
route
(
"/sms"
,
methods
=
[
"POST"
])
def
handle_sms
()
:
user
=
request
.
form
[
"From"
]
course
=
request
.
form
[
"Body"
].
strip
().
upper
()
if
course
not
in
valid_courses
:
return
respond
(
user
,
body
=
"Hm, that doesn"t look like a valid course. Try something like "CS 101"."
)
redis_client
.
sadd
(
course
,
user
.
encode
(
"utf-8"
))
return
respond
(
user
,
body
=
f
"Sweet action. We"ll let you know when there are seats available in {course}"
)
if
__name__
==
"__main__"
:
app
.
run
(
debug
=
True
)
我們針對以上代碼做了兩個大的改動。首先,我們驗證用戶是否在尋找一個有效的課程。其次,當用戶請求更新時,我們對其響應。在響應函數中,我們建立了一個 TwiML 響應,用指定的信息內容回復指定的號碼。
確保已安裝 Redis 並通過 redis 伺服器命令啟動它。將上述代碼保存在一個命名為 sms_handler.py 的文件中,並通過 Python 運行它。
誠然,這裡的響應消息有點傻,但我驚訝地看到用戶很喜歡它們。有時候一些人為觸發的反饋能帶來更好的用戶體驗。
現在,讓我們擴展早期的腳本,完善通知功能,真正滿足那些想要在課程開放時獲得通知信息的人。
# scraper.py
from
twilio
.
rest
import
Client
client
=
Client
(
twilio_account_sid
,
token
)
redis_client
=
redis
.
StrictRedis
(
host
=
"localhost"
,
port
=
6379
,
db
=
0
)
def
message
(
recipient
,
body
)
:
message
=
client
.
messages
.
create
(
to
=
recipient
,
from_
=
my_number
,
body
=
body
)
if
__name__
==
"__main__"
:
courses
=
get_open_seats
()
for
course
,
seats
in
courses
.
items
()
:
if
seats
==
0
:
continue
to_notify
=
redis_client
.
smembers
(
course
)
for
user
in
to_notify
:
message
(
user
.
decode
(
"utf-8"
),
body
=
f
"Good news! Spots opened up in {course}. "
+
"We"ll stop bugging you about this one now."
)
redis_client
.
srem
(
course
,
user
)
通過 Python 運行 scraper.py,我們對採集程序進行一次性測試。
使用 Cron 密切關注課程
雖然將查看課程註冊網站的過程簡化成了單個的腳本,我們仍希望腳本能夠隔幾分鐘自動運行一次。通過使用 Cron 能夠輕鬆解決這一問題。運行 crontab-e 並添加以下代碼後,我們可以添加一個每三分鐘運行一次的任務:
*/3 * * * * /path/to/scraper.py
寫入代碼後,Cron 守護進程將每隔三分鐘運行一次我們的採集程序。運行 crontab-l 後,我們可以看到計劃任務。這就完成了!我們訂閱課程更新後,就可以不用管它,專註於我們手邊重要的事情。除了獲得樂趣外,你的朋友會非常感激你,因為你讓他們緊繃的神經得到了舒緩,「輕輕鬆鬆」就能選到想要的課程。依靠這個程序選到了想要的課程無疑是對我努力的最大的回報,但它同時也幫助我周圍的很多人選到了心儀的課程。
(—— CS 101)
(—— 收到,當課程 CS 101 有空位時,我們會立刻通知您!)
(—— 好消息,偵察到課程 CS 101 存在空位,我們將不再發送關於此課程的消息。)
你可以使用本文的技術,為各種不同的事情設置通知。例如,有人用 Ruby 和 Twilio 來跟蹤有沒有工藝啤酒在售。若要獲得本文的所有代碼,請參見這個 gist。你也可以通過以下方式聯繫我:
sgt@samueltaylor.org
@SamuelDataT
免責聲明:請務必確保設置通知不會違反你所在大學的學生系統服務條款。如有疑問,請詢問知情者!
看完本文有收穫?請轉
發分享給更多人
關注「P
ython開發者」,提升Python技能
※【Python爬蟲實戰】爬取糗事百科段子
※可能是史上最全的機器學習和Python速查表
※各種相似性度量及Python實現
TAG:Python |
※Electrical Engineering Master 選課指南
※UC Berkeley學姐經驗分享:美高選課要趁早!
※新?考排選課SaaS服務商「?知」獲千萬元Pre-A輪融資 寧澤資本擔任獨家FA
※趙懿:公派留美的魔法少女的選課、Job Fair秘籍——乾貨塞不下、精彩看不夠
※下學期的選課,我打算用AI來幫助決定了
※美國大學的選課形式有哪些?GPA怎麼算?馬上開學了,趕緊進來弄清楚
※學習IB課程,選課之前一定要知道這些問題
※開智新版 App 與小程序上線,開智文化精選課程賀十一
※「選師、選課、走班」,教師培訓新範式,可以實現嗎?| 推薦
※開學季,在韓留學生間必上演的災難大片《選課》!
※北大通選課、公選課表
※體育課選課開啟 你需要這份指南
※大學選課,這些雷區一定不要踩!
※多倫多大學選課小技巧,幫你擺脫選課難題!
※選課走班中的教師:不育人,無法教書
※內師大體育必修課、通識課選課通知
※《大話西遊》成哈佛教材 , 選課學生增2倍, 教授落淚!
※【她 · 教育】留學加拿大的你知道怎麼選課嗎?遵循這幾個原則就好了!
※如何在土豆般的選課系統里鬥智斗勇
※冬季研學表現優異,選課走班優勢凸顯——我校學子吳萌澤同學在美研學期間通過校園面試