杨培文

Yang Peiwen

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# coding: utf-8
__author__ = 'ypw'

import urllib2
import socket
import time
import os

url = "http://192.168.0.1/"
urlspeed = "http://192.168.0.1/userRpm/SystemStatisticRpm.htm?contType=1&sortType=5&Num_per_page=50&Goto_page=1"
socket.setdefaulttimeout(3)
opener = urllib2.build_opener()
opener.addheaders.append(('Cookie', 'Authorization=Basic%20YWRtaW46MTIzNDU2;'))
request = urllib2.Request(urlspeed)
request.add_header("Referer", "http://192.168.0.1/userRpm/MenuRpm.htm")

def zhongjian(yourstr, leftstr, rightstr):
leftposition = yourstr.find(leftstr)
rightposition = yourstr.find(rightstr, leftposition)
return yourstr[leftposition+len(leftstr):rightposition]

def tospd(string):
string = int(string)
if string > 1024*1024:
return str(string/1024/1024)+"MB/s"
elif string > 1024:
return str(string/1024)+"KB/s"
else:
return str(string)+"B/s"

while True:
html = opener.open(request).read()
content = zhongjian(html, "Array(n", ")")
content1 = content.split("n")
os.system("cls")
for content2 in content1:
content3 = content2.split(",")
if len(content3) >= 8:
print content3[1], content3[2], tospd(content3[5]), tospd(content3[6])
time.sleep(0.5)


这是自家TP-LINK监控网速的程序...需要改动才能在其他路由里运行,仅供参考. 新知识:包括Cookies的模拟和Referer的伪造. 复习:文本处理

1
2
3
4
5
6
7
8
9

def zhongjian(yourstr, leftstr, rightstr):
leftposition = yourstr.find(leftstr)
rightposition = yourstr.find(rightstr, leftposition)
return yourstr[leftposition+len(leftstr):rightposition]

s = "test123abc"
print zhongjian(s, "test", "abc")

相信这个程序很简单,大家都能看得懂. 最经典的是这一段,一个数组取它的一部分可以直接这样取

1
2
3

str[left:right]

星新一小说下载程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# coding: utf-8
__author__ = 'ypw'

from urllib import *
from progressbar import *
from bs4 import BeautifulSoup
import socket
import os

url = "http://www.xiexingcun.com/Global/xingxinyi/1.htm" # 星新一小说
path = "x"

socket.setdefaulttimeout(3) # 3秒超时
if os.path.exists(path) is False:
os.makedirs(path)
# 如果没有这个文件夹就创建一个
html = urlopen(url).read()
soup = BeautifulSoup(html)
# <a href="mydoc001.htm" target="_parent">被窃的文件</a> 寻找这样的链接
links = soup.find_all('a', target="_parent")
index = 0
pbar = ProgressBar(widgets=[Percentage(), Bar(), ETA()], maxval=len(links)).start()
# 建立一个进度条,ETA()是estimated time of arrival,剩余时间
for link in links:
index += 1
html2 = html = urlopen("http://www.xiexingcun.com/Global/xingxinyi/" + link['href']).read()
soup2 = BeautifulSoup(html2)
title = soup2.find("font", size="4").get_text()
text = soup2.find("font", size="2").get_text()
f = open(path+"/" + title + ".txt", "w")
f.write(text.encode("utf-8"))
f.close()
pbar.update(index)
# 更新进度条
pbar.finish()

这回甚至可以直接打开了,效果更好

入坑之前,我只会玩游戏开挂,当时还是小学的时候,玩冒险岛,那时候我就会用CE(Cheat Engine)这样的软件来修改游戏数据,冒险岛这个游戏是靠打怪升级为乐趣的,如果有一款外挂能帮你把怪物定在一个点(吸怪),那么这个外挂就十分有价值.如下图:

(图片来自网络)

其实原理很简单,获取到屏幕的左右上下之后,修改为一个点就可以让怪物集中在一个点.当时除了CE这样的工具之外,还出现了用易语言写的一键傻瓜型外挂.这种程序通常写得很简陋,里面通常是一些按钮,偶尔还带一点编辑框.当时我就下了这样的外挂尝试,效果还不错.有一次出现了一个非常厉害的外挂,好像是类似于无敌之类的.就是怪物碰你你不会损失生命值.这样的挂给我们带来了巨大的诱惑力.但是我在网上下载了一个程序之后,提示了类似于这样的信息:

,然后下载了易语言...

下下来之后,我发现,哈,我可以创建窗口!我可以放按钮在上面!我还可以balabala......那个外挂后来发现只是少了一个文件,下下来就好了.不过编程之路就从这里开始踏下了第一步.为什么不学C!小学根本不会英文啊..

