目标网站:www.k780.com
语言:python
任务:自动注册并登录网站爬取用户信息
1、分析post包
http://www.k780.com/?q=account&type=register。由于此网站注册时不需要验证邮箱,所以只需要伪造post包骗过服务器即可完成注册。
抓取浏览器注册时的post内容,header部分用于模拟浏览器请求,body部分用于填写表单。
header:
POST /?q=account&type=register HTTP/1.1\r\n
Host: www.k780.com\r\n
Connection: keep-alive\r\n
Content-Length: 129\r\n
Cache-Control: max-age=0\r\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n
Origin: http://www.k780.com\r\n
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.101 Safari/537.36\r\n
Content-Type: application/x-www-form-urlencoded\r\n
DNT: 1\r\n
Referer: http://www.k780.com/?q=account&type=register\r\n
Accept-Encoding: gzip,deflate\r\n
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,ja;q=0.4,zh-TW;q=0.2\r\n
Cookie: PHPSESSID=adgnskgi1kf46khlqce6614u42; AJSTAT_ok_pages=3; AJSTAT_ok_times=5\r\n
body:
usernm=xlltest5&passwd=12345&passwd1=12345&email=terwtwe%40qq.com&authcode=4461®ister=%E9%A9%AC%E4%B8%8A%E6%B3%A8%E5%86%8C
header中其他字段在每次请求的时候都不会发生变化,在手动发送post包时照抄即可,而cookie值得我们注意,与网站的验证码机制有关。
body中usernm,passed,passwd1,email字段也都显而易见,在注册时随机生成,保证并没有被使用过即可。authcode字段代表验证码。register字段在此情况内并不会改变,照抄。因此,如何我们填写上正确的authcode就可以完成注册。那么服务器是如何识别post包内填的验证码是否正确呢?
传统的验证码识别机制有两种:1、cookie。2、session
cookie:浏览器在请求注册页面时,服务器会直接将验证码值存入cookie内,待浏览器post时验证body内验证码和cookie的值是否相等。这样子的弊端就是用户可以直接通过分析cookie内的值来反导出验证码值。
session:如果浏览器是第一次访问网页,那么服务器在发现无cookie的浏览器请求验证码时,会首先发一同发送set-session给浏览器,这时浏览器cookie就拥有一个session id了,并在请求网页时带上此cookie。。与此同时,服务器开辟一块以session id标识的空间,存储验证码值。浏览器post后,服务器将body内的值与相对应的session id内的值比对。如果浏览器重新刷新了验证码,服务端也会将刷新后的值替换掉之前的值,存在session id对于的空间中。如果浏览器已经含有cookie,则session id并不会改变,服务器也会在原先的session id处存取验证码值。这样子的好处是验证码存储在服务器内,不会被破解。
分析这个网页,显而易见是session模式。破解这种模式的关键是保证session一致性。整个过程对客户端来说有两份cookie,请求验证码图片时get头中的cookie,以及提交表单,post头中的cookie。保证这两次所带的cookie中session值一致,那么我们所需要破解的验证码便与浏览器所需要验证的验证码一致了,破解才有意义。于是我们有两种方法,1、伪装成已经有session id,copy一份浏览器中的session值并在get验证码图片以及post表单时带上此session。2、伪装成没有session id,不带cookie请求验证码图片,抓取响应头中set-session值,并在post时带上此cookie。
但实际上这种验证机制有个bug,就是服务器端并没有验证session id是服务器自己生成的还是用户自己瞎编的机制,所以伪造session便十分简单,直接随机生成一个字符串,保证get验证码图片和post表单时请求头”Cookie: PHPSESSID=*”一致。
明白这些之后剩下的就是破解验证码了。
2、破解验证码
对于简单的验证码图片破解有两种方式,1、PyTess库 2、自制字库识别。这里采用方法2。
方法2需要安装PIL库
1).所有的验证码都是数字,我们先下载10份验证码。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#下载样本图片
import urllib
for i in range(10):
url = 'http://vip.k780.com:88/?app=img.set_authcode' #验证码的地址
print "download", i
file("./pic/%04d.gif" % i, "wb").write(urllib.urlopen(url).read())
2).去色(这段代码直接copy的)
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#图像二值处理
import os
from PIL import Image
j=1
dir="./pic/"
path = "./font/"
for f in os.listdir(dir):
if f.endswith(".gif"):
img = Image.open(dir+f) # 读入图片
img = img.convert("RGBA")
pixdata = img.load()。、
#二值化
for y in xrange(img.size[1]):
for x in xrange(img.size[0]):
if pixdata[x, y][0] < 90:
pixdata[x, y] = (0, 0, 0, 255)
for y in xrange(img.size[1]):
for x in xrange(img.size[0]):
if pixdata[x, y][1] < 136:
pixdata[x, y] = (0, 0, 0, 255)
for y in xrange(img.size[1]):
for x in xrange(img.size[0]):
if pixdata[x, y][2] > 0:
pixdata[x, y] = (255, 255, 255, 255)
img.save(path+f, "GIF")
3).把图片砍成一个个单独数字,这里需要找到验证码text生成jpg时规律,把每个数字完整地分开,保证字库正确性。
#!/usr/bin/env python
# -*- coding: UTF-8 -*-
#图像分割
import os ,Image
j = 1
dir="./font/"
for f in os.listdir(dir):
if f.endswith(".gif"):
img = Image.open(dir+f)
for i in range(4):
x = 9*i #这里的数字参数需要自己
y = 3 #根据验证码图片的像素进行
img.crop((x, y, x+9, y+12)).save("./fonts/%d.gif" % j) #适当的修改
print "j=",j
j += 1
4).从劈开的数字中选出比较好的,组成一个0-9的完整字库,并将其文件名改成其对应的数组。至此我们的字库完成了。
然后我们将所需要破解的验证码图片按照同样的逻辑:(去色)、(分别单独劈开)、(分别和字库做像素比对,遍历字库,取最接近的图片,读取其文件名作为自己的文件名)、(将文件名加起来组成验证码)
#!/usr/bin/env python
# ?*? coding: UTF?8 ?*?
#将整个文件做成一个识别模块,输入图片地址,返回验证码
import os, Image
def binary(f): #图像的二值化处理
#print f
img = Image.open(f)
#img = img.convert('1')
img = img.convert("RGBA") #参考文章中无该行,无该行,我这里会报错
pixdata = img.load()
for y in xrange(img.size[1]):
for x in xrange(img.size[0]):
if pixdata[x, y][0] < 90:
pixdata[x, y] = (0, 0, 0, 255)
for y in xrange(img.size[1]):
for x in xrange(img.size[0]):
if pixdata[x, y][1] < 136:
pixdata[x, y] = (0, 0, 0, 255)
for y in xrange(img.size[1]):
for x in xrange(img.size[0]):
if pixdata[x, y][2] > 0:
pixdata[x, y] = (255, 255, 255, 255)
return img
nume = 0
def division(img): #图像的分割,就是验证码按字符分割出来
global nume
font=[]
for i in range(4):
x = 9*i #该函数中的像素值都需要自己进行微调
y = 3
temp = img.crop((x,y,x+7,y+10))
temp.save("./temp/%d.gif" % nume)
nume=nume+1
font.append(temp)
return font
def code_compare(img): #分隔出来的字符与预先定义的字体库中的结果逐个像素进行对比找出差别最小的项
fontMods = []
for i in range(10):
fontMods.append((str(i), Image.open("./num/%d.gif" % i)))
result=""
font=division(img)
for i in font:
target=i
points = []
for mod in fontMods:
diffs = 0
for yi in range(10):
for xi in range(7): #以下多行为自己修改,参考文章中的值对比存在问题
#print "mod[1].getpixel((xi, yi)):"+str(mod[1].getpixel((xi, yi)))
#print "target.getpixel((xi, yi)):"+str(target.getpixel((xi, yi)))
if 0 in target.getpixel((xi, yi)):
compare = 0
else:
compare = 255
if mod[1].getpixel((xi, yi)) != compare:
diffs += 1
#print "diffs:" + str(diffs)
points.append((diffs, mod[0]))
points.sort()
result += points[0][1]
return result
#if __name__ == '__main__':
# codedir="./pic/"
# for imgfile in os.listdir(codedir):
# if imgfile.endswith(".gif"):
# dir="./result/"
# img=binary(codedir+imgfile)
# num=recognize(img)
# dir += (num+".gif")
# print "save to", dir
# img.save(dir)
def code_identify(path):
img=binary(path)
num=code_compare(img)
return num
至此,自动注册完成
3、自动登录并读取用户信息
完成自动注册后自动登录是同样的逻辑,自制session完成登录后,保留session id。之后以同样的session发送get请求,服务器就会默认发动请求的用户是已登录的用户,返回200响应头,否则会直接返回登录界面。
#usr/bin/python
import recognize
import requests
import HTMLParser
import urlparse
import urllib
import urllib2
import cookielib
import string
import re
import json
from random import Random
import time
global cookie
def random_str(randomlength=8):
str = ''
chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
length = len(chars) - 1
random = Random()
for i in range(randomlength):
str+=chars[random.randint(0, length)]
return str
def auto_login(usrname):
global cookie
#print usrname
#get authcode from pic_url
pic_url = 'http://vip.k780.com:88/?app=img.set_authcode&session_id='
pic_name = "./pic/temp.gif"
cookie = random_str()
pic_url = pic_url+cookie
request_authcode = urllib2.Request(pic_url)
response_authcode = urllib2.urlopen(request_authcode)
file(pic_name, "wb").write(response_authcode.read())
authcode = recognize.code_identify(pic_name)
#print authcode
headers = { 'Host':'www.k780.com',
'Connection':'keep-alive',
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Origin':'http://www.k780.com',
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.101 Safari/537.36',
'Content-Type':'application/x-www-form-urlencoded',
'DNT':'1',
'Referer':'http://www.k780.com/?q=account&type=login',
'Accept-Encoding':'gzip,deflate',
'Accept-Language':'zh-CN,zh;q=0.8,en;q=0.6,ja;q=0.4,zh-TW;q=0.2',
'Cookie':'PHPSESSID='+cookie
}
posturl = "http://www.k780.com/?q=account&type=login"
postData = {'usernm':usrname,
'passwd':'527964015',
'authcode':authcode,
'login':'%E9%A9%AC%E4%B8%8A%E7%99%BB%E5%BD%95'}
postData = urllib.urlencode(postData)
print postData
request = urllib2.Request(posturl, postData, headers)
response = urllib2.urlopen(request)
text = response.read()
#print "post:", text
def get_info():
global cookie
r = requests.get(url='http://www.k780.com/?q=mgr&mark=my_info', headers={'cookie':'PHPSESSID='+cookie+'; AJSTAT_ok_pages=2; AJSTAT_ok_times=2', 'Referer':'http://www.k780.com/?q=account&type=login'})
#print "get:", r.text
return r.text
if __name__ == '__main__':
global cookie
s = open("./usrname.txt", "r")
f = open("./usr.txt","a+")
while 1:
#for i in range(1,3):
usrname = s.readline()
print usrname
usrname = usrname[:8]
if len(usrname) == 0:
break
auto_login(usrname)
text = get_info()
#incorrect authcode
while text.find("Appkey") == -1:
print "incorrect authcode, try again."
auto_login(usrname)
text = get_info()
Appkey = re.search("Appkey:</td><td>[0-9]+</td>", text).group()
Appkey = Appkey[16:21]
#print Appkey
Sign = re.search("Sign:</td><td>[0-9a-z]+</td>", text).group()
Sign = Sign[14:-5]
#print Sign
result = Appkey+":"+Sign+"\n"
print result
f.write(result)
time.sleep(10)