用python模拟登录(解析cookie + 解析html + 表单提交 + 验证码识别 + excel读写 + 发送邮件)...

news/2024/7/19 12:38:14 标签: python, 爬虫, xhtml

    老婆大人每个月都要上一个网站上去查数据,然后做报表。

    为了减轻老婆大人的工作压力,所以我决定做个小程序,减轻我老婆的工作量。

 

准备工作

1.tesseract-ocr

    这个工具用来识别验证码,非常好用。

    ubuntu上安装:

sudo apt-get install tesseract-ocr

    非常简单。 

 

2.pytesseract和PIL(pillow)

    pytesseract用来在python中调用tesseract-ocr,PIL(pillow)用来加载图片,安装方法如下:

pip3 install pytesseract
pip3 install pillow

    也非常简单。

    如果安装pillow的时候报如下错误:

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 }

        其中4、5、6行是访问首页的时候,在首页的源代码中返回的参数

        但__EVENTARGUMENT常年为空,所以干脆直接写死空字符串即可;__VIEWSTATE和__EVENTVALIDATION则需要对html进行解析。

        7、8、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的动力。

    本章完。

转载于:https://www.cnblogs.com/prpl/p/5901423.html


http://www.niftyadmin.cn/n/1157309.html

相关文章

ubuntu8.04+skyeye1.2.4搭建linux2.6.24+s3c2410的模拟arm-linux开发环境

一,准备工具:交叉编译工具:[url]http://www.handhelds.org/download/projects/toolchain/arm-linux-gcc-3.4.1.tar.bz2[/url] linux2.6.24的内核(ubuntu8.04):[url]http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.24.tar…

微信小程序开发文档官方版发布

微信公众平台团队喜欢在深夜给人惊喜,这不,微信小程序开发文档昨晚23点半发出了,很多开发者表示要兴奋得睡不着了。和ytkah一起看看小程序文档吧!小程序文档包含:小程序简易教程、小程序框架、小程序组件、小程序API、…

linux中文件d属性是什么,Linux中文件属性第一个字符“d表示”____文件。

根据以下材料,回答下列各题: Questions 62 to 66 are based on the following passage.??????? Researchers in the field of psychology have found that one of the best ways to make an important decision, such as choosing a university t…

WAP网页设计入门(转)

WAP网页设计入门 1.基本规则 2.基本格式和文件头 3.显示文本 4.显示图片 5.锚和任务 6.显示表单 7.事件 8.CGI编程   这里介绍WAP协议标准组织所制定的标记语言WML,由…

Linux php调用ffmpeg,linux怎么安装ffmpeg-php扩展

安装方法:1、下载ffmpeg安装包,使用tar命令进行解压;2、使用make命令编译安装ffmpeg;3、3、载ffmpeg-php安装包,使用tar命令进行解压;4、使用make命令编译安装ffmpeg-php即可。本教程操作环境:R…

Spring 中拦截器与过滤器的区别

spring 中拦截器 与servlet 的filter 有相似之处.比如二者都是aop 编程思想的体现都能实现权限检查,日志记录等. 不同之处 使用范围不同 Filter 是Servlet 规定的.只能用于web 程序.而拦截器既可以用于Web 程序,也可以用于Apllicatioon,Swing程序中。规范不同:Filte…

linux开启安全审计功能,Linux操作系统之安全审计功能

内核编译时,一般打开NET选项就打开AUDIT选项了。在系统中查看audit是否打开,root 用户执行:service auditd status我们知道在Linux系统中有大量的日志文件可以用于查看应用程序的各种信息,但是对于用户的操作行为(如某用户修改删除…

c语言中成绩查询编程,[编程入门]成绩评定 (C语言代码)

解题思路:for 嵌套 if else注意事项:参考代码:#include using namespace std;int a[5];int used[5];int print[4];void dfs(int cur,int cont){if(cur 4){for(int i 1;i < 3;i){cout<}cout<return;}for(int i 1;i < 4;i){if(used[i] 0&&i ! cont){used…