利用Python實現(xiàn)某OA系統(tǒng)的自動定位功能
本文介紹了筆者通過python程序?qū)崿F(xiàn)某OA系統(tǒng)自動考勤打卡功能及相關(guān)邏輯原理的解析。
Github: https://github.com/cahi1l1yn/eChecker
需求分析
疫情期間,筆者所在公司使用某OA系統(tǒng)的考勤功能代替原來的刷臉考勤,結(jié)果導(dǎo)致很多人經(jīng)常忘記打卡,于是筆者尋思著能不能寫個程序?qū)崿F(xiàn)自動考勤,希望實現(xiàn)的主要功能是:指定用戶名密碼登錄和指定時間簽到簽退,擴展功能是:自定義簽到和簽退的IP或定位地址。
系統(tǒng)邏輯分析
為了通過python實現(xiàn)上述功能,首先需要人工訪問系統(tǒng)進行相關(guān)的操作,并抓包分析請求和返回數(shù)據(jù),弄清邏輯原理,下面介紹分析過程:
登錄
訪問OA系統(tǒng)登錄頁面,點擊輸入登錄信息后截取登錄數(shù)據(jù)包,分析發(fā)現(xiàn)登錄接口除了驗證用戶名和密碼外,還會驗證下圖紅框所示的cookie和token參數(shù)。因此我們需要找到這兩個參數(shù)值從哪里獲取。

重新訪問登錄頁面并抓取返回包,首先從返回包頭部看到了JSESSIONID參數(shù),而另一個lt參數(shù)則在返回頁面的源碼中。


弄清楚這兩個參數(shù)的來源后,我們重新回到登錄頁面提交登錄請求,獲取并記錄下會話cookie。
考勤
登錄賬號后,進入考勤模塊進行打工并截取數(shù)據(jù)包,可以看出程序是通過向考勤接口提交參數(shù)值為CHECKIN和CHECKOUT的json字符串以實現(xiàn)簽到和簽退。

另外可以看到請求包中攜帶了好幾個cookie參數(shù),經(jīng)過不斷的測試排除后,最終確定WEBID、JSESSIONID和ETEAMSID這三個為關(guān)鍵cookie,其余幾個都可以忽略。
自定義考勤地址
上述測試過程是PC端的,由于其中并沒有涉及到地址的參數(shù),因此轉(zhuǎn)到APP端進行測試。截取APP端的考勤請求包,可以看到checkaddress參數(shù)就是考勤定位地址。

筆者嘗試在PC端的考勤請求參數(shù)中插入checkaddress,從響應(yīng)包中可以看出已經(jīng)成功使用該參數(shù)自定義考勤地址進行考勤,同時這里如果再加入經(jīng)緯度參數(shù)的話,即可高度模擬定位考勤。

值得關(guān)注的是,筆者分析發(fā)現(xiàn)當考勤請求攜帶了PC端UA時,服務(wù)端會將客戶端識別為PC端,此時不會處理checkaddress參數(shù),簽到地址就是客戶端的真實IP地址。當考勤請求攜帶移動端UA或者pythonUA時,服務(wù)端會將客戶端識別為移動端且處理checkaddress參數(shù),此時就可以實現(xiàn)自定義考勤地址,包括IP地址和地理位置。
邏輯梳理
通過上述操作后,筆者已經(jīng)了解到登錄接口和考勤接口的邏輯和請求形式,下面簡單梳理相關(guān)流程,這個流程也就是后續(xù)編寫程序主要的邏輯依據(jù):
1.【用戶訪問登錄頁面】
||
\/
2.【登錄頁面返回一個cookie(JSESSIONID)和token(lt)】
||
\/
3.【用戶攜帶cookie像登錄接口提交token、用戶名和密碼】
||
\/
4.【登錄接口驗證成功后返回會話cookie(ETEAMSID\JSESSIONID\)】
||
\/
5.【用戶攜帶會話cookie向考勤接口提交簽到/簽退請求】
功能實現(xiàn)
這里先回顧一下本程序?qū)崿F(xiàn)需求是:指定用戶名密碼登錄和指定時間簽到簽退。通過上述邏輯梳理,已經(jīng)可以實現(xiàn)指定用戶和密碼登錄已經(jīng)簽到簽退,另外還需要實現(xiàn)的就是指定時間,下面我們加入指定時間相關(guān)的功能再次梳理python程序的主要功能邏輯:
1.【輸入用戶名、密碼、簽到簽退時間運行程序】
||
\/
2.【登錄系統(tǒng)獲取會話cookie】
||
\/
3.【程序獲取本地時間】
||
\/
4.【程序比對本地時間和用戶設(shè)定時間】
||
\/
5.【在指定時間攜帶會話cookie進行考勤】
程序結(jié)構(gòu)
梳理出程序主要功能邏輯后,開始定義函數(shù)分別實現(xiàn)上述主要功能,下面列出程序的主要函數(shù)結(jié)構(gòu):
def get_cookie(user,passwd):登錄系統(tǒng),獲取會話cookie,該函數(shù)實現(xiàn)了[邏輯梳理]中的第2-4步 def keep_session():維持會話cookie有效性,因cookie長期不活躍會失效,因此通過此函數(shù)訪問系統(tǒng)以維持cookie,如果cookie已經(jīng)失效,則會調(diào)用get_cookie函數(shù)重新登錄獲取cookie def check_in():簽到模塊,攜帶cookie向考勤接口提交CHECKIN def check_out():簽退模塊,攜帶cookie向考勤接口提交CHECKOUT def get_position():定位模塊,根據(jù)用戶輸入的地理位置獲取經(jīng)緯度 def check_time():獲取本地時間并于用戶設(shè)定時間作比對,觸發(fā)考勤模塊和會話維持模塊 def main():程序入口函數(shù),獲取用戶輸入
代碼解析
通過上面的介紹,我們已經(jīng)大概了解整個程序的運行邏輯,下面對部分關(guān)鍵代碼進行解析(部分常規(guī)代碼有省略):
def get_cookie(user,passwd):
...........
token = re.search(r'LT\S+cn',html).group()
#urllib訪問登錄頁面后,從頁面中獲取lt參數(shù)值,即token
pcookie = re.search(r'JSESSIONID=\S+',str(pres.info().headers)).group()
#訪問登錄頁面后,從返回包頭部中獲取cookie,后續(xù)提交登錄請求時需要攜帶該cookie
data ='lt='+token+'&execution=e1.2&j_pcClient=&_eventId=submit&isApplyed=false®isterSourceUrl=®isterSource=®isterDataSource=&username='+user+'&password='+passwd
#組合token和用戶輸入的登錄信息,用于組成登錄請求
req = urllib2.Request(lurl)
cj = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
#通過cookiejar記錄登錄成功后頁面返回的會話cookie
opener.addheaders = [('Cookie',pcookie)]
#在登錄請求包頭中加入一開始獲取到的cookie
try:
res = opener.open(lurl,data=data,timeout=10)
except urllib2.URLError:
print '[ERROR]Urlllib error, retry later'
try:
cookie = re.search(r'ETEAMSID=\w+',str(cj)).group()+';'+re.search(r'JSESSIONID=\w+',str(cj)).group()+';'+re.search(r'WEBID=\w+',str(cj)).group()
#到這一步已經(jīng)登錄成功,cookiejar已經(jīng)記錄了會話cookie,記錄形式是這樣的:
<CookieJar[<Cookie BIDUPSID=B681378758CB3586029EBFFFF16FBDE2 for .baidu.com/>, <Cookie PSTM=1532404690 for .baidu.com/>, <Cookie BD_NOT_HTTPS=1 for www.baidu.com/>]>
因此這里利用正則匹配出我們所需要的3個cookie值
print '[INFO]Login succeed, your cookie is:'+cookie
...........
def check_in():
............
req = urllib2.Request(curl)
req.add_header("Cookie",cookie)
req.add_header("Content-Type","application/json")
if stat == '0':
data = json.dumps({"type":"CHECKOUT","checkAddress":addr,"longitude":longi,'latitude':lati})
#當用戶自定義了考勤地址時,且成功獲取到經(jīng)緯度信息時,提交的請求中加入了地理位置和經(jīng)緯度參數(shù),服務(wù)端默認將urllib的UA識別為移動端,故會記錄用戶提交的地理信息,完美模擬定位考勤效果
elif stat == '1':
data = json.dumps({"type":"CHECKOUT","checkAddress":addr})
#當用戶自定義了考勤地址時,但未成功獲取到經(jīng)緯度信息時,提交的請求中只加入地理位
elif stat =='2':
req.add_header('User-Agent',ua)
data = json.dumps({"type":"CHECKOUT"})
#當用戶未自定義考勤地址時,提交的請求按PC端原始格式,且此處需要加入自定義的PC端UA,否則服務(wù)端會將簽到地址記錄為空值
try:
res = urllib2.urlopen(req,data=data,timeout=5).read()
smsg = res.find('簽到成功')
fmsg = res.find('簽到失敗')
if smsg > -1:
print '[INFO]'+time.strftime('%Y-%m-%d_%H:%M',time.localtime())+' Checkin succeed'
elif fmsg > -1:
print '[WARNING]'+time.strftime('%Y-%m-%d_%H:%M',time.localtime())+' Checkin fail:'+res
#以上代碼通過在返回報文中查找成功和失敗的字符,作為考勤是否成功的判斷依據(jù),并輸出到終端提示用戶
..........
def check_time():
while True:
ltime = time.strftime('%H:%M',time.localtime()).lstrip('0')
day = time.strftime('%a',time.localtime())
#獲取當前的時間,lstrip去0是為了時針為0-9的個位數(shù)時進行格式統(tǒng)一
..........
if ltime == '4:30':
keep_session()
time.sleep(60)
#由于會話cookie在一定時間后(貌似是十幾個小時)會失效,因此設(shè)定在凌晨調(diào)用keepsession()維持cookie
elif ltime == intime.lstrip('0') and day not in ('Sat','Sun'):
#比對本地時間與用戶輸入時間,且判斷是否周末
keep_session()
#進行考勤前,再次檢驗cookie是否有效
rnd = random.randint(0,600)
print '[INFO]Checkin after ' + str(int(rnd)/60) + ' Min ' + str(int(rnd)%60) + ' Sec'
time.sleep(int(rnd))
check_in()
#為了避免用戶設(shè)定一個時間后,程序每天都在同一時間點考勤,這里結(jié)合sleep和random實現(xiàn)在用戶設(shè)定時間上正向浮動隨機時間進行考勤
time.sleep(60)
........
........
check_time()
def get_position(addr):
global longi
global lati
url = 'http://api.map.baidu.com/geocoding/v3/?address='+addr+'&output=json&ak='+api_key+'&callback=showLocation'
html = urllib2.urlopen(url.encode('utf-8')).read()
longi = re.search(r'lng":\d+.\d+',html).group().lstrip('lng":')
lati = re.search(r'lat":\d+.\d+',html).group().lstrip('lat":')
#調(diào)用百度地圖API獲取經(jīng)緯度信息,使用encode('utf-8')處理url可以避免中文亂碼問題(需要注冊APIKEY)
運行效果



總結(jié)
本文分享了筆者利用python編寫某OA系統(tǒng)自動考勤程序的過程,包括對系統(tǒng)邏輯的分析、程序結(jié)構(gòu)的介紹和關(guān)鍵代碼的解析等內(nèi)容。
程序最終實現(xiàn)了用戶自定義考勤時間、地址,并自動根據(jù)地址獲取經(jīng)緯度(如地址為IP地址則不獲取),每天在指定時間以上述自定義信息進行考勤。
注:考勤地址可自定義的漏洞已經(jīng)上報。
到此這篇關(guān)于利用Python實現(xiàn)某OA系統(tǒng)的自動定位功能的文章就介紹到這了,更多相關(guān)python實現(xiàn)OA系統(tǒng)自動定位內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
用Python將mysql數(shù)據(jù)導(dǎo)出成json的方法
今天小編就為大家分享一篇用Python將mysql數(shù)據(jù)導(dǎo)出成json的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-08-08
人臉識別經(jīng)典算法一 特征臉方法(Eigenface)
這篇文章主要為大家詳細介紹了人臉識別經(jīng)典算法一,特征臉方法Eigenface,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-03-03
Python中內(nèi)建模塊collections如何使用
在本篇內(nèi)容里小編給大家整理的是關(guān)于Python中內(nèi)建模塊collections的用法,有需要的朋友們可以參考下。2020-05-05
python繪制規(guī)則網(wǎng)絡(luò)圖形實例
今天小編大家分享一篇python繪制規(guī)則網(wǎng)絡(luò)圖形實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-12-12

