用python模擬登陸(解析cookie + 解析html + 表單提交 + 驗證碼識別 + excel讀寫 + 發送郵件)

    老婆大人每月都要上一個網站上去查數據,而後作報表。html

    爲了減輕老婆大人的工做壓力,因此我決定作個小程序,減輕我老婆的工做量。python

 

準備工做

1.tesseract-ocrweb

    這個工具用來識別驗證碼,很是好用。ubuntu

    ubuntu上安裝:小程序

sudo apt-get install tesseract-ocr

    很是簡單。 服務器

 

2.pytesseract和PIL(pillow)cookie

    pytesseract用來在python中調用tesseract-ocr,PIL(pillow)用來加載圖片,安裝方法以下:併發

pip3 install pytesseract
pip3 install pillow

    也很是簡單。app

    若是安裝pillow的時候報以下錯誤:less

ValueError: zlib is required unless explicitly disabled using --disable-zlib, aborting 

    那麼咱們更新一下pip便可

sudo pip3 install --upgrade pip

    若是pip速度很慢,能夠改用國內的源,在命令後面加上 -i http://pypi.douban.com/simple (百度一下一大把),但pillow好像國內鏡像都沒有,只能用蝸牛速度從自帶的源下載咯...

    一切準備就緒。

 

分析網站

    咱們的目標網址是:http://222.217.19.16:3512/Site/LzsfySite/Default.aspx

 

    預覽圖:

 

 

    看上去很low啊...心疼我老婆....看來我必須快點完成這個小程序了!

    通過簡單的分析能夠獲得關鍵信息:

    1.表單的提交地址:http://222.217.19.16:3512/Site/LzsfySite/Default.aspx

    2.驗證碼地址:http://222.217.19.16:3512/Main/AspCode/ZhuChengXu/AuthenImage.aspx

    3.表單的格式:

 1 {
 2             '__LASTFOCUS' : '',
 3             '__EVENTTARGET' : 'ctl00$ContentPlaceHolder1$Login1$btnLogin',
 4             '__EVENTARGUMENT' : '',
 5             '__VIEWSTATE' : __VIEWSTATE,
 6             '__EVENTVALIDATION' : __EVENTVALIDATION,
 7             'ctl00$ContentPlaceHolder1$Login1$txtUsr' : 用戶名,
 8             'ctl00$ContentPlaceHolder1$Login1$txtPwd' : 用戶密碼,
 9             'ctl00$ContentPlaceHolder1$Login1$txtYZM' : 驗證碼
10 }

        其中四、五、6行是訪問首頁的時候,在首頁的源代碼中返回的參數

        但__EVENTARGUMENT常年爲空,因此乾脆直接寫死空字符串便可;__VIEWSTATE和__EVENTVALIDATION則須要對html進行解析。

        七、八、9則對應用戶名、密碼和驗證碼,用戶名密碼能夠寫死,驗證碼則須要用到tesseract-ocr進行識別。

    4.表單提交的報文頭

 1 {
 2             'Accept' : b'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
 3             'Accept-Encoding' : 'gzip, deflate, lzma',
 4             'Accept-Language' : 'zh-CN,zh;q=0.8',
 5             'Cache-Control' : 'max-age=0',
 6             'Connection' : 'keep-alive',
 7             'Content-Length' : 表單內容長度,
 8             'Content-Type' : 'application/x-www-form-urlencoded',
 9             'Cookie' : cookie內容,
10             'Host' : '222.217.19.16:3512',
11             'Origin' : 'http://222.217.19.16:3512',
12             'Referer' : 'http://222.217.19.16:3512/Site/LzsfySite/Default.aspx',
13             'Upgrade-Insecure-Requests' : '1',
14             'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36 OPR/38.0.2220.41}'
15 }

        其中7行能夠根據構造的表單報問題長度來計算,9行須要從cookie中獲取。

 

主要技術

獲取cookie

    python3中獲取cookie的方式很簡單,用http.cookiejar。

    cookiejar擴展閱讀:https://docs.python.org/3.0/library/http.cookiejar.html

import urllib.request
import urllib.parse
import http.cookiejar

#登陸的主頁面
hosturl = 'http://222.217.19.16:3512/Site/LzsfySite/Default.aspx' #設置一個cookie處理器,它負責從服務器下載cookie到本地,而且在發送請求時帶上本地的cookie
cj = http.cookiejar.LWPCookieJar()
cookie_support = urllib.request.HTTPCookieProcessor(cj)
opener = urllib.request.build_opener(cookie_support, urllib.request.HTTPHandler)
urllib.request.install_opener(opener)

#打開登陸主頁面(目的是從頁面下載cookie,這樣咱們在再送post數據時就有cookie了,不然發送不成功)
hostOpen = urllib.request.urlopen(hosturl)

#解析cookie
cookieText = ''
for item in cj:
    cookieText = cookieText + item.name + '=' + item.value + '&'
cookieText = cookieText[0:-1]

print(cookieText)

    這樣咱們就能夠獲得cookie啦。

 

識別驗證碼

    這個也簡單,咱們先把它下載到本地,而後用pytesseract來解析它:

import urllib.request
import pytesseract
from PIL import Image

#驗證碼圖片地址
checkCodeUrl = 'http://222.217.19.16:3512/Main/AspCode/ZhuChengXu/AuthenImage.aspx'

#下載驗證碼
checkCodeOpen = urllib.request.urlopen(checkCodeUrl)
data = checkCodeOpen.read()
local = open('image.gif', 'wb')
local.write(data)
local.close()

#pytesseract解析
img = Image.open('image.gif')
checkCode = pytesseract.image_to_string(img)

print(checkCode)

    哈哈哈哈就這麼簡單暴力~

 

    誒等等!好像有點不對。咱們多執行幾回,而後對比一下輸出和圖片

    

                      ...出現了英文,什麼鬼...再來

    

            ...此次是正確的。再試試...

                      又不對了。

 

    多試幾回,發現驗證碼的識別率不過高。

    在識別率不高的狀況下,那麼咱們只有開個循環,多識別幾回驗證碼,而後多提交幾回表單便可。——總有一次會正確滴~~

#如下是僞代碼
def 提交方法():
    識別驗證碼
    構造表單
    提交表單
    解析服務器返回報文
    
    if 登陸成功:
        return true
    else:
        return false

while not 提交方法():
    等待1000秒

print('登陸成功啦')

 

解析html 

我這裏用的是python自帶的HTMLParser,這種簡單暴力的辦法很是好用。   ^_^

from html.parser import HTMLParser
import urllib.request

#主頁面  
hosturl = 'http://222.217.19.16:3512/Site/LzsfySite/Default.aspx'

#打開登陸主頁面
hostOpen = urllib.request.urlopen(hosturl)

#解析__VIEWSTATE和__EVENTVALIDATION
#這裏用了HTMLParser的庫。
#自定義的DefaultHTMLParser繼承了HTMLParser
#在調用此類型對象的feed方法對二進制字節流解析時,
#若遇到tag的開始標籤則會觸發handle_starttag方法,
#若遇到tag中的內容時則會觸發handle_data方法
class DefaultHTMLParser(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.hasLogin = False
    
    #若是是input標籤,則判斷其id屬性是不是__VIEWSTATE或__EVENTVALIDATION
    #若是是兩者之一,則在對象.xxxx屬性中存入對應值
    #這裏假定必定可以從中讀取到__VIEWSTATE和__EVENTVALIDATION
    #沒有作錯誤處理
    def handle_starttag(self, tag, attrs):
        iid = ''
        value = ''
        if tag == 'input':
            for attr in attrs:
                if attr[0] == 'id':
                    iid = attr[1]
                    break;
#用exec來設置屬性值,節省代碼量^_^
if iid in ('__VIEWSTATE', '__EVENTVALIDATION'): for attr in attrs: if attr[0] == 'value': exec('self.' + iid + " = attr[1]") def handle_data(self, data): #根據可否找到跳轉語句判斷是否登錄 if data.find('window.location=\'../../Main/AspCode/ZhuChengXu/ShowSelect.aspx\'') != -1: self.hasLogin = True #get方法,用來獲取屬性值。
#這裏偷懶用了eval——eval的效率不過高,但很是省代碼量。
#若是對執行速度要求比較高建議不要用這個方法喔。
def get(self, attr): result = eval('self.' + attr) return result p = DefaultHTMLParser() p.feed(hostOpen.read().decode('GB2312')) print(p.get('__VIEWSTATE')) print(p.get('__EVENTVALIDATION'))

 

提交表單

根據以前的內容,咱們已經獲取了提交登陸表單所須要的一切信息。

因此咱們能夠開始構造一個表單並提交

 1 import zlib
 2 import urllib.request
 3 import urllib.parse
 4 
 5 #表單提交的url  
 6 hosturl = 'http://222.217.19.16:3512/Site/LzsfySite/Default.aspx'
 7 
 8 #構造表單
 9 formData = {
10     '__LASTFOCUS' : '',
11     '__EVENTTARGET' : 'ctl00$ContentPlaceHolder1$Login1$btnLogin',
12     '__EVENTARGUMENT' : '',
13     '__VIEWSTATE' : '__VIEWSTATE',
14     '__EVENTVALIDATION' : '__EVENTVALIDATION',
15     'ctl00$ContentPlaceHolder1$Login1$txtUsr' : '用戶名',
16     'ctl00$ContentPlaceHolder1$Login1$txtPwd' : '密碼',
17     'ctl00$ContentPlaceHolder1$Login1$txtYZM' : 'xxxx'
18 }
19 #對formData進行url編碼
20 formData = urllib.parse.urlencode(formData)
21 
22 #構造登錄用header
23 headers = {
24     'Accept' : b'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
25     'Accept-Encoding' : 'gzip, deflate, lzma',
26     'Accept-Language' : 'zh-CN,zh;q=0.8',
27     'Cache-Control' : 'max-age=0',
28     'Connection' : 'keep-alive',
29     'Content-Length' : len(formData.encode('GB2312')),
30     'Content-Type' : 'application/x-www-form-urlencoded',
31     'Cookie' : 'cookieText',
32     'Host' : '222.217.19.16:3512',
33     'Origin' : 'http://222.217.19.16:3512',
34     'Referer' : 'http://222.217.19.16:3512/Site/LzsfySite/Default.aspx',
35     'Upgrade-Insecure-Requests' : '1',
36     'User-Agent' : 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36 OPR/38.0.2220.41}'
37 }
38 
39 #開始登錄
40 loginRequest = urllib.request.Request(hosturl, formData.encode('GB2312'), headers)
41 loginResponse = urllib.request.urlopen(loginRequest)
42 #返回的數據是壓縮過的,因此要用zlib進行解碼
43 loginResponseData = zlib.decompress( loginResponse.read(), 16+zlib.MAX_WBITS).decode('GB2312')
44 
45 print(loginResponseData)

    須要注意的是,12-17行以及31行這裏要填入前幾節說明的解析的內容。不然服務器會返回500的響應碼喔。

    上述內容基本上涵蓋了作一個爬蟲所須要的知識。

 

