■ 江蘇 王坤
編者按:在日常的工作中,需要周期性的登錄某些站點獲取任務信息、任務進展狀態、匯總數據及傳送文件至共享服務器等,登錄步驟繁瑣且密碼復雜多樣,登錄成功后需要逐個查看并保存任務,日常操作過程中工作效率不高,需要花費大量時間。本文介紹了利用Python模擬登錄網站后臺并采集數據的程序實現原理及設計方法。
Python是一門非??旖莸墓ぞ哒Z言,類庫非常豐富,在解決工作及生活中各類問題都有很多現成的工具和例子。近年來,Python成為數據科學家們在數據分析、可視化和機器學習方面的語言,它可以帶來高效率和高生產力。利用Python采集數據,在自動登錄環節,需要對驗證碼進行識別,識別正確后,利用POST提交用戶名、密碼信息完成登錄操作。登錄成功后,系統會返回登錄憑證的Cookie信息,利用登錄憑證查找特定數據,如數據存在更新則保存為mht文件。
圖1 數據采集程序功能流程
程序功能如圖1所示,其難點在于如何實現自動登錄及利用登錄憑證抓取特定數據。在自動登錄過程中,由于驗證碼采用了空心化、扭曲、翻轉、干擾線等手段防止程序的自動識別,在特定頁面的訪問上,需要獲取登錄憑證。同時,頁面采取了“反盜鏈”機制,對頁面來源、頁面代理進行嚴格檢測。因此,在程序設計過程中,需要訓練樣本,打造模型,完成對驗證碼的識別并容錯控制,在采集數據過程中,需要 “偽裝”成正常用戶行為,繞過頁面檢測。
1.驗證碼識別技術
設計驗證碼的主要目的是區分人類和計算機,用來防止網絡機器人的一些行為。因此許多人開始研究網絡機器人技術,用來實現郵箱自動注冊、群發信息、自動灌水、自動登錄等功能。目前,各種類型網站系統都利用驗證碼阻止網絡機器人入侵,從而驗證碼識別技術成為研究熱點。
通過批量下載大量驗證碼分析,驗證碼采用了空心化、扭曲、翻轉、干擾線等手段防止程序的自動識別,同時干擾線顏色與驗證碼顏色相近,無法有效去除。驗證碼如圖2所示。
驗證碼采用4位隨機字符,字符采用數字和大寫字母組成,字符位置固定??赏ㄟ^云除噪音、灰度化、二值化后,對字符進行切割,切割后保存到對應文件夾內,如圖3所示。
由于噪音線條和驗證碼字符的顏色相近,去噪難度較大,只能去除黑點和顏色較深的部分。在人工標注好切割后的驗證碼后,利用svm[2]、pca[3]訓練數據,生成pca.model及clf.model文件,最后測試識別成功率在70%左右。
2.登錄獲取會話令牌
Python中cookielib[4]庫(python3中 為 http.cookiejar)為存儲和管理cookie提供客戶端支持,該模塊主要功能是提供可存儲cookie的對象,使用此模塊捕獲cookie并在后續連接請求時重新發送,還可以用來處理包含cookie數據的文件。這個模塊主要提供了這幾個對象:CookieJar,FileCookieJar,MozillaCookieJar,LWPCook ieJar,此處主要用到LWPCookieJar。def login():
loginurl1 = "https://www.fjhh.com/workspace/sys.LoginCtl.depLogin.do"
圖2 驗證碼形態
圖3 切割驗證碼
loginurl2 = "https://www.fjhh.com/workspace/sys.LoginCtl.login.do"
cj = cookielib.LWPCookieJar()
cookie_support = urll ib2.HTTPCookieProcessor(cj)
opener = urllib2.build_opener(cookie_support, urllib2.HTTPHandler)
urllib2.install_opener(opener)
img_req=urllib2.Request("https://www.fjhh.com/workspace/Captcha.jpg")
img_response=opener.open(img_req)
try:
out=open('captcha.jpg','wb')
#print img_response.read()
out.write(img_response.read())
out.flush()out.close()
p r i n t'------------Veri fy------------'
# print 'Get code success'
except IOError:
print 'file wrong'#input code
img_code=captcha("cap tcha.jpg")
print img_code
print 'your code is%s'%img_code
for index,item in enumerate(cj):
tmp = re.compile('Co okie (.*) for').findall(str(item))
cookievalue = tmp[0]
headers = {'User-Agent' : 'Mozilla/5.0(Windows NT 6.1; WOW64;rv:14.0) Gecko/20100101 Firefox/14.0.1',
'Referer' : '******'}postData1 = {
'userType' : 'intern al',
'departmentname' :'fjhh',
'password' :'password-fjhh',
'captcha_key' : img_code
}
postData1 = urllib.urlencode(postData1)
request = urllib2.Request(loginurl1,postData1, headers)
l o g i n _response=opener.open(request)
login_response=login_response.read()
#如果驗證碼出錯,則
if("Verfication code is wrong" in login_response):
print "Fail,Login1 Again"
login()
圖4 程序運行效果圖
print 'login1 success'
運行后如圖4所示。
為了快速準確匹配到特定內容,需要用到正則表達式[5]來實現此功能。正則表達式使用單個字符串來描述、匹配一系列符合某個句法規則的字符串。在很多文本編輯器里,正則表達式通常被用來檢索、替換那些符合某個模式的文本。在Python中利用re模塊來實現正則匹配。在匹配特定內容前,在header頭的User-Agent參數填寫IE客戶端參數,“偽裝”成正常用戶的訪問。
def saveHttpPage(url,cookie,taskID,type_ccc):
url = "https://www.fjhh.com"+url
request = urllib2.Request(url)
request.add_header('User-Agent', 'Mozilla/4.0(compatible; MSIE 8.0;Windows NT 6.1; WOW64;Trident/4.0; SLCC2;.NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C;.NET4.0E)')
request.add_header('Referer','https://www.fjhh.com/workspace/tests.LabSubTasksCtl.listLabTasks.do')
request.add_header('Language', 'zh-CN,zh;q=0.8')
request.add_header('Cookie', cookie)
response = urllib2.urlopen(request)
response_body =response.read()
people_weituo= re.compile('').findall(response_body)
filename =re.compile('appNumber"value="(.*)">').findall(response_body)
applyID = filename[0].decode('utf8')
applyTime_tmp= re.compile('
applyTime ="20"+apply Time_tmp[0].decode('utf 8')
productSortCode_tmp =re.compile('productSortC
ode" value="(.*)">').findall(response_body)
productSortCode=productSortCode_tmp[0].decode('utf8')
獲得特定數據后,先保存為html文件,再調用pywin32庫中的win32com.client將html轉換為mht格式,方便IE瀏覽器用戶的訪問。
1.將html文件轉化為mht格式
def html2mht(filenam e):
filename2 = os.path.join(os.p
ath.abspath('.'),file name)
try:
iMsg = Dispatch('CDO.Message')
iMsg.CreateMHTMLBody('file://
/' + filename2)
iMsg.GetStream().SaveToFile(os.path.splitext(filename2)[0]+'.mht')
#mht文件生成后,刪除html文件,此處需優化os.remove(filename2)
except Exception,e:
print filename+"is error"
2.利用Samba傳輸文件
保存好文件后,利用Samba協議將文件傳輸至文件共享服務器。
conn = SMBConnection(userID, password, client_machine_name, server_name, domain=domain_name, use_ntlm_v2=True,is_direct_tcp=True)
conn.connect(server_ip, 445)
shares = conn.listShares()
remotefilelist = []
for share in shares:
if not share.isSpecial and share.name not in ['NETLOGON','SYSVOL']:
sharedfiles = conn.listPath(share.name,'/')
for sharedfile in sharedfiles:
#
print(sharedfile.filename)
remotefilelist.append(sharedfile.filename)
num = 0
def copy2Remote(file name,):
try:
file = open(filename,'rb')
c o n n .storeFile('share',filename, file) #share mean share name
file.
close()
global num
num = num + 1
print str(num) + " "+ filename + " upload"
except:
pass
def getFileName(path):
f_list = os.listdir(p ath)
for i in f_list:
# os.path.splitext():分離文件名與擴展名
if os.path.splitext(i)[1] == '.mht':
if i.decode("gbk") in remotefilelist:
print "ERROR! %s already exists."%i
else:
copy2Remote(i.decode("g bk"))
getFileName(".")
print str(num) + "files had upload!"
conn.close()