学习python不到一个星期,看的东西可能连皮毛也算不上,只是简单的看了几篇快速入门的技术博客,随手敲了敲代码,这也能算是入门吗 = =
由于之前的研究方向和这些没有太多交集,所以总觉得爬虫之类的东西很神奇,恰巧前些时间,学校里的一位老师“不小心” 的泄露了所有同学的“学号”+“身份证号”的组合,学生群里刚刚有人说这样不好,但是我已经知道了该怎样以这个为基础用python做个小玩具了。
这里主要完成了一个自动登录教务系统(哪个学校就不说了),抓取并下载考试成绩的爬虫程序,功能实现后代码没有什么优化,只是实现了想要的功能。最后从抓取的结果来看,自己成绩真的不怎么样,哎,说多了都是泪~~
1.环境&开发工具
python:强类型,隐式类型,动态性,大小写敏感,面向对象
版本:python2.7,安装过后需要配置环境变量path(类似java),此处不赘述
开发工具:从ST3又换到了IDLE。ST3的运行是Ctrl+B,停止运行Esc即可,但不知道为什么不能输入,所以换到了IDLE。个人还是倾向于ST。
2.语法/数据类型
单行注释:#
多行注释:多行#
数据类型:tuple,list,dictionary,代表符号依次为"()","[]","{}",前两个类似数组,区别是可变与不可变的关系,个人感觉用list的多些,一般情况下数组的返回类型也都是list,dictionary类似于hashmap,存储的是键值对。特别的是python的数据类型没有规定一定要使用某一种数据类型,也并没有固定的数据类型,所以以上几种数据类型中的每一项都可以是任意的数据类型,这种做法极大提高了编程的自由度。当然,二维数组什么的就可以用列表的列表来模拟,访问的时候也可以用"[][]"来访问,以上几种数据结构的访问也都是用"[]"来访问,只不过里面写的是数组的索引还是key的区别了。
3.文件IO
个人感觉python里的IO很简洁,在爬虫里主要用作:
①批量读入学生的学号和身份证号(账号和密码)
②批量向文本文件写入处理好的学生信息和成绩
写文件
f = file('src', 'w')
<pre>f.write('hello world')
其中,'w'参数设定为写入,每次会将原有存在内容覆盖,追加写入可以将参数变为'a',写入后不要忘了flush和close,可以用try&finally实现。对于写文件简单写这么多,其实感觉知识还是很多的,以后再做仔细研究吧~
读文件
f = open('stu.txt')
while True:
line = f.readline()
if(line):
#code...
这个也不多说,其实网上有很多资源。
提一下,我在写的时候,读取的每一行多了个换行符,还需要将这个换行去掉。
4.输入和输出
程序最后没有用到输入,主要用于控制台监测程序执行状况,也可以将写入文本内容输出。
输入:有以下两种方法,接受的分别是数字型的变量和字符串型的变量,可以用type(val)来进行查看,这里要注意,对于第一种输入,如果输入的是非数字,会报变量名找不到的错。参数是提示输入的内容。
numVal = input('input a num:')
strVal = raw_input('input a str:')
输出:简单粗暴的一个print,可以不加括号后面直接跟变量,变量后加','表示不换行,否则就是换行输出,这一点的设计非常好用,没有了那么多繁琐的输出方式。print(str1,str2)
5.OO
这个先不讨论,毕竟自己最后也没用到,python还是自由一些,小程序没必要一定要OO,但是OO也是一种编程习惯。
6.函数
使用def对函数进行声明,可以有返回值,也可以没有,可以返回元组,有必选参数和可选参数,可选参数必须赋默认值。
def funcName(para1, para2):
#code...
#return...
在函数中使用全局变量(虽然不建议使用全局变量)的话,就要注意一点:由于python的变量没有声明,所以是系统会把赋值的变量认为是本地变量,因此在函数中调用函数外部的全局变量如果直接写变量名,会变成局部变量。
若要访问全局变量,可以在函数中对全局变量重复使用global进行声明。
7.正则
这个对于处理下载的HTML文本是必须的,包括匹配并处理HTML文本,获取必要信息
由于之前学过一点,所以就直接拿来主义了
首先要"import re",才能使用正则的内置对象,主要就是以下两部,第一步建立匹配对象,第二部进行匹配以及对匹配内容进行操作。
p = re.compile(r'<[^>]+?>')
name = p.sub("", name)
方法有很多种,这里只用了sub(替换/删除)和findall(查找并返回元组),具体还有很多,现用现查也来得及,总之是可以满足任何需求的。
这里提个技巧,去除文本中所有的空格,换行,回车。其中join和split是互逆的两个方法。
right = opener.open('http://e.xxx.edu.cn/xxx/ax/ax').read()
squeezed_right = "".join(right.split()))
总之,这个现用现学完全来得及。8.异常处理
在不断的登录过程中,总会有失败的时候,失败的时候程序会报错,导致程序终止,这样与我们获取所有人的成绩的需求是不符的,因此使用了几层try except来预防同一个学生账号多次连接失败的情况,如果一个学生连接失败的概率是p,那么写n个try except的情况下,失败的概率会被降到p^(n+1),在最后的测试中没有出现问题,网上看到可以用sleep的方式来延迟程序登录的频率,也是一种好方法。但是后来又换了一个时间测试了一次,就比较顺利,这个也许是和网络环境也有一定的关系。
其他异常处理见网上各种资源。
9.爬虫入门
这个觉得也都是在用python的库,其实更多的东西都是靠知识的积累和细心。第一次写这类的东西,感觉新鲜感还是挺足的。
主要是下载页面,解析页面文本
代码什么的,见最后吧~
10.小细节
浏览器调试很重要,很多东西都是靠F12和自己的观察来完成,做登录的时候要看浏览器和服务端都发送了哪些信息,在chrome里,依次是F12→Network,在这里选中想要的http请求,查看headers就可以了。一般找到登录请求,就可以将登录需要的字段找到。爬取中会遇到字符编码的问题,如错误“UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-2: ordinal not in range(128)”,这时候网上通用的三段式做法如下:
import sys
reload(sys)
sys.setdefaultencoding('utf-8')
但修改后会发现,虽然不报错,但是程序的print,input等都不能用,这是因为重置sys后,输入输出都没有了定义,解决方法如下:
import sys
stdout = sys.stdout
stderr = sys.stderr
reload(sys)
sys.stdout = stdout
sys.stderr = stderr
sys.setdefaultencoding('utf-8')
11.成果
由于涉及到一点隐私,就不放太多的了,把名字也抹去了
12.结语
暑假期间,刚好打算学学这门语言,学习的过程还是挺有意思的,感觉学习的过程中还是需要有实践,在学习4天后写了这么个东西,大概有不到一半的同学没有修改密码,所以就获取到了同学们的成绩。其实,不如写点别的好玩的东西了,因为最后发现,除了有挂科的同学,我的成绩基本垫底,而且很少有挂科的同学 = =||,唉,成绩单不好看,真的不怪我,已经很努力了,好吧,就从其他地方弥补吧,还有一年,要加油了。
这也是CSDN的第一篇博客,之前的博客都是写在本机word里的,今天想想这样做毕竟是不好。至于这样做为什么不好,是有多方面原因的。这是这几天的学习和心得,以后有东西还是及时放上来,东西多了也就不想网上放了。
13.代码
初学,代码很丑,仅仅是实现了功能
# -*- coding: utf-8 -*-
import urllib
import urllib2
import cookielib
import traceback
import re
import sys
import time
stdout = sys.stdout
stderr = sys.stderr
reload(sys)
sys.stdout = stdout
sys.stderr = stderr
sys.setdefaultencoding('utf-8')
cnt = 0
tot = 0
def getInfo(uid, psw):
cookie = cookielib.CookieJar()
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookie))
#伪装成浏览器
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.125 Safari/537.36',
'Host':'e.tju.edu.cn',
'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Referer':'http://e.tju.edu.cn/stuslls/achi/achi',
'Cookie':'',
'Connection':'keep=alive'
}
#需要POST的数据#
postdata=urllib.urlencode({
'loginBean.username':uid,
'loginBean.password':psw
})
#自定义请求#
req = urllib2.Request(
url = 'http://e.tju.edu.cn/stuslls/login/login!login',
data = postdata,
headers = headers
)
#访问该链接#
result = opener.open(req)
top = opener.open('http://e.tju.edu.cn/stuslls/main/Top.jsp').read()
name_pattern = re.compile(r'<b>.+</b>')
#登录成功(该学生没有修改密码)
if len(name_pattern.findall(top))!=0:
global cnt
cnt += 1
name = name_pattern.findall(top)[0].decode("utf-8")
p = re.compile(r'<[^>]+>')
name = p.sub("", name)
print(name)
writeStr("result.txt", 'a', name)
right = opener.open('http://e.tju.edu.cn/stuslls/achi/achi').read()
scoreline_pattern = re.compile(r'<trbgcolor="#FFFFFF".+?</tr>')
scorelines = scoreline_pattern.findall("".join(right.split()))
for scoreline in scorelines:
td_pattern = re.compile(r'<td.+?</td>')
tds = td_pattern.findall(scoreline)
str_scoreline = ''
for td in tds:
td = td.decode("utf-8")
otd = p.sub(",",td)
str_scoreline += otd
print(str_scoreline)
writeStr("result.txt", 'a', str_scoreline.decode("utf-8"))
def writeStr(src, opType, data):
try:
f = file(src, opType)
f.write(data.decode("utf-8") + '\n\r')
finally:
f.flush()
f.close()
stuFile = open("stu.txt")
while True:
line = stuFile.readline()
tot +=1
if(line):
info = line.split(" ")
if(len(info)==2):
stuID = info[0]
stuPsw = info[1]
rmn_pattern = re.compile(r'\n')
rmr_pattern = re.compile(r'\r')
stuPsw = rmn_pattern.sub('',stuPsw)
print stuID
print stuPsw
try:
print str(cnt),'/',str(tot)
getInfo(stuID, stuPsw)
except:
time.sleep(2)
print 'second time!!!!!!!!!!'
getInfo(stuID, stuPsw)
else:
break