擴展內容

    但個人工做還沒完,我還得給我老婆生成一個excel,併發送到她郵箱!

    因此,下面是關於寫excel和發送email的擴展內容,不感興趣的同窗能夠跳過啦。

快捷寫入excel

    咱們能夠先手動作一個有標題,但內容爲空的excel模板,像這樣:

 

    注意,這裏是第四個sheet。而後將其保存爲empty.xls

 

    在這裏我使用python的xlutils對此報表進行寫入。(擴展閱讀:http://xlutils.readthedocs.org/en/latest/

    先安裝。

sudo pip3 install xlutils

    簡單示例:

 1 from xlutils.copy import copy
 2 import xlrd
 3 import xlwt
 4 from xlwt.Style import easyxf
 5 
 6 
 7 #打開文件,formatting_info=true表示讀入單元格style信息
 8 file = xlrd.open_workbook('empty.xls',formatting_info=True)
 9 #用xlutils.copy的copy方法獲取一個報表對象
10 w = copy(file)
11 
12 #定義居中對齊格式示例
13 alignment = xlwt.Alignment()
14 alignment.horz = xlwt.Alignment.HORZ_CENTER
15 style = xlwt.XFStyle()
16 style.alignment = alignment
17 
18 #write方法的第一個參數對應要寫入的行數,第二個參數對應要寫入的列數,兩者都是從0開始計算的
19 #用居中對齊格式寫入第3張sheet的3行7列單元格
20 w.get_sheet(3).write(2,6, '2016 年 7 月 21 日至2016 年 8 月 20 日', style)
21 #用居中對齊格式寫入第3張sheet的16行3列單元格
22 w.get_sheet(3).write(15,2, '2016 年 8 月 21 日' , style)
23 
24 #定義邊框示例
25 borders = xlwt.Borders()
26 borders.left = 1
27 borders.right = 1
28 borders.top = 1
29 borders.bottom = 1
30 style = xlwt.XFStyle()
31 style.borders = borders 
32 style.alignment = alignment
33 
34 #填充數據
35 for i in range(1, 18):
36     w.get_sheet(3).write(9,i,int(100), style)
37     w.get_sheet(3).write(10,i,int(100), style)
38 
39 
40 #寫入公式示例
41 for i in range(1,18):
42     column = chr(ord('A')+i)
43     w.get_sheet(3).write(13, i, xlwt.Formula('SUM(' + column + '10:' + column + '13)'),style)
44 
45 #保存爲新文件
46 w.save('報表.xls')

 

    而後咱們就能夠獲得以下表格啦~~    python真的是很是簡單又暴力...

 

發送帶有附件的email

這個更簡單...smtplib在ubuntu下的python是自帶的。

示例以下:

import smtplib  
from email.mime.multipart import MIMEMultipart  
from email.mime.text import MIMEText  
from email.mime.application import MIMEApplication  

print('準備郵件....')

#qq郵箱用戶名和密碼,自帶星號屏蔽
#必須在帳戶設置開啓smtp服務才能登陸
_user = "27*****68@qq.com"
_pwd  = "***********"
_to   = "10*****09@qq.com"  

#初始化消息
msg = MIMEMultipart()
msg["Subject"] = "2016年9月份統計報表"
msg["From"]= _user  
msg["To"]  = _to  

#這是文字部分
part = MIMEText("詳見附件...")  
msg.attach(part)

#這是附件部分  
part = MIMEApplication(open('報表.xls','rb').read())  
#filename最好設置成英文,不然容易出亂碼
part.add_header('Content-Disposition', 'attachment', filename="baobiao.xls")
msg.attach(part)  

#開始發送
print('from ' + _user + ' to ' + _to + '...')
#必需要用SSL方式加密
smtp = smtplib.SMTP_SSL('smtp.qq.com')
smtp.login(_user, _pwd)
smtp.sendmail(_user, _to, msg.as_string())
smtp.quit()
print('發送完畢')

 

    所作的一切都很是簡單!因此python是世界上最好的語言!     笑....

    綜合上述技術,刪刪改改增增減減,最後成果展現

 

 

    

    最後,感謝我老婆,讓我有學習python的動力。

    本章完。

相關文章
相關標籤/搜索