腳本目標:
1. 自動爬取pu口袋校園活動,篩選出需要的活動,此處我的篩選條件是線上活動,因為可以不用去就可以白嫖學時
2. 自動發送郵件到QQ郵箱,每次只發送更新的活動,因為QQ郵箱的提醒可以在QQ里直接看到
3. 掛在后台,不顯示控制台
已知條件:
1. pu口袋校園有網頁版,可以直接登錄,在網頁上查看活動,和報名活動,所以可以寫一個網頁爬蟲爬取數據
2. pu口袋校園在查看活動列表的時候不需要cookie就可以看到,如果需要點進活動頁面查看活動的具體內容需要cookie,而活動地點是在具體內容頁面里的,所以需要准備一份cookie
3. 整個網頁沒有反爬機制,不需要准備user-agent
4. 判斷線上活動的方式:活動地點為空
5. 是服務器端渲染的網頁,不需要用selenium
腳本思路:
1. 寫一個簡單的網頁爬蟲,爬取頁面源代碼,提取其中活動的具體內容的url
2. 爬取每個活動具體內容的源代碼,篩選出活動地點為空的內容,將活動名稱添加到列表
3. 將第一次整理出來的列表為初始化的列表,每次更新先和之前的列表比對,篩選出來新的活動,作為一個列表
4. 發送活動名稱到QQ郵箱
代碼實現:
先把需要在后面用到的內容寫在前面
obj = re.compile(".*? <span class=\"b1\">活動地點:</span><a title=\"(?P<area>.*?)\">") obj2 = re.compile(".*?<title>(?P<name>.*?)</title>") header = {"Cookie": "TS_LOGGED_USER=-kowQgrvPgFq4iqGji4hPA5vH; PHPSESSID=51fb2a5a61c010f0465d0; Hm_lvt_dd3ea352543392a029ccf9da1be54a50=1639667636,1639918552,1639977234; TS_think_language=zh-CN; Hm_lpvt_dd3ea352543392a029ccf9da1be54a50=1639994037"}
這個鏈接點開可以得到活動列表頁面https://xxx.pocketuni.net/index.php?app=event&mod=School&act=board&cat=all&&p=1
我選擇爬取前面八頁的活動列表,返回八頁活動列表的鏈接
def get_page_url(): url_list=[] for num in range(1,8+1): url = f"https://xxx.pocketuni.net/index.php?app=event&mod=School&act=board&cat=all&&p={num}" url_list.append(url) return url_list
這個函數用來請求八頁活動列表的頁面源代碼,並且得到每個活動具體內容的網頁地址,用xpath解析,返回具體活動內容的鏈接
def handle(url_list): child_url=[] for url in url_list: resp = requests.get(url) resp.close() resp_tree=etree.HTML(resp.text) child_url.extend(resp_tree.xpath("/html/body/div[2]/div/div[3]/div[2]/div[1]/ul/li/div[2]/div[1]/a/@href")) return child_url
這個函數用來請求所有,活動具體內容的源代碼,並且用正則表達式篩選出活動地點和活動名稱,將活動地點為空所對應的活動名稱添加到active_name_list的列表里,返回符合條件的活動名稱
def handle2(child_urls): x=1 active_name_list=[] for url in child_urls: resp = requests.get(url,headers=header) resp.close() active_area = str(obj.findall(resp.text)).replace("[","").replace("]","").replace("'","").replace(" ","") active_name = str(obj2.findall(resp.text)).replace("[","").replace("]","").replace("'","").replace(" ","") if active_area =='': active_name_list.append(active_name) return active_name_list
比對出新更新的內容
if x==1: #x=1只在程序開始的時候初始化一次,判斷是不是第一次執行程序,如果是就初始化old_list來作為下一次活動的比對條件 old_list = old_list+active_name_list send_email(old_list) x+=1 update_list = [x for x in active_name_list if x not in old_list] #將得到的新列表和old_list比對,將更新的內容保存在update_list並發送郵件 if len(update_list)==0: print("未出現新活動,最近一次更新時間為",time.strftime('%H:%M:%S',time.localtime(time.time()))) else: print("出現新活動,發送")
send_email(update_list)
這個函數用來發送郵件,用自己的163郵箱發送給自己的QQ郵箱,當然自己給自己的郵箱發送也是可以的。
此處授權密碼是隨便寫的,用自己的授權密碼。
關於發送郵件需要注意:自己的郵箱需要開通pop3/SMTP服務,會給一個授權碼,username和password分別寫自己的郵箱和授權密碼,注意開放的端口,端口不正確會導致連接不上郵箱
def send_email(text): smtpsrever = 'smtp.163.com' # 發送郵件的用戶名和密碼 username = 'shui_feng_xxxxx@163.com' password = 'NJXXXXXXXXXNB' # 授權密碼 # 接收郵件的郵箱 receiver = '1609519xxx@qq.com' # 創建郵件對象 message = MIMEMultipart('relate') #生成一個帶附件的郵件對象 message = MIMEText(f'{text}', 'plain', 'utf-8') subject = "第一次測試" # 郵件的主題 # 把郵件的信息組裝到郵件對象里 message['from'] = username message['to'] = receiver message['subject'] = subject # 登錄smtp服務器並發送郵件 smtp = smtplib.SMTP() smtp.connect(smtpsrever) smtp.login(username, password) smtp.sendmail(username, receiver, message.as_string()) smtp.quit()
最后寫一個主函數來執行這一切
if __name__=='__main__': old_list=[] x=1 while True: url_list=get_page_url() child_urls=handle(url_list) active_name_list=handle2(child_urls) if x==1: old_list = old_list+active_name_list send_email(old_list) x+=1 update_list = [x for x in active_name_list if x not in old_list] if len(update_list)==0: print("未出現新活動,最近一次更新時間為",time.strftime('%H:%M:%S',time.localtime(time.time()))) else: print("出現新活動,發送") send_email(update_list) time.sleep(60*60*24) #死循環,每二十四小時執行一次
最終功能代碼
import requests import os import re import time from lxml import etree import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart obj = re.compile(".*? <span class=\"b1\">活動地點:</span><a title=\"(?P<area>.*?)\">") obj2 = re.compile(".*?<title>(?P<name>.*?)</title>") header = {"Cookie": "TS_LOGGED_USER=-kowQgrvPgFq4iqGji4hPA5vH; PHPSESSID=51fb2a5a61c010f0465d0; Hm_lvt_dd3ea352543392a029ccf9da1be54a50=1639667636,1639918552,1639977234; TS_think_language=zh-CN; Hm_lpvt_dd3ea352543392a029ccf9da1be54a50=1639994037"} def handle(url_list): child_url=[] for url in url_list: resp = requests.get(url) resp.close() resp_tree=etree.HTML(resp.text) child_url.extend(resp_tree.xpath("/html/body/div[2]/div/div[3]/div[2]/div[1]/ul/li/div[2]/div[1]/a/@href")) return child_url def handle2(child_urls): x=1 active_name_list=[] for url in child_urls: resp = requests.get(url,headers=header) resp.close() active_area = str(obj.findall(resp.text)).replace("[","").replace("]","").replace("'","").replace(" ","") active_name =str(obj2.findall(resp.text)).replace("[","").replace("]","").replace("'","").replace(" ","") if active_area =='': active_name_list.append(active_name) return active_name_list def send_email(text): smtpsrever = 'smtp.163.com' # 發送郵件的用戶名和密碼 username = 'shui_feng_XXXX@163.com' password = 'NXXXXXXXXXB' # 授權密碼 # 接收郵件的郵箱 receiver = '1609519XXX@qq.com' # 創建郵件對象 message = MIMEMultipart('relate') # 生成一個帶附件的郵件對象 message = MIMEText(f'{text}', 'plain', 'utf-8') subject = "pu口袋校園活動" # 郵件的主題 # 把郵件的信息組裝到郵件對象里 message['from'] = username message['to'] = receiver message['subject'] = subject # 登錄smtp服務器並發送郵件 smtp = smtplib.SMTP() smtp.connect(smtpsrever) smtp.login(username, password) smtp.sendmail(username, receiver, message.as_string()) smtp.quit() def get_page_url(): url_list=[] for num in range(1,8+1): url = f"https://XXX.pocketuni.net/index.php?app=event&mod=School&act=board&cat=all&&p={num}" url_list.append(url) return url_list if __name__=='__main__': old_list=[] x=1 while True: url_list=get_page_url() child_urls=handle(url_list) active_name_list=handle2(child_urls) if x==1: old_list = old_list+active_name_list send_email(old_list) x+=1 update_list = [x for x in active_name_list if x not in old_list] if len(update_list)==0: print("未出現新活動,最近一次更新時間為",time.strftime('%H:%M:%S',time.localtime(time.time()))) else: print("出現新活動,發送") send_email(update_list) time.sleep(60*60*24)
實現后台運行的方法:使用pyinstaller打包軟件時,用 -w 可以不顯示控制台,但是在后台可以在后台看到它正在運行
所以只需要用pyinstaller -F -w pu口袋校園.py 再執行exe文件就可以后台運行程序,或者再加一個開機自啟也可以
實現效果:
且此時桌面沒有控制台窗口的顯示
小腳本完成,按照腳本給的活動名稱報名就可以了,看有的線上活動會顯示掃碼簽到,所以要注意一下,一般無需簽到或者外勤簽到都可以白嫖學時
后記:關於腳本的更改
不同的學校我覺得頁面應該都長差不多,畢竟都是pu口袋校園,只是要把腳本中以下內容改一下
1. 活動列表的頁面url
2. 訪問時的cookie
3. 發送的郵箱,授權密碼,接收的郵箱
4. 根據自己的需要,重寫篩選的條件
ENDING.............