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

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

准备工作

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.表单的格式:

 {
'__LASTFOCUS' : '',
'__EVENTTARGET' : 'ctl00$ContentPlaceHolder1$Login1$btnLogin',
'__EVENTARGUMENT' : '',
'__VIEWSTATE' : __VIEWSTATE,
'__EVENTVALIDATION' : __EVENTVALIDATION,
'ctl00$ContentPlaceHolder1$Login1$txtUsr' : 用户名,
'ctl00$ContentPlaceHolder1$Login1$txtPwd' : 用户密码,
'ctl00$ContentPlaceHolder1$Login1$txtYZM' : 验证码
}

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

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

7、8、9则对应用户名、密码和验证码,用户名密码可以写死,验证码则需要用到tesseract-ocr进行识别。

4.表单提交的报文头

 {
'Accept' : b'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Encoding' : 'gzip, deflate, lzma',
'Accept-Language' : 'zh-CN,zh;q=0.8',
'Cache-Control' : 'max-age=0',
'Connection' : 'keep-alive',
'Content-Length' : 表单内容长度,
'Content-Type' : 'application/x-www-form-urlencoded',
'Cookie' : cookie内容,
'Host' : '222.217.19.16:3512',
'Origin' : 'http://222.217.19.16:3512',
'Referer' : 'http://222.217.19.16:3512/Site/LzsfySite/Default.aspx',
'Upgrade-Insecure-Requests' : '1',
'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}'
}

其中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'))

提交表单

根据之前的内容,我们已经获取了提交登录表单所需要的一切信息。

所以我们可以开始构造一个表单并提交

 import zlib
import urllib.request
import urllib.parse #表单提交的url
hosturl = 'http://222.217.19.16:3512/Site/LzsfySite/Default.aspx' #构造表单
formData = {
'__LASTFOCUS' : '',
'__EVENTTARGET' : 'ctl00$ContentPlaceHolder1$Login1$btnLogin',
'__EVENTARGUMENT' : '',
'__VIEWSTATE' : '__VIEWSTATE',
'__EVENTVALIDATION' : '__EVENTVALIDATION',
'ctl00$ContentPlaceHolder1$Login1$txtUsr' : '用户名',
'ctl00$ContentPlaceHolder1$Login1$txtPwd' : '密码',
'ctl00$ContentPlaceHolder1$Login1$txtYZM' : 'xxxx'
}
#对formData进行url编码
formData = urllib.parse.urlencode(formData) #构造登陆用header
headers = {
'Accept' : b'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Encoding' : 'gzip, deflate, lzma',
'Accept-Language' : 'zh-CN,zh;q=0.8',
'Cache-Control' : 'max-age=0',
'Connection' : 'keep-alive',
'Content-Length' : len(formData.encode('GB2312')),
'Content-Type' : 'application/x-www-form-urlencoded',
'Cookie' : 'cookieText',
'Host' : '222.217.19.16:3512',
'Origin' : 'http://222.217.19.16:3512',
'Referer' : 'http://222.217.19.16:3512/Site/LzsfySite/Default.aspx',
'Upgrade-Insecure-Requests' : '',
'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}'
} #开始登陆
loginRequest = urllib.request.Request(hosturl, formData.encode('GB2312'), headers)
loginResponse = urllib.request.urlopen(loginRequest)
#返回的数据是压缩过的,所以要用zlib进行解码
loginResponseData = zlib.decompress( loginResponse.read(), 16+zlib.MAX_WBITS).decode('GB2312') 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

简单示例:

 from xlutils.copy import copy
import xlrd
import xlwt
from xlwt.Style import easyxf #打开文件,formatting_info=true表示读入单元格style信息
file = xlrd.open_workbook('empty.xls',formatting_info=True)
#用xlutils.copy的copy方法获取一个报表对象
w = copy(file) #定义居中对齐格式示例
alignment = xlwt.Alignment()
alignment.horz = xlwt.Alignment.HORZ_CENTER
style = xlwt.XFStyle()
style.alignment = alignment #write方法的第一个参数对应要写入的行数,第二个参数对应要写入的列数,二者都是从0开始计算的
#用居中对齐格式写入第3张sheet的3行7列单元格
w.get_sheet(3).write(2,6, '2016 年 7 月 21 日至2016 年 8 月 20 日', style)
#用居中对齐格式写入第3张sheet的16行3列单元格
w.get_sheet(3).write(15,2, '2016 年 8 月 21 日' , style) #定义边框示例
borders = xlwt.Borders()
borders.left = 1
borders.right = 1
borders.top = 1
borders.bottom = 1
style = xlwt.XFStyle()
style.borders = borders
style.alignment = alignment #填充数据
for i in range(1, 18):
w.get_sheet(3).write(9,i,int(100), style)
w.get_sheet(3).write(10,i,int(100), style) #写入公式示例
for i in range(1,18):
column = chr(ord('A')+i)
w.get_sheet(3).write(13, i, xlwt.Formula('SUM(' + column + '10:' + column + '13)'),style) #保存为新文件
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的动力。

本章完。

用python模拟登录(解析cookie + 解析html + 表单提交 + 验证码识别 + excel读写 + 发送邮件)的更多相关文章

  1. 结合API Gateway和Lambda实现登录时的重定向和表单提交请求(Python3实现)

    1. 创建Lambda函数,代码如下: from urllib import parse def lambda_handler(event, context): body = event['body' ...

  2. Python模拟登录wap版百度贴吧+自己主动回贴

    模拟登录的原理都差点儿相同.大致都是这样: 打开首页获取相关cookie: 提交登陆表单(即username与password). 确认是否登录成功. 假设想了解更具体的原理与相关知识,推荐到具体解释 ...

  3. 【py登陆】python模拟登录

    用Python模拟登录网站 前面简单提到了 Python 模拟登录的程序,但是没写清楚,这里再补上一个带注释的 Python 模拟登录的示例程序.简单说一下流程:先用cookielib获取cookie ...

  4. Python模拟登录的几种方法

    目录 方法一:直接使用已知的cookie访问 方法二:模拟登录后再携带得到的cookie访问 方法三:模拟登录后用session保持登录状态 方法四:使用无头浏览器访问 正文 方法一:直接使用已知的c ...

  5. [Python] Python 模拟登录,并请求

    Python 模拟登录,并请求 # encoding: utf- import requests import socket import time socket.setdefaulttimeout( ...

  6. 【Python数据分析】Python模拟登录(一) requests.Session应用

    最近由于某些原因,需要用到Python模拟登录网站,但是以前对这块并不了解,而且目标网站的登录方法较为复杂, 所以一下卡在这里了,于是我决定从简单的模拟开始,逐渐深入地研究下这块. 注:本文仅为交流学 ...

  7. python模拟登录浙江大学彩云库

    前言: 群里一位朋友叫我帮他写 一个模拟登录的. 代码: import requests import time url="http://yk3.gokuai.com/web/index&q ...

  8. 忘记秘密利用python模拟登录暴力破解秘密

    忘记秘密利用python模拟登录暴力破解秘密: #encoding=utf-8 import itertools import string import requests def gen_pwd_f ...

  9. Python 模拟登录几种常见方法

    方法一:直接使用已知的cookie访问 优点: 简单,但需要先在浏览器登录 原理: 简单地说,cookie保存在发起请求的客户端中,服务器利用cookie来区分不同的客户端.因为http是一种无状态的 ...

随机推荐

  1. python之路第五篇之装饰器:(进阶篇)

    装饰器: 学前必备知识: def f1(): print "f1" f1() #表示函数执行 f1 #表示函数,指向内存地址 f1 = lambda x: x + 1 f1() # ...

  2. python中html解析-Beautiful Soup

    1. Beautiful Soup的简介 简单来说,Beautiful Soup是python的一个库,最主要的功能是从网页抓取数据.官方解释如下: Beautiful Soup提供一些简单的.pyt ...

  3. 正则表达式与grep和sed

    正则表达式与grep和sed 目录 1.正则表达式 2.grep 3.sed grep和sed需要正则表达式,我们需要注意的正则表达式与通配符用法的区分. 1.正则表达式 REGEXP,正则表达式:由 ...

  4. 机器学习技法:01 Linear Support Vector Machine

    Roadmap Course Introduction Large-Margin Separating Hyperplane Standard Large-Margin Problem Support ...

  5. Node Inspector 代理实现

    本文首发于 https://github.com/whxaxes/blog/issues/9 背景 平时做 node 开发的时候,通过 node inspector 来进行断点调试是一个很常用的 de ...

  6. LINUX 笔记-条件测试

    格式:test condition 文件测试状态 -d 目录 -s 文件长度大于0,非空 -f 正规文件 -w 可写 -l 符号链接 -u 文件有suid位设置 -r 可读 -x 可执行 字符串测试 ...

  7. MySQL数据库主从复制实践

        MySQL 主从(MySQL Replication),主要用于 MySQL 的实时备份.高可用HA.读写分离.在配置主从复制之前需要先准备 2 台 MySQL 服务器. 一.MySQL主从原 ...

  8. Java反射机制(Reflect)解析

    一.导读 反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问.检测和修改它本身状态或行为的一种能力.这一概念的提出很快引发了计算机科学领域关于应用反射性的研究.它首先被程序语言的设计 ...

  9. Java基础-方法(07)

    方法的定义 方法其实就是完成特定功能的代码块在很多语言里面都有函数的定义函数在Java中被称为方法 格式: 修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2…) { 函数体; ret ...

  10. Java就业企业面试问题-ssh框架

    SSH框架阶段SSH的优缺点,使用场景?   Hibernate优点:   (1) 对象/关系数据库映射(ORM) 它使用时只需要操纵对象,使开发更对象化,抛弃了数据库中心的思想,完全的面向对象思想 ...