如何使用Python實現易班自動報送

前言

emmm,我是那位上牆的“巧克力微微甜”,讓大家見笑了QwQ。完整的源碼在文章的最後,歡迎交流。本人蒟蒻,還望大佬輕噴···

整體思路

我們知道很多app的一些頁面其實都是網頁,從“分享”功能裏提供了“複製鏈接”這個選項,可以看出來易班報送頁面其實也是一個網頁。而且這個網頁還是唯一對應每個賬戶,也就是說有了這個網址連賬號密碼都不需要就能幫別人完成填報。
學過一點爬蟲會知道python可以使用selenium+chromedriver模擬人對瀏覽器進行操作(其實用Request應該也是可以的,不過我不太會用)。
於是我們把這個鏈接搗鼓到電腦上,寫個代碼讓瀏覽器自動訪問填報頁面並且完成填報。
如果你還不會selenium的話,可以出門左轉學習一波。

以上是我代碼實現的思路,除了這個思路以外還可以直接在手機上對app進行操作,這個需要用到Appium。具體實現我沒嘗試過。不會Appinum的可以點我(可以用來爬取你微信的朋友圈,爬啥都行)

最後一種思路是一位大佬和我交流過程中提到了adb,不過我不會adb,應該也是可以實現的。

下面開始正文

報送頁面無法用網頁打開?!Fiddler抓包攻破易班防線

到這一步我們手上已經有鏈接了(ios是無法成功拿到鏈接的···QAQ)。先用瀏覽器打開它試試。咦?一打開就跳到了易班首頁。
在這裏插入圖片描述
這是user-agent在作祟,一個表明訪問環境的字段。易班網頁通過它判斷環境是否爲易班app,不是就拒絕訪問。因此我們必須想辦法獲取易班使用的user-agent,然後用它僞裝自己的瀏覽器,假裝自己是易班app。

可以想到使用抓包的辦法,這裏我用了Fiddler對手機進行抓包。
學習Fiddler戳這
抓到包以後隨意打開其中一個,在請求頭裏就可以看到user-agent了在這裏插入圖片描述
然後打開chrome瀏覽器,配置一個新的user-agent,命名爲易班,粘貼進搞到手的那一串,再用它打開網頁就可以進入了。
在這裏插入圖片描述
接下來用selenium在瀏覽器裏一通操作就可以完成報送了。
然後讓電腦每天定點自動執行程序就可以了
Windows用任務計劃程序,Linux用crontab命令。
這裏我是掛載在我的阿里雲服務器上(Windows Server)

改進升級你的自動報送程序

如何實現不同的賬戶的自動簽到

這個簡單,把每個人的填報信息放進一個文件裏,寫個文件操作就可以了。

增強魯棒性,應對已經報送過的用戶

有時候你的用戶/基友/閨蜜可能哪天突然早起或者想到了要報送,於是自己報上了,如果不考慮這個情況程序就會被卡住。在填報第一個項目的時候用上try語句就可以了。

如何提高報送效率,應對高用戶量

考慮到工具用戶量有可能很大(你看我做的白日夢多香),用原本的辦法填報一個人經計算平均需要35s,那麼一小時只能填報120人(然鵝現在只有不到10人用)
因爲是網絡請求操作,很多時間其實花費在等待網頁的加載上,因此用上多線程。i7-7700HQ,8GB的電腦經過測試5~8線程差不多是最高效率,再多反而更慢,報送效率提升到平均一分鐘15人,也就是一個人均攤4s。

如何發送提醒郵件

一開始我用selenium操作瀏覽器打開QQ郵箱網頁版進行操作,會遇到的問題是驗證碼,雖然也可以解決(看這裏),但是我還是改用了SMTP發送郵件的方式。

完整源碼

import os
import sys
import smtplib
from email.mime.text import MIMEText
from email.utils import formataddr
from time import sleep, time
from selenium import webdriver
from threading import Thread, Lock
from random import randrange, shuffle
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains


class Info(object):
    def __init__(self, url, sel1, sel2, sel3, email, low, high):
        self.url = url
        self.sel1 = sel1
        self.sel2 = sel2
        self.sel3 = sel3
        self.email = email
        self.low = low
        self.high = high


def mail(my_user):
    try:
        msg = MIMEText(
            '易班報送成功。', 'plain', 'utf-8')
        msg["Accept-Language"] = "zh-CN"
        msg["Accept-Charset"] = "ISO-8859-1,utf-8"
        # 發件人郵箱暱稱、發件人郵箱賬號
        msg['From'] = formataddr(["Auto5", my_sender])
        # 收件人郵箱暱稱、收件人郵箱賬號
        msg['To'] = formataddr(["Auto5User", my_user])
        msg['Subject'] = "易班自動報送成功"                # 郵件的標題
        # print(msg.as_string())

        # 括號中對應的是發件人郵箱賬號、收件人郵箱賬號、發送郵件
        server.sendmail(my_sender, [my_user, ], msg.as_string())

    except Exception as e:
        print(e)