从初中开始,就陆陆续续地做过了一些奇葩玩意.比如为了刷某网页访问量做的自动刷新(当时既不会HTTP操作也不会线程,就用时钟周期事件和超文本浏览框来刷),当时还在网吧里的地下一层开了所有的机器打开我的程序来刷,想想也挺牛的.软件大致结构就象这样:

这里面真的有16个浏览器...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

.版本 2
.支持库 HtmlView

.子程序 _时钟1_周期事件
.局部变量 地址, 文本型

地址 = “http://ypw.ys168.com”
超文本浏览框1.地址 = 地址
超文本浏览框2.地址 = 地址
超文本浏览框3.地址 = 地址
超文本浏览框4.地址 = 地址
超文本浏览框5.地址 = 地址
超文本浏览框6.地址 = 地址
超文本浏览框7.地址 = 地址
超文本浏览框8.地址 = 地址
超文本浏览框9.地址 = 地址
超文本浏览框10.地址 = 地址
超文本浏览框11.地址 = 地址
超文本浏览框12.地址 = 地址
超文本浏览框13.地址 = 地址
超文本浏览框14.地址 = 地址
超文本浏览框15.地址 = 地址
超文本浏览框16.地址 = 地址


之后我的第二次启蒙来源于一个效率非常高的刷QQ空间人气的软件.那软件一秒居然可以访问50次.我的天当时那个单核的时代他一秒50次简直颠覆了我的世界观.后来我看了他的源码之后知道了线程这么个概念,知道了HTTP这个概念(原来以为必须要浏览器啊).同时还学会了POST.学会了利用WPE截包(现在才发现chrome截包方便太多).

下面是我做的一些小工具: QQ空间数据查看----查看你的空间访客量多少,有多少阳光指数,爱心指数,balabala....(数据挖掘啊)(2009年7月15日) 空格点鼠标----当时估计是觉得鼠标不好用,然后用空格键代替点鼠标(键盘热键和鼠标模拟)(2009年7月19日) 柯南音乐播放器----当时在某站找到一个网盘,然后把我搜刮的柯南音乐全部上传上去了,做了一个小播放器(2009年8月3日)

SOSO刷搜索次数----刷到了世界第一的神器(2009年10月18日)

4399刷签到----为了无聊的分数,弄了一个每天自动签到的后台程序(插入启动项,杀毒白名单,可怕)(2009年8月4日)

全能(ALL)----一款可以登录QQ,测试GET和POST的软件,可以连续发送(多线程),可以转码,内置浏览器方便登录,支持代理IP.(2010年1月2日)快速测试HTTP神器啊...现在还在用它

QQ牧场激活器----当时QQ农场和QQ牧场是两个东西,牧场不是所有人都能玩,但是我们发现了一个漏洞,所以就做了这么个玩意(2010年1月24日)

密保卡安全浏览器----这是玩腾讯游戏的时候避免别人拍照而做的软件..没什么特别的,大家应该看明白了.

QQ炸弹----正如这个神奇的名字,它就是用来炸QQ好友的,自动生成英文/数字/甚至是中文,自动输入并发送,可调时间间隔(可怕的科技)(2010年4月19日)

占用内存程序----正如这个名字...它没有任何用处,就是为了占内存.(2010年6月10日)

QQ拼音刷打字记录程序----没错拉,键盘模拟打字而已,可以把每日打字量刷非常高(2010年7月1日)

刷化肥----这个软件应该是当时QQ农场牧场第一个外挂吧..(2014年7月31日),利用腾讯服务器时间不同步的漏洞,可以在0点领翻倍数量的化肥,并且有黄钻用户领到了几万袋化肥(人民币价值几十万),成功成为当时排行榜前几名.也是一个传奇了.

自动种植----QQ农场自动利用刷来的化肥种植农作物,收割,铲除,多线程异步刷等级,曾经一天刷了将近一百级(2010年8月3日)下图为8月5日,下下图为10月31日

犇牛病毒专杀工具----当时不知道为什么中毒了,电脑各个地方都是usp10.dlllpk.dll,于是写了一个工具全盘搜索该文件删除.(2010年8月17日)

抢黄钻----这玩意简直是抢火车票的祖先..自带帐号记录,北京时间,多线程刷分,当时搞活动可以领黄钻,但是只有特定时候开放,于是我就写了个这个软件,到点了马上开线程刷,一定能领到,没有人比一秒50次的加特林还快了.(2010年9月5日)

(注:程序的北京时间经过修改,原来使用的授时中心的时间源已经404了) 附上时间校对的程序(甚至考虑了东八区UTC+8):

1
2
3
4
5
6
7
8
9
10
11
12
13

.版本 2

.子程序 校对
.局部变量 second, 整数型
.局部变量 a, 文本型

