python学习笔记
今年初接触Python,实际下载使用最近不到一个月的时间。套用广为流传的一句话,“人生苦短,我用Python”再合适不用。
起因是网站爬虫,对象是公司内管理设备的内网,不堪频繁的手动操作,打起Python的主意。短短的三百行的代码,实现了自动登录、根据条件查找设备列表、远程执行批处理并收集结果、下载任意文件及文件夹。大大地提高了工作效率。
下面就分如下几个小方面进行逐一回顾(版本:Python 3.7.0)。
0,F12(开发人员工具)
要想进行相关的网络开发,熟悉这个F12是前提条件。但刚开始也走了些弯路,写下来供参考。Chrome及其衍生浏览器(如我常用的360极速)似乎没啥要注意的;IE的话,前提是WIN7默认自带的IE8版本及其以上才支持,但IE8下打开后只有四个选项卡----HTML、CSS、Javascript(脚本)、Profiler(探查器),缺少最为关键的Network(网络),这个功能在IE9以上才支持。
1,自动登录
这个功能其实是后来才实现的,原因是网站的密码在Post前调用JS函数进行了加密,这里费了些周折。其实通过execjs预先compile该JS文本, 就可call相应的JS函数,第一个参数为函数情况,第二个开始依次为函数的第一个参数。。。
另外一个,不要直接使用requests的get或post,而是s = requests.session()生成一个Session后通过这个s来调用get或post就方便多了,再也不用重复携带Header(包含Cookie,User-Agent等请求标头信息)
1 import requests 2 import execjs 3 4 # http地址 5 url = 'http://xx.xx.xx.xxx:8080/pages/' 6 7 ## [加载本地JS] 8 def get_js(jsFile): 9 f = open(jsFile, 'r', encoding='UTF-8') 10 line = f.readline() 11 htmlstr = '' 12 while line: 13 htmlstr += line 14 line = f.readline() 15 return htmlstr 16 17 jsstr = get_js("./JS/Encrypt.js") 18 ctx = execjs.compile(jsstr) 19 20 ## [执行本地JS加密函数] 21 account = 'userid' 22 password= 'passwd' 23 password2= ctx.call('strEncode', password, account, account, account) 24 data = {'account': account, 'password': password2} 25 26 ## [模拟登录&生成Session] 27 s = requests.session() 28 r = s.post(url + "login.do?action=login", data=data) 29 if r.status_code==200 and r.text.find('homepage.jsp')>=0: 30 loginRt = '登录成功' 31 else: 32 loginRt = '登录失败' 33 print('xxxx管理系统:' + loginRt + ' account=' + account + ' password=******')
2,文件上传
调用sessionr 的post方法,带上files参数就好。
1 import requests 2 3 # http地址 4 url = 'http://xx.xx.xx.xxx:8080/pages/' 5 6 ## [模拟登录&生成Session] 7 s = requests.session() 8 9 ##[上传文件] 10 files = {'file': open('./test.html', 'rb')} 11 r = s.post( 12 url + "docViewer.do?action=UploadFile&devNo=xxxx&path=D:\\", 13 files=files) 14 15 if r.status_code == 200 and r.text.find('成功') >= 0: 16 print('上传成功') 17 else: 18 print('上传失败')
3,下载文件
调用sessionr 的post方法,请求地址中带上get或post参数即可。需要注意的写本地文件时,一定要使用'wb'模式打开,write参数需要使用r.content而不是r.text,即当作一个二进制文件把请求的返回原始内容写入。
1 import requests 2 3 # http地址 4 url = 'http://xx.xx.xx.xxx:8080/pages/' 5 6 ## [模拟登录&生成Session] 7 s = requests.session() 8 9 ##[下载文件(未考虑大文件,需要后续优化)] 10 r = s.post( 11 url + "docViewer.do?action=DownloadFile&devNo=xxxx" + 12 "&fileName=test.html" + "&viewpath=D:\\", 13 ) 14 15 fDownloadOK = False 16 with open('./test.html', 'wb') as f: 17 f.write(r.content) 18 fDownloadOK = True 19 20 print('下载成功' if fDownloadOK else '下载失败')
4,html解析
解析方法库有很多,我只使用了最一般的xpath. 至少xpath其匹配的tag、参数、属性啥的,慢慢琢磨下一定有规律的,因为网站的原始信息就是使用模块生成的而不是一行一行手工写上去的(除非是故障混淆反爬过,至于怎么反反爬,我还没学习到)。
1 import requests 2 from lxml import etree 3 4 # http地址 5 url = 'http://xx.xx.xx.xxx:8080/pages/' 6 7 ## [模拟登录&生成Session] 8 s = requests.session() 9 10 r = s.get( 11 url + 'docViewer.do?action=ViewFile&devNo=xxxx' + '&viewpath=D:\\', 12 ) 13 html = etree.HTML(r.text) 14 result = html.xpath('//td[@width="25%"]/a/text()') 15 16 for e in result: 17 print (e)
5,INI文件读取
引入configparser,直接read整个INI文件,再调用get即可。但需要注意的是,如果INI文件本身不太规范,如同一section下的key重名,则会报错:configparser.DuplicateOptionError: While reading from 'xxxx.ini' [line xx]: option 'xxx' in section 'yyy' already exists,而这又常常不可避免的。这可能就需要try...except..来自动纠正再重读(尚未实现,仅仅是想法)。
1 import configparser 2 3 ## [读取INI文件] 4 def getINIValue(iniFile, section, key): 5 config = configparser.ConfigParser() 6 config.read(iniFile) 7 return config.get(section, key)
6,Reg文件读取
这个还着实让我花了些时间去查,如import winreg等等,但这些都是对注册表本身操作而不是注册表导出后reg文件。再仔细一看,大概齐就是INI文件,但直接读会报错:MissingSectionHeaderError: File contains no section headers. 这是因为reg文件的道行会有REGEDIT4或Windows Registry Editor Version 5.00表示注册表版本,而这行内容并不包含在任何一个section内。这可能也需要try...except..来自动纠正再重读(尚未实现,仅仅是想法)。
此外还有一个需要注意,注册表项目的名称在reg文件中使用双引号括上,依INI格式读取时需要有意识地加上;还有get到的值可能是字符串,也可能是数值,像是dword:00000064这样的,也需要特殊处理下。
7, 其他有意思的用法
7-1:in 和not in
如果查找一个元素是否在list中,使用它就对了,注意不要直接使用index,除非你确诊它就在里面,否则会报错:ValueError: xx is not in list
7-2:类似C++中的三目运算符
OKKKKK if 条件 else NGGGGG
7-3:没有++,和--,只有+=和-=
7-4:整除//,取余数%