wepy微信機器人:向好友推送爬取的公告

寫在前面

做的什麼

1.爬取學院和教務處公告

  1. 使用requests庫發起get請求獲得目標HTML。
  2. 使用bs4用來解析HTML獲取公告標題和鏈接。

2.開發微信機器人向好友和自己推送標題和鏈接

  1. 好友微信內自主訂閱通知,以此獲取到一個好友列表
  2. 遍歷列表,向好友推消息

一些tips

微信機器人和QQ機器人開發類似,有對應的py庫,但是今年早些時候騰訊停止了WebQQ服務。所以正規的QQ機器人就沒法開發了,但是聽說也有一些其它的渠道可以開發QQ機器人,不過既然官方都停止維護了,就沒有再去深入瞭解。
所以就轉向了微信機器人,有幾點很頭疼:

  1. 想搞個小號開發,但微信申請新的賬號不像QQ那麼容易
  2. 我申請完了發現小號登不上Web微信!查了一下好像說新申請的號一年內不讓登陸
  3. 問了好多渣男有沒有不用的微信小號而且能登陸網頁微信的,沒結果。我一想:有也不會給啊,人喫飯的工具啊。

折騰了一陣子最後還是用自己的號做開發,有點小煩。
建議先快速瀏覽一下wepy官方中文文檔,對於這個庫能做什麼、該怎麼做應該會有一個大概的認識。
大概瞭解後,後面開發的時候根據自己的需求對應地去找相應的API就好了。
下面是一個最簡單地示例,向你暱稱爲“XX小舟”的好友發送’Hello WeChat!’。

from wxpy import *
# 初始化機器人,掃碼登陸
bot = Bot()
my_friend = bot.friends().search('XX小舟', sex=MALE)[0]#返回的是一個列表,所以加上[0]
my_friend.send('Hello WeChat!')

1.爬蟲部分

分出去寫了,一是因爲內容比較獨立,二是加進來總體篇幅太長。
鏈接:py爬蟲爬取學校通知公告

2.微信機器人轉發消息

上面部分說到了如何判斷通知是最新的。
用的方法是: 實時獲取當天的年月日,與網站內通告的日期比對,相等則一定是最新消息,爬到一條後就把它的鏈接添加到一個“已抓取”列表中,再次抓取的時候先判斷通知的鏈接是否在列表內,在則是當日已抓取信息;不在內則是當日新信息,到了第二天則清空“已抓取”列表。

2.1機器人需求以及實現過程

我的微信機器人要達到的目標很簡單:就是給指定好友發送消息。所以其實很多功能都沒用到,比如說對羣聊的監控、自動拉人入羣以及圖靈機器人在線陪聊等等。
所以我的開發路線也很明確,根據對應想要的功能在前面給的官方文檔中直接去找API。開發其它功能也是同樣的思路,雖然說網上關於微信機器人開發的博客還挺多的,也包括我這篇。但是別人的博客也可能是對應實現某個功能,可能不是你自己想要的,這樣去找、去看博客其實效率很低。遇到不確定的地方還是得去查官方文檔。

2.2向好友發送推送

2.2.1得到一個好友列表

如果每次都是通過如下的方式去尋找好友再發消息出去,就很傻。

my_friend = bot.friends().search('XX小舟', sex=MALE)[0]

所以我搞了一個“訂閱”:好友主動訂閱通知,由此將好友分別加入兩個通知的兩個列表。大致效果如下
在這裏插入圖片描述
代碼如下,其實可以簡單的理解成一個自動回覆的例子:

@bot.register(Friend, TEXT)# 規定響應好友發來的“TEXT”類型的msg
def service(msg):
    print(msg.text)
    if '通知' in msg.text:
        msg.reply_msg("你想訂閱哪些網站通知?\n 請回復標題,如:1,2或12\n1.機自學院通告\n2.教務處通告\n注:訂閱成功後可回覆TD取消訂閱")
    if '1' in msg.text:
        if msg.sender in friends_school:
            msg.reply_msg("您已經成功訂閱機自學院通告,無需再次操作")
        else:
            friends_school.append(msg.sender)
            msg.reply_msg("成功訂閱機自學院通告")
    if '2' in msg.text:
        if msg.sender in friends_teaching:
            msg.reply_msg("您已經成功訂閱教務處通告,無需再次操作")
        else:
            friends_teaching.append(msg.sender)
            msg.reply_msg("成功訂閱教務處通告")
    if "TD" in msg.text:
        if msg.sender in friends_school and msg.sender in friends_teaching:
            friends_school.remove(msg.sender)
            friends_teaching.remove(msg.sender)
            msg.reply_msg("成功退訂學院、教務處通告")
        elif msg.sender in friends_school:
            friends_school.remove(msg.sender)
            msg.reply_msg("成功退訂學院通告")
        elif msg.sender in friends_teaching:
            friends_teaching.remove(msg.sender)
            msg.reply_msg("成功退訂教務處通告")
        else:
            msg.reply_msg("您還未訂閱")

2.2.2遍歷好友列表推送消息

如此就得到了兩個列表friends_school和friends_teaching,分別存放了訂閱兩個網站通知的好友。發送消息時,遍歷列表,向好友推送消息。

for eachFriend in friends_school:
    eachFriend.send("學院有新通知,標題:“" + each_text + "”")
    eachFriend.send("鏈接:" + real_href)

如果爬到新消息,就推送出去,效果如下
在這裏插入圖片描述

3.微信機器人代碼

3.1結構

前面一些全局變量沒有截圖進來,佔位置。
在這裏插入圖片描述

3.2主程序

因爲涉及到爬蟲,所以寫了個while一直重複執行,每次循環最後sleep 5秒。

while 1:
	# 獲取當天時間,格式與網頁內相同,Y-M-D
    timeGet = time.strftime('%Y-%m-%d', time.localtime())
    if timeGet == today:
        pass
    else:
    	# 如果到達明天,today重新賦值,並且清空“已爬取網頁列表”
        today = timeGet
        exception = []
    # print(today)
    # 學院通告
    get_school()# 爬取學院公告併發給好友
    # 教務處通告
    get_teaching()# 爬取教務處公告併發給好友
    print("##########################")
    print("已爬取名單:")
    print(exception)# 已爬取的通知列表
    print("學院名單:")
    print(friends_school)
    print("教務處名單:")
    print(friends_teaching)
    time.sleep(5)

3.3get_school()函數內部

get_teaching就不放了,兩者幾乎一樣。

def get_school():
    url = 'http://www.auto.shu.edu.cn/synr/tzgg.htm'
    # 模擬瀏覽器發送HTTP請求
    header = {'User-Agent': 'Mozilla/5.0'}
    try:
        response = requests.get(url, headers=header)
        response.raise_for_status()
        response.encoding = response.apparent_encoding
        html = response.text
        soup = BeautifulSoup(html, "html.parser")
    except:
        print("爬取失敗了,朋友")
        mail("報錯", "爬蟲失敗了")#這個mail函數文中沒講,是給我自己發送郵件,參數爲主題+正文
        return
    target = soup.find_all("span", string=today)
    for eachOne in target:
    	# 得到鏈接和標題
        each_text = eachOne.parent.parent.td.a.text
        each_href = eachOne.parent.parent.td.a.get("href")
        if each_href in exception:
            pass
        else:
            # 沒在“已爬取列表”內的話,發送鏈接、消息給好友
            real_href = "http://www.auto.shu.edu.cn/" + each_href[3:]# 處理一下鏈接
            print(real_href)
            print(each_text)
            # 給我自己發郵件通知
            mail("學院有新通知", "標題:" + each_text + "\n鏈接:" + real_href)
            for eachFriend in friends_school:
                eachFriend.send("學院有新通知,標題:“" + each_text + "”")
                eachFriend.send("鏈接:" + real_href)
            # 加入“已爬取列表”
            exception.append(each_href)

3.4小補充,關於@bot.register()

爲了保持登陸,官方文檔這麼說的(截圖):
在這裏插入圖片描述
因爲我程序裏寫了while,所以就不需要堵塞線程,程序不會結束。但是突然一想,我的兩個@bot.register()註冊的函數都在while外面,能被執行到嗎?
調試證明,這兩個@bot.register()註冊的函數都能被執行,所以我推斷它的本質是回調函數。

2019/11/19補充

以前用戶信息放在一列表內,一旦重啓用戶信息就沒了,所以寫了數據庫保存用戶信息,記錄了亂碼問題。
py操作MySQL以及解決亂碼問題的記錄
並且把代碼上傳了:spider

寫在後面

記錄學習,歡迎交流,多多指教

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章