a = 彗星HTTP读文本 (“http://open.baidu.com/special/time/”)
输出调试文本 (a)
second = 到长整数 (取文本中间内容 (a, “baidu_time(”, “)”)) ÷ 1000
SJ = 增减时间 (到时间 (“1970.01.01 08:00:00”), #秒, second)


强制关机程序----这个有点厉害,调用系统API提权强制黑屏关机(2010年10月17日)

1
2
3
4

RtlAdjustPrivilege (#SE_SHUTDOWN_PRIVILEGE, 1, 0, 0)
NtShutdownSystem (#SHUTDOWN)

bilibili视频地址获取器----没想到吧!当时我居然做出来了这么神奇的玩意,用来下视频的(2010年11月1日)当时的视频地址长这样:http://bilibili.us/video/av34026/,现在已经失效.

Q宠大乐斗辅助----这个功能就十分强大了,几乎把一个游戏的每日操作做得半自动化了,比如自动刷牧场(这是个漏洞,牧场有个拍蚊子功能,我直接把POST数据改为num=100了,这样一天得到的经验值就满了),然后还有自动浇花(花藤),好友列表双击PK,领取各种每日奖励.(2010年11月22日)

QQ农场牧场辅助工具----依然是一系列的全自动操作,免除各种烦恼.(2010年11月25日)原理都一样,不再细说.

独立钻石棋----没错!我做过游戏!非常简单的一个棋类游戏,规则可以自行百度.(2010年11月30日)

百度一系列工具----百度登录,百度注册,百度扫号,刷粉器,掌上百度发帖爆吧器.还带有权限验证功能.恐怖到自动获取代理IP.(这时候我就会正则表达式了)(2011年2月19日)

批量创建QQ群----可以创建出连号QQ群来卖!可以批量登录QQ,带有cookies管理!(意味着你可以同时有一百个QQ号在一个软件上登录)(2011年4月17日)

键盘记录器----而且还写了配套回放软件...记录了键值,按下还是弹起,距离上次操作的间隔时间,可以完全回放出来.(2011年8月28日)

远程手机攻击器----是短信轰炸的那种攻击,远程就是放在服务器上面的那种.可以远程下指令给服务器让服务器短信轰炸手机.发短信的方式是利用某网站搞活动,验证手机号码会发送短信,但是没有限制次数.(2012年1月9日)

高考倒计时----哈哈哈 就是高考倒计时拉,我后来居然还用单片机做了一个真正的高考倒计时装置,然后被班主任没收了.(2012年6月17日)中间时间跨度好大,因为我犯事了,所以好像半年没有碰电脑.

这里有一个大事----不能说(2013年10月)

BTBU上网登录----给学生们上网登录的软件(2013年10月14日)

BTBU免流量软件----顾名思义,SSL-VPN不多解释(2013年11月14日)

素数螺旋图的绘制----具体定义请维基百科,这只是个人兴趣,GEEK向(2013年11月14日)

眨眼识别----这个碉堡了,这是脑电波识别,但是我水平不够,就做了个眨眼识别(2014年1月29日)

透传----这是给我的wifi插座做的服务器端,透明传输(2014年3月24日)

BTBU录入----懂的人懂,这个不能说(2014年3月30日)

机器鱼代码更新工具----自动替换api的文本处理工具,临时性工具(2014年5月21日)

DHT11远程温湿度监控系统----某爱德课设作品,远程监控温湿度,温度条显示(2014年6月18日)

雾霾查看器----这个就是利用了气象局的信息做的临时小创意(2014年7月31日)现在他们自己服务器好慢.

mini3----这又是一个不能说的程序(2014年12月2日)

BTBU VPN连接器----这是连接学校VPN的小工具,由于有些学生电脑本身是WIN8的系统,又升级了IE,导致VPN连不上,所以做了这么个工具(发出来没几天学校就改版了VPN,把域名换了)(2015年1月17日)

未完待续...

需要:

  1. Python 2.7.9 (default, Dec 10 2014, 12:24:55) [MSC v.1500 32 bit (Intel)] on win 32
  2. BeautifulSoup(安装方式:pip install BeautifulSoup)(文档) 首先让我们来体验一下python的魔力吧~获取第一个页面.
1
2
3
4
5
6

from urllib import *
html = urlopen("http://m.lssdjt.com/?date=2015-1-1").read()
html = html.decode("utf-8")
print(html)

这是历史上的今天的页面,我们看到页面已经正常地被加载了.现在我们来讲一下其中是怎么工作的.

  1. 引入urllib这个库.
  2. urlopen读取url数据,并且read()成string
  3. 解码,由于页面是UTF-8编码的,为了显示出来不至于乱码,我们进行了解码
  4. 输出到屏幕上. python.就是这么简单.

好了,我们抓到了网页数据之后呢,就要进行分析了.这个页面http://m.lssdjt.com/?date=2015-1-1如果大家去看呢,就会发现我们需要抓的仅仅就是那些链接.接下来我们摆出大招,抓链接.

1
2
3
4
5
6
7
8
9
10
11
12
13

from urllib import *
from bs4 import BeautifulSoup

html = urlopen("http://m.lssdjt.com/?date=2015-1-1").read()
soup = BeautifulSoup(html)
for link in soup.find_all('a'):
if link.string is not None:
title = link.get_text()
href = link['href']
print href, title


这里我们输出结果就十分漂亮了.

分析:

  1. for link in soup.find_all('a'): 寻找所有a标签,也就是2010年-中国-东盟自贸区正式建成这样的标签,找到之后用link循环
  2. if link.string is not None: 如果它不是空的
  3. title = link.get_text()  获取文本,也就是标签之间的文字
  4. href = link['href']获取链接,也就是href="xxx"
  5. 输出 现在我们链接也OK了,直接下载下来存在硬盘里就OK拉.

为了我们方便寻找,我决定把title和href保存成csv文件(表格).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# coding: utf-8
__author__ = 'ypw'

from urllib import *
from bs4 import BeautifulSoup

html = urlopen("http://m.lssdjt.com/?date=2015-1-1").read()
soup = BeautifulSoup(html)
f = open('data.csv', 'w')
for link in soup.find_all('a'):
if link.string is not None:
title = link.get_text()
print title
title = title.encode("gbk")
href = link['href']
urlretrieve("http://m.lssdjt.com/" + href, href)
f.write(href + "," + title + "n")

f.close()

讲解:

  1. f = open('data.csv', 'w')  以写文件('w')的模式打开data.csv文件
  2. title = title.encode("gbk")  windows环境下,我们用gbk编码,excel才不会乱码
  3. urlretrieve("http://m.lssdjt.com/" + href, href)  这里的urlretrieve是下载文件的命令,我们这样用就行:urlretrieve(url,filename),如果需要更详细的用法请自行百度.
  4. f.write(href + "," + title + "n")  在csv文件里写一行数据
  5. f.close() 关闭文件 以上代码请新建一个文件夹,把上面的代码保存成一个.py文件,然后新建一个d文件夹(因为链接是/d/xxx.html).最后才打开你刚才保存的.py文件.不要在cmd中直接运行以上代码,如果你直接运行你会发现系统目录下多了很多html文件,别问我怎么知道的..

在刚才创建的文件夹中运行之后你就会发现神奇的事情:

抓下来了所有的页面.

之后我们再来一个循环,第一页下载完之后就下载第二天. 那么怎么寻找第二页呢?我们看这个第二天的链接:

1
2
3

<li class="r" onClick="location.href='?date=2015-1-2'">后一天>></li>

主要特征:li,class=r,onClick=xxx,那么我们可以构建这样的语句来抓取onClick:

1
2
3

soup.find("li", "r")['onclick']

这里为什么onclick是小写的呢?因为soup抓出来的我们输出的时候就是小写的了..我也不知道为什么.大写报错所以就小写呗. 输出location.href='?date=2015-1-2' 我们再用文本处理的办法得到''里的数据

1
2
3
4

r = soup.find("li", "r")['onclick'].split("'")[1]
print r

看看我们加了什么,我们把取出来的location.href='?date=2015-1-2'首先split("'")了,这样就会变成这样: ['location.href=', '?date=2015-1-2', ''] 然后我们需要的是第二段,于是我们就[1] 这样最后的结果就是?date=2015-1-2了.

然后写个循环,爬一年,然后避免出错我们选择在下载的时候用try语句,为了节省时间我们设置socket3秒超时.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# coding: utf-8
__author__ = 'ypw'

from urllib import *
from bs4 import BeautifulSoup
import socket

socket.setdefaulttimeout(3)

f = open('data.csv', 'w')
r = "?date=2015-1-1"
t = 0
while True:
t += 1
html = urlopen("http://m.lssdjt.com/"+r).read()
soup = BeautifulSoup(html)
r = soup.find("li", "r")['onclick'].split("'")[1]
for link in soup.find_all('a'):
if link.string is not None:
title = link.get_text()
print title
title = title.encode("gbk", "ignore")
href = link['href']
if href.find("html") > 0:
success = True
while success:
try:
print "getting " + href
urlretrieve("http://m.lssdjt.com/" + href, href)
success = False
except IOError:
print "超时"

f.write(href + "," + title + "n")
print "这是第{0}天".format(t)
if t > 365:
break

f.close()

上面就是我目前正在运行的程序.

目前它已经运行完毕,爬了将近一万四千个页面.

以下是抓取的href,title的表格 data.csv

让我们来了解一下instabtbu的教务管理功能开发历史

首先我们为了用户体验,决定采用识别验证码技术来解决验证码难认,验证码小等问题.

这是最初我们识别的时候,matlab输出的数据,此时我们的验证码准确率已经达到100%(样本超过50张),之后我们把识别验证码的代码从matlab语言翻译到C语言.(matlab源码也是原创)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

public String shibie(Bitmap myBitmap)
{
String yzm = "";
String[] myyzm = {"1","2","3","b","c","m","n","v","x","z"};
try{
int qietu,duibi;
int x,y;
for(qietu=0;qietu<4;qietu++)
{
int errnum[]={100,100,100,100,100,100,100,100,100,100};
for(duibi=0;duibi<10;duibi++)
{
int errpixel=0;
InputStream is = getResources().getAssets().open(myyzm[duibi]+".bmp");
Bitmap rawBitmap = BitmapFactory.decodeStream(is);
for(y=0;y<12;y++)
{
for(x=0;x<9;x++)
{
int col = myBitmap.getPixel(x+3+10*qietu, y+4);
int col2 = rawBitmap.getPixel(x,y);
if((col2&0xFF)>(col&0xFF))errpixel++;
}
System.out.print("nn");
}
errnum[duibi]=errpixel;
}
int wz=0,min=100,i;
for(i=0;i<10;i++)if(errnum[i]<min){min=errnum[i];wz=i;}
yzm+=myyzm[wz];
}
}catch(Exception e)
{}
System.out.println(yzm);
return yzm;
}

识别验证码其实并不难,上面的源码已经给出来了,实际上就是一张张图去比较,谁的错误点最少那么就是谁拉.由于识别并不难这里我不详细讲了.验证码组成元素只有十个,分别是123bcmnvxz.

这个就是在安卓上面识别的效果.识别好验证码之后,我们就开始做登录.本来是想做得跟上网一样,后来一想,反正同学们每次进教务仅仅只是查成绩或者查课表,干脆就一键查吧,于是登录按钮被砍掉了.

好了,让我们看登录怎么做吧! 嘛,假设你已经会了POST,如果不会请看上网登录是怎么写出来的. 会了POST之后呢,我们就可以通过Chrome的F12开发者工具截包,找到登录的数据包的结构,是这样的:

http://jwgl.btbu.edu.cn/Logon.do method=logon&USERNAME=学号&PASSWORD=密码&RANDOMCODE=验证码

OK,我们按照这个格式发送过去,得到的返回数据如果包含"http://jwgl.btbu.edu.cn/framework/main.jsp"这一串数据,意味着登录成功了.登录成功之后首先必须进入这个页面(http://jwgl.btbu.edu.cn/Logon.do?method=logonBySSO),才能进行其他操作,不然提示未登陆.我们把这个叫获取权限.获取到权限之后我们就可以开始查课表或者查成绩了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

Runnable kebiaoRunnable = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
try{
yzmBitmap = GET("http://jwgl.btbu.edu.cn/verifycode.servlet");
gengxin("获取验证码完毕");
EditText numEditText = (EditText)findViewById(R.id.changpao_num2);
EditText pswEditText = (EditText)findViewById(R.id.changpao_psw2);
yzmBitmap=im2bw(yzmBitmap);
String yzm = shibie(yzmBitmap);
gengxin(yzm);
String resultString = POST("http://jwgl.btbu.edu.cn/Logon.do","method=logon&USERNAME="+numEditText.getText()+"&PASSWORD="+pswEditText.getText()+
"&RANDOMCODE="+yzm);
//System.out.println(resultString);
if(dialog2.isShowing())dialog2.dismiss();
if(find(resultString, "http://jwgl.btbu.edu.cn/framework/main.jsp"))
{
gengxin("登录成功,获取权限……");
POST("http://jwgl.btbu.edu.cn/Logon.do?method=logonBySSO","");
//登录成功
loadkebiao();
}else if(find(resultString,"验证码错误"))
{
gengxin("验证码错误,重新登录……");
run();
}
else if(find(resultString, "errorinfo"))
{
if(dialog2.isShowing())dialog2.dismiss();
show(zhongjian(resultString, "errorinfo">","</span>")+"n默认密码为学号或身份证后六位。");
}else show("登录失败");
}catch(Exception e)
{
if(dialog2.isShowing())dialog2.dismiss();
}
if(dialog2.isShowing())dialog2.dismiss();
}
};

上面是查课表之前做的操作.

查课表

终于到查课表了,首先我们需要获取的是学期,学期格式大致是2014-2015-1. 有了学期之后,我们POST这样的数据就可以查到课表: http://jwgl.btbu.edu.cn/tkglAction.do?method=goListKbByXs&sql=&xnxqh=你要查的学期 就象这样

1
2
3

result = POST("http://jwgl.btbu.edu.cn/tkglAction.do?method=goListKbByXs&sql=&xnxqh="+xueqi, "");

你得到的数据通常是十分复杂的,所以我们就要引入一个十分有用的东西,它叫正则表达式(Regex),教程参考这个链接,之后我们的数据处理都要使用其中的工具.

这里我先贴一张图,感受一下正则表达式的威力.

我们可以看到,正则表达式威力巨大,上图是提取课程名称,上课老师和教师在哪的.有人问,时间呢?这里面没有时间..时间是这个正则表达式提取的:<div id="(.+?)-2".*?>(.+?)

让我们再上一张图:

我们看到了1-1,1-2,1-3这样的id,这个就是表示第几周的第几节课.我们取出来上课时间之后,再把右边的数据用刚才的正则表达式取一遍,就可以取出来课程名称,上课地点等数据了.然后就可以用程序画一个课程表了.

这是我们第一个课表,十分简陋,经过各方面考虑和调整,我们取消了左边的时间,然后将有单双周这样的特殊课程用颜色标出来,然后针对课程名称短的方格进行扩大处理,保证了整齐性,下面就是经过多方面调整的课程表:

像这样的课表,我们从一个网页,获得它中间有用的数据,到显示出来,虽然是一瞬间,但是非常的复杂,下面是从数据源到数组的程序(只有变成数组才能显得好看,没有人愿意看

这样的东西).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

try{
p=Pattern.compile("<div id="(.+?)-2".*?>(.+?)</div>");
m=p.matcher(result);
String temp;
while(m.find()){
shijian.add(m.group(1));
temp=m.group(2);
Pattern p2=Pattern.compile(" (.*?)<br>(.+?)<br>(.*?)<br><nobr> *(.*?)<nobr><br>(.*?)<br>(.*?)<br>");
Matcher m2=p2.matcher(temp);
if(m2.find())
{
mingcheng.add(m2.group(1));
// int i;String myString="";
// for(i=1;i<6;i++)if(m2.group(i).length()!=0)myString+=m2.group(i)+"n";

// xiangxi.add(myString.substring(0,myString.length()-1));
temp=temp.replace(" ","");
System.out.println(temp);
Pattern p3=Pattern.compile("<.+?> *");
Matcher m3=p3.matcher(temp);
temp=m3.replaceAll("n");
while(temp.indexOf("nn")!=-1)
{
temp=temp.replace("nn","n");
}
temp=temp.substring(0,temp.length()-1);
xiangxi.add(temp);

String[] tempStrings = temp.split("n");

if(tempStrings.length==5)
{
String myString = "";
if(tempStrings[3].indexOf("单周")!=-1)myString+="单周n";
else if(tempStrings[3].indexOf("双周")!=-1)myString+="双周n";
myString+=tempStrings[4];
didian.add(myString);
}
else if(tempStrings.length==6)
{
if(tempStrings[4].indexOf("暂无")==-1)didian.add(tempStrings[4]+"n"+tempStrings[5]);
else didian.add(tempStrings[5]);
}
else if(tempStrings.length==10)
{
String myString = "";
if(tempStrings[3].indexOf("单周")!=-1)myString+="单周n";
else if(tempStrings[3].indexOf("双周")!=-1)myString+="双周n";
else myString+=tempStrings[3]+"n";
myString+=tempStrings[4]+"n";

if(tempStrings[8].indexOf("单周")!=-1)myString+="单周n";
else if(tempStrings[8].indexOf("双周")!=-1)myString+="双周n";
else myString+=tempStrings[8]+"n";
myString+=tempStrings[9];

didian.add(myString);
}
else if(tempStrings.length==12)
{
String myString = "";
if(tempStrings[3].indexOf("单周")!=-1)myString+="单周n";
else if(tempStrings[3].indexOf("双周")!=-1)myString+="双周n";
else myString+=tempStrings[3];
myString+=tempStrings[4]+tempStrings[5]+"n";

if(tempStrings[9].indexOf("单周")!=-1)myString+="单周n";
else if(tempStrings[9].indexOf("双周")!=-1)myString+="双周n";
else myString+=tempStrings[9];
myString+=tempStrings[10]+tempStrings[11];
didian.add(myString);
}

else {
didian.add(tempStrings[4]);
}
}else
{
mingcheng.add("");
xiangxi.add("");
didian.add("");
}
}
}catch(Exception e)
{}

做完了这个之后,我们觉得这个功能的应用场景应该是早上快迟到了,在文二楼找教室又忘记了在哪上课,掏出手机查课表的情况,于是乎我们想到,那时候肯定没网啊,于是离线课表做出来了.

其实说了这么久,你还不知道怎么选学期对不对?选学期呢,我们是通过这个方式来取出学期的(其实就是教务管理系统选学期的页面)

1
2
3
4
5
6
7

System.out.println("开始选学期");
result = POST("http://jwgl.btbu.edu.cn/tkglAction.do?method=kbxxXs", "");
p=Pattern.compile("<option value=".*?".*?>(.*?)</option>");
m=p.matcher(result);m.find();
while(m.find()){xueqiList.add(m.group(1));}

这里我们再一次发挥正则表达式的威力,很显然用正则表达式是十分方便的. 我们取出来了学期之后发现,十年都有了...可是大学才四年对不对!! 下面就是教务管理里面的选学期部分.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

<select name="xnxqh" id="xnxqh" style="width:100px" size=1>
<option value="">---请选择---</option>
<option value="2014-2015-1" selected>2014-2015-1</option>
<option value="2013-2014-2">2013-2014-2</option>
<option value="2013-2014-1">2013-2014-1</option>
<option value="2012-2013-2">2012-2013-2</option>
<option value="2012-2013-1">2012-2013-1</option>
<option value="2011-2012-2">2011-2012-2</option>
<option value="2011-2012-1">2011-2012-1</option>
<option value="2010-2011-2">2010-2011-2</option>
<option value="2010-2011-1">2010-2011-1</option>
<option value="2009-2010-2">2009-2010-2</option>
<option value="2009-2010-1">2009-2010-1</option>
<option value="2008-2009-2">2008-2009-2</option>
<option value="2008-2009-1">2008-2009-1</option>
<option value="2007-2008-2">2007-2008-2</option>
<option value="2007-2008-1">2007-2008-1</option>
<option value="2006-2007-2">2006-2007-2</option>
<option value="2006-2007-1">2006-2007-1</option>
<option value="2005-2006-2">2005-2006-2</option>
<option value="2005-2006-1">2005-2006-1</option>
<option value="2004-2005-2">2004-2005-2</option>
<option value="2004-2005-1">2004-2005-1</option>
<option value="2003-2004-2">2003-2004-2</option>
<option value="2003-2004-1">2003-2004-1</option>
<option value="2002-2003-2">2002-2003-2</option>
<option value="2002-2003-1">2002-2003-1</option>
<option value="2001-2002-2">2001-2002-2</option>
<option value="2001-2002-1">2001-2002-1</option>
<option value="2000-2001-2">2000-2001-2</option>
<option value="2000-2001-1">2000-2001-1</option>
<option value="1999-2000-2">1999-2000-2</option>
</select>

于是乎我们从学号的前两位入手,你最多只能上到你入学那个年份的学期啊!(比如13XXXXXXXX第一个学期是2013-2014-1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

List<String> xueqiList2 = new ArrayList<String>();
SharedPreferences sp = getSharedPreferences("data", 0);
String num = sp.getString("num_jiaowu", null);
num=num.substring(0,2);
int i;
for(i=0;i<xueqiList.size();i++)
{
String temp = xueqiList.get(i).toString();
xueqiList2.add(temp);
if(temp.indexOf(num)!=-1&(Integer.valueOf(temp.substring(temp.length()-1,temp.length()))==1))break;
}
String[] xueqiStrings = new String[xueqiList2.size()];
xueqiList2.toArray(xueqiStrings);

这里我们取出来了num的前两位,然后xueqiList循环取学期,出现了13且最后一位是1的话我们就break了(其实就是在找2013-2014-1) 这样我们的学期问题就给彻底解决了而且在学期末还可以查下个学期的课表

比如上图...就是我下个学期的课表.

然后原始课表这个不起眼的小按钮是打开了教务管理那个丑的原始页面.为什么要留这个功能呢?因为有周末要上课的同学反馈查不到周末的课表,但是我这里排版已经这样了,再压缩就显示不出来了,于是留下了原始课表这个功能.

这个问题十分复杂,我们首先看看我们为什么要去写上网登录:

我们可以看到,官方版(下载地址)大体上还是不错的,但是难用在以下几点

  • 不能保存密码,长密码每次打起来难受
  • 掉线三分钟才给提示,打不开网页但是又显示可以正在连接特别纠结
  • 掉线不能重连
  • 没有使用多线程导致wifi不好的时候登录特别卡
    经过一个月的折磨,我决定推出自己的版本:

然后我就尝试在电脑上利用古老的易语言开发了第一款可以掉线重连的上网登录器(源码)...

  • 可保存密码
  • 可保存多用户
  • 状态实时显示,可以查是否掉线
  • 流量条显示
  • 解除受限(wifi连上了但是就是受限没办法登录)

电脑版上网登录原理

好了,下面说它的了,其实它的原理跟上网登录网页版(http://weblogin.btbu.edu.cn)原理一模一样. 首先我们来了解一下网页版的登录方式. 举个例子,登录:登录实际上就是POST一个数据到服务器上. 参考资料:HTTP 方法:GET 对比 POST 我假设你此时已经明白什么是POST. 下面则是上网登录各种操作的通用格式.

  • http://weblogin.btbu.edu.cn/cgi-bin/netlogincgi.cgi?msajaxfix=时间戳

  • cinfo=cinfo&einfo=einfo&chgpwd=chgpwd&logout=Logout&login=Login&netlogincmd=操作命令&proxyip=127.0.0.1&newpassword=&password=密码&account=帐号 学校的上网规律是这样的,没有登录的时候首先要登录一下,之后每隔30秒发送一次保持在线的命令,如果超过一分钟没有保持在线就默认断开了,那么就需要重新登录.

  • 登录的操作命令为1

  • 改密码的操作命令为2

  • 查流量的操作命令为3

  • 保持在线的操作命令为5

  • 断开的操作命令为0

你是不是想问4是什么命令?我也不知道,因为我的操作过程中没有碰到过4.

让我们来总结一下,首先我们学会了POST,然后知道了学校的命令格式,我们根据这些格式就能够实现登录,改密码,查流量,保持在线和断开这几个操作,我们是不是可以开始写程序了呢?其实现在还需要一个重要的东西来满足我们的用户体验,它叫多线程技术.为什么要多线程?当你按下一个按钮,看到程序卡上几秒钟再弹出一个登录失败的窗口之后,你就明白多线程了.

我们把学校的登录和保持在线的逻辑写在程序里,然后再用多线程承载网络等耗时任务,就可以做出来上网登录的程序了.

上图是我应用得最多的一款单片机...因为它跟最小系统板没啥区别了...整个电路板非常简洁,已烧好bootloader,依靠串口烧写程序,功能与下图的板子没什么区别,价格仅需十块钱不到...因此小制作中大量采用此板.

这款板子最大的好处就是引出了大量IO口,舵机什么的可以直插,而且相比上面的板子增加了了3.3V电源,USB转串口,底板AMS1117稳压5V,因此可以接9V电池等.不过鉴于目前USB口非常多(比如移动电源),这个好处也没有那么明显.我最开始入门的时候用的这块板子,之后通常只在需要3.3V电源的时候才会用它.

这块板最大的好处应该就是IO口巨多,并且可以烧更大的程序(Atmega2560芯片,容量256kb),舵机一多的时候,就需要这款单片机了,因为它有多达16个PWM...之前的芯片数字口总数才13个...当然这个板子唯一的缺点就是贵啦...一块MEGA2560相当于最上面的小板五块了.

第一次用单片机的三个IO来控制串行数码管模块输出八位数字.

该模块的原理是利用2片74HC595级联实现控制8个数码管,一个控制显示哪一位数码管,一个控制显示什么数字.

这个图是百度百科上的74HC595的芯片引脚图.我们可以看到VCC和GND接电源,左边七个和右边那个Q0一共八个引脚用来输出,ST_CP,SH_CP,DS用来输入,MR保持低高电平,OE接地,如果需要级联,那么第一级的Q7'需要接第二级的DS,ST_CP和SH_CP共用即可.

时序图如下图:

(随便找的一张图)

大家看到这个时序图肯定会喷死我,这TM是个什么玩意,我来形象地讲解一下大致原理。 首先是数据怎么传入的。这里面的原理是一根数据线,一根时钟线,时钟线一旦从低到高(或者从高到低),我们就记录一次数据线上的电平高低,然后往里面压一位数据。比如我想传输10101010,我就首先让数据位输出0,然后让时钟线从低到高,再从高到低,也就是输出10,然后0就会被记录下来,然后我再依次推入1,0,1,0…… 数据就是这样变化的: 0 10 010 1010 01010 101010 0101010 10101010 然后数据就传输完毕了。 那么数据传输完毕之后,还要输出,我就把第三根线叫做更新线吧,更新线也10(高电平低电平)一次,那么芯片的八个引脚就会按照储存器里面的数据输出。如果没有这个更新的步骤,输出就不会变。你说我能不能不要这个更新,让他时刻都更新啊,那样的话除非你输入的00000000,只要有1就会在数据中来回游动,数码管就会八个led都亮了。视觉暂留现象。

其实这只是数据在芯片之间传输的一种方法,还一种很普遍的方法叫串口,它就没有时钟线什么的,只有两根线,一根输出一根输入(全双工),数据传输速度是一开始就商量好了的,之后我传输过去的东西你就按照时间去算,这个优点很明显,节省io,原理简单,缺点也明显,速度慢,一位错了就全错了。

0%