def select_element(driver, element_idx=1, selection_idx=0):
    driver.find_element_by_xpath(
        '//*[@id="WFForm"]/div[2]/div/div[' +
        str(element_idx)+']/div/div[1]').click()
    sleep(1)
    dragger = driver.find_element_by_xpath(
        '/html/body/div[2]/div/div[2]/div/div/div/div/div[2]/div/div[2]')
    ActionChains(driver).drag_and_drop_by_offset(
        dragger, 0, -50*selection_idx).perform()
    sleep(1)
    driver.find_element_by_xpath(
        '/html/body/div[2]/div/div[2]/div/div/div/div/div[1]/div[3]').click()
    sleep(1)


def random_temperature(low=3, high=8):
    return "36." + str(randrange(low, high, 1))


def operate(infos=[]):
    user_count = len(infos)
    if user_count == 0:
        return
    # 隨機順序
    shuffle(infos)
    # 隨機延時上下限計算
    # 計算公式 最長時間=(3600-100*用戶數)/(用戶數)
    # 計算公式 最短時間=最長時間/2
    most = (3600-100*user_count)/user_count
    least = most/2
    for idx in range(len(infos)):
        # mail(infos[idx].email)
        # 隨機延時
        sleep_time = randrange(least, most)
        print(sleep_time)
        sleep(sleep_time)
        # 進行操作
        options = webdriver.ChromeOptions()
        options.add_argument(
            'user-agent="Mozilla/5.0 (Linux Android 8.0.0 MIX 2 Build/OPR1.170623.027 wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/71.0.3578.99 Mobile Safari/537.36 yiban_android"')
        driver = webdriver.Chrome(chrome_options=options)
        driver.maximize_window()
        driver.get(infos[idx].url)
        sleep(1)
        # 點擊任務
        driver.find_element_by_xpath(
            '//*[@id="root"]/div[3]/div[1]/div[3]').click()
        sleep(1)
        # 點擊第一個信息採集欄目
        driver.find_element_by_xpath(
            '//*[@id = "root"]/div/div[1]/div[1]/div/div/div[1]/div').click()
        sleep(1)
        # 點擊反饋
        driver.find_element_by_xpath(
            '//*[@id = "root"]/div/footer/a').click()
        sleep(1)
        # 以下填報各項信息
        # 打開第一個信息欄目
        try:
            select_element(driver, 1, infos[idx].sel1)
        except Exception as e:
            print("Fail")
            print(e)
        else:
            select_element(driver, 2, infos[idx].sel2)
            select_element(driver, 3, infos[idx].sel3)
            driver.find_element_by_xpath(
                '//*[@id="WFForm"]/div[2]/div/div[4]/div/div[2]/input').send_keys(random_temperature(infos[idx].low, infos[idx].high))
            sleep(1)
            # 提交
            driver.find_element_by_xpath(
                '//*[@id = "root"]/div/a').click()
            # 發送提示郵件
            mail(infos[idx].email)
        finally:
            # print(infos[idx].email)
            # mail(infos[idx].email)
            # 關閉瀏覽器
            sleep(1)
            driver.close()
    driver.quit()


my_sender = ''    # 發件人郵箱賬號
my_pass = ''       # 發件人郵箱密碼
# 登錄郵箱
server = smtplib.SMTP_SSL("smtp.qq.com", 465)  # 用發件人郵箱的SMTP服務器地址和端口創建一個新的連接實例
server.login(my_sender, my_pass)  # 登錄
# 獲取絕對路徑並打開數據文件
path = os.path.abspath(os.path.dirname(sys.argv[0]))
fp = open(path + "/test.dat", "r")
# 線程數設定
# 當前用戶數較少設定爲1
# TODO 自適應線程數功能
thread_num = 1
# thread_num = int(input("thread_num = "))
# 根據線程數設定建立對應數量的list存放task
thread_tasks = []
for _ in range(thread_num):
    thread_tasks.append([])
# 從文件讀取數據並加入list
now_thread = -1
while True:
    now_thread += 1
    # 讀取用戶易班鏈接
    url = fp.readline()
    # 檢測文件是否已經結束
    if url == "":
        break
    # 讀取用戶三個選項的選擇
    sel1 = int(fp.readline())
    sel2 = int(fp.readline())
    sel3 = int(fp.readline())
    # 讀取用戶郵箱地址並去掉末尾的\n\r
    # 如果不去掉換行符會導致郵件內容亂碼
    email = fp.readline()
    email = email.replace('\n', '').replace('\r', '')
    # 讀入用戶設定的最低和最高體溫
    low = int(fp.readline())
    high = int(fp.readline())
    # 將當前用戶分配給線程
    thread_tasks[now_thread % thread_num].append(
        Info(url, sel1, sel2, sel3, email, low, high))
# 讀取完畢,關閉文件
fp.close()
# 開始多線程操作
threads = []
# start = time()
for now_tasks in thread_tasks:
    t = Thread(target=operate, args=(now_tasks,))
    threads.append(t)
    t.start()
    sleep(0.5)
# 等待所有線程執行完畢
for t in threads:
    t.join()
# end = time()
# print('總共耗費了%.2f秒.' % (end - start))

# 向自己發送提示郵件,表明程序正常運行結束,已經完成所有用戶的簽到。
mail('')
# 關閉郵箱連接
server.quit()

缺陷

對網頁的依賴性

網頁源代碼一旦改變工具就會失效,必須馬上更新,沒得辦法的硬傷。

ios無法獲取填報頁面鏈接

我也好無奈

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