这次的需求是自动登录某机构网站, 其验证码很具特色, 很适合做验证码识别入门demo, 先贴主要代码, 其中图片对比使用了编辑距离算法, 脚本使用了pillow库
1 | from PIL import Image |
字库已经部署到GitHub上, 链接
这次的需求是自动登录某机构网站, 其验证码很具特色, 很适合做验证码识别入门demo, 先贴主要代码, 其中图片对比使用了编辑距离算法, 脚本使用了pillow库
1 | from PIL import Image |
字库已经部署到GitHub上, 链接
每次写完代码, 打开FileZilla, 把写好的文件上传到vps上, 久而久之觉得腻烦, 寻思有没有更geek的方法, 便有此文.
WebHook是跟随着Git而兴起的技术, 当你push到服务器的时候, 服务器会发送一个特殊的请求到你指定的url上, 而我们可以使用脚本语言来获取这个请求并且在vps端执行git pull来达到自动部署的目的, 老规矩先贴代码:
1 |
|
把这段代码用FileZilla上传到vps上(最后一次使用FileZilla了<3), 把指向这个php文件的url填写到WebHook页面的deploy_url里面, 并且点击测试, 之后回到vps上查看MySQL是否已经有log, 再在开发机试试commit&push, 你会发现, 代码已经悄然部署到vps上了.
云对象存储服务, BAT都有其业务, 本文选择腾讯是因为腾讯有每月免费流量, 而阿里和百度都是需要先付费后使用, 另外新兴的像七牛云存储, 也是很不错的解决方案.
贴代码
1 |
|
这篇文章是前作的续篇(part 3), 为了达到最好的教学效果, 请务必先阅读前作.
在这篇续作当中, 我们将要开发一个简易文本阅读器, 简单起见, 这个阅读器只能阅读文本文档的内容, 但是, 只需要经过一些简单的修改就能成为一个全能的编辑器啦.
1 | import sys |
其实你应该能看懂绝大部分的代码, 我们定义的类继承与 QMainWindow
这个基类, 这样可以让我们把菜单栏放置到窗体的顶部.
12-15行: 使用信号和槽来让对象互相之间进行通讯, 这是Qt众多核心特征之一, 当一个事件发生的时候, 将会发出一个信号, 而槽则是Python里面的可调用的对象, 如果一个信号和一个槽相连接的话, 这个槽所指的对象将会在收到信号的时候被调用, 反之亦然.
当按下 Open 的时候, 相当于给 openFile
这个对象发出了调用信号, 同样的, 通过键盘快捷键来完成这一事件同样在代码中定义了.
22-25行: 定义了一个菜单组件, 并且给这个菜单组件添加了两个动作, 这样一来, 用户将会看到一个 File 菜单, 快捷键是 Alt+F, 当其被点击的时候, 将会出现含有两个选项的菜单.
第37行: 弹出一个浏览文件的对话框, 其中第二个参数是浏览文件对话框的标题内容, 第三个参数则是默认打开的目录, 这个目录默认是这个代码所在目录.
41-42行: 检测这个文件是否存在, 如果存在则把其赋值给fh这个变量.
44-45行: 尝试打开这个文件, 如果打开失败则退出程序.
47-49行: 把这个文件对象里面的内容全部读取出来, 然后尝试设置文件编码, 我们最先尝试Unicode编码, 如果失败了则使用Latin-1编码.
51-52行: 把窗体的标题改为文件路径, 并在前面加上 ‘Notepad’ .
我在底部留了一个状态栏的坑, 欢迎大家前来填坑~
希望你能喜欢这篇文章 :-)
这篇文章是前作的续篇(part 2), 为了达到最好的教学效果, 请务必先阅读前作.
在这篇续作当中, 我们依旧是通过实例代码来学习PyQt5, 我会涉及到下一篇教程(part 3)的一些内容.
1 | from PyQt5.QtCore import Qt |
我将会从第33行 Addressbook
这个类开始讲解, 因为大部分代码我在前作有讲解过, 我只会讲解前作没有涉及到的地方.
38-39行: 预先声明两个变量 oldName
和 oldAddress
以便后续代码调用.
42-43行: 我们声明一个文本编辑控件, 并且设置其只读属性为真, 当我们点击这个文本框的时候, 是无论如何都无法输入任何内容的.
49-58行: 我们为窗体添加一些按钮控件, 在第50行, show()方法会使 addButton
可见. 另外, submitButton
和 cancelButton
将会被我们隐式的创建. nextButton
和 previousButton
将会在窗体中显示出来, 但是他俩是灰色不可用的状态, 用户不能点击.
60-64行: 我们为控件添加点击触发的事件.
对于学习过前作的你来说, 看懂剩下的类简直是易如反掌了.
当我们第一次运行这个程序, 只有 Add
按钮是可以点击的, 这个按钮触发事件的代码在第87行.
88-89行: 还记得我们在38-39行定义的俩变量吗? 现在把 nameLine
和 addressText
的值赋给他俩.
91-96行: 我们把 nameLine
和 addressText
的文本框内容清空, 并且使用setReadOnly(false)
来让这俩变成可输入的状态, 最后把输入光标聚焦到nameLine
这里.
98-102行: 我们把 Add
, Previous
和 Next
这三个按钮禁用掉, 然后吧 Submit
和 Cancel
按钮设置为可见状态. 其中 cancel
按钮的出发事件代码从第137行开始.
138-139行: 还记得88-89行的那俩变量吗? 现在把 nameLine
和 addressText
的值赋回去.
141-143行: 但如果这些变量并没有存放任何数据, 我们便相应的设置文本框为空.
149-151行: self.contacts
是一个包含了我们的 address book 输入值的字典, 我们获取这个字典的大小并且赋给一个叫做 number
的变量, 如果输入的变量大于2个的话, 我们就把 nextButton
和 previousButton
设置为可用. 其中, Submit
按钮的触发事件代码在第104行开始.
105-106行: 把 name
和 address
这俩变量的值赋给文本框.
108-110行: 只要这些变量其中有为空值的, 我们就会报错.
113-120行: 这个字典的一个属性必须是唯一的键值对, 如果这个键值对不在我们的字典里面, 我们将会加到字典里并且显示一个 “success” 对话框. 同样的, 如果这个键值对存在于这个字典了, 我们就会显示错误. 其中, 字典的触发事件代码从第5行开始, 这个类会生成一个有序字典, 在代码的第36行被调用了.
7-11行: 初始化一些私有变量, _idx
是字典的索引, _nr_items
存放着字典的大小.
16-24行: 把列表进行排序, 当 _idx
比 _nr_items
大的时候, 将会触发一个exception, 而正常情况我们会步进字典, 这个方法返回以index为索引, 自增长为1的键值对.
如果你想了解更多关于Python字典的内容, see this page
157-158行: 这个方法会在我们点击 next
按钮的时候触发, 输入框的值会赋给 name
变量, iter()
方法会给字典存在的变量赋值, 返回不在字典的键.
160-168行: 我们同样准备了 try-catch 代码, 在第162行的try语句里, 我们给键值对赋值.
170-171行: 我们把 next_name
, next_address
的值赋给 nameLine
和 addressText
. 其中 previous
按钮的触发事件代码从第173行开始.
第176行: 初始化 prev_name
, prev_address
这两个变量, 并赋值为None.
177-182行: 我们迭代字典来储存 this_name
, this_address
这两个变量的值, 如果 this_name
等于 nameLine
里面的值, 我们就退出迭代, 反之则把 prev_name
, prev_address
赋给我们得到的变量.
189-190行: pass
这条语句本身毫无意义, 只是确保语法正确, 所以当 prev_name
为空时, 我们默认设置其值为空字符串.
希望你能喜欢这篇文章 :-)
学习php的商城开发遇到递归创建级联目录问题,在学习了php下面的解决方法后,不禁想用Python来实现一下
1 |
|
1 | import os |
二者实现思路一样,甚至使用函数都大同小异,另外二者皆有内置函数支持创建级联目录
从判断条件可以发现,php的dirname函数明显优于Python的os.path.dirname方法,后者在参数为单层目录的情况返回空字符串,而php返回表示当前目录的'.'
,这样导致Python需要多判断一种情况才能实现目标,希望Python后续版本可以借鉴一下php的这种方法~
前不久,学校弄了个SPOC网站,用JAVA写的,内容不多,但是网站的登录验证使用了少见的RSA算法对POST数据进行加密,不禁让我想到可否用Python来模拟用户登录,便有此文.
简单浏览网站后,得知加密算法是通过JavaScript实现,RSA键值对存放在登录页面html代码中的hidden属性的input标签里.
获取RSA键值对对于Python是很容易的,因为是静态存在于html内,而对于动态的算法,Python则显得无力,这时候便祭出Python的拜把子兄弟PhantomJS,使用PhantomJS处理JavaScript部分的算法,返回加密后的数据给Python继续进行POST操作.
JavaScript部分:
1 | var system = require('system'); |
Python部分:
1 | cmd = 'phantomjs E:\\demo.js' \ |
登录搞定了基本上相当于搞定了全部,之后你可以发帖,回帖,等等……玩法很多.甚至玩刺激点的可以把脚本挂上vps,然后……
最近学习Django框架, 是基于Python3的, 配置MySQL的时候出了点岔子, 因为MySQLdb目前还不能完美兼容Python3, 而Django的MySQL驱动只能识别MySQLdb, 于是便有此文
使用支持Python3的PyMySQL
而最关键的一点在于, 在站点目录下的__init__.py文件里面加上
1 | import pymysql |
这样就能让Django识别出伪装成MySQLdb的PyMySQL了
Django关于MySQL的配置代码
1 | DATABASES = { |
PyMySQL安装方法
1 | pip install pymysql |
2015/12/15更新
OOP版本
先贴上代码,注释都写的比较详细了
import urllib.request
import http.cookiejar
import re
import hashlib
import base64
re_csrf_token = re.compile(r'(?<="csrf_token" content=").+(?="/>)') # 预编译匹配csrf_token的正则表达式
re_switch_status = re.compile(r'(?<=<dataswitch>).+(?=</dataswitch>)') # 预编译匹配csrf_token的正则表达式
re_response = re.compile(r'(?<=<response>).+(?=</response>)') # 预编译匹配csrf_token的正则表达式
main_url = 'http://192.168.8.1/html/unicomhome.html' # 用于获取csrf_token的url
login_url = 'http://192.168.8.1/api/user/login' # 登录api
dataswitch_url = 'http://192.168.8.1/api/dialup/mobile-dataswitch' # 数据开关api
user = 'admin' # 用户名
psw = 'hldh214' # 密码
cookie = http.cookiejar.CookieJar() # 用cookiejar存储cookies
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cookie))
csrf_token = opener.open(main_url).read().decode()
g_requestVerificationToken = re_csrf_token.findall(csrf_token)
# 开始编码密码
psw1 = hashlib.sha256(psw.encode()).hexdigest()
psw2 = base64.b64encode(psw1.encode()).decode()
psw3 = user + psw2 + g_requestVerificationToken[0]
psw4 = hashlib.sha256(psw3.encode()).hexdigest().encode()
psd = base64.b64encode(psw4).decode()
# 编码结束
login_data = '''<?xml version="1.0" encoding="UTF-8"?><request><Username>''' + user + '''</Username><Password>''' + psd + '''</Password><password_type>4</password_type></request>''' # 构造post数据
opener.addheaders = [('__RequestVerificationToken', g_requestVerificationToken[0])] # 伪造csrf_token头
login = opener.open(login_url, login_data.encode()).read().decode() # 登录
switch_on_data = '''<?xml version="1.0" encoding="UTF-8"?><request><dataswitch>1</dataswitch></request>'''
switch_off_data = '''<?xml version="1.0" encoding="UTF-8"?><request><dataswitch>0</dataswitch></request>'''
switch_status = opener.open(dataswitch_url).read().decode()
switch_status = re_switch_status.findall(switch_status)[0]
if (not int(switch_status)):
csrf_token = opener.open(main_url).read().decode()
g_requestVerificationToken = re_csrf_token.findall(csrf_token)
opener.addheaders = [('__RequestVerificationToken', g_requestVerificationToken[0])]
data_switch_on = opener.open(dataswitch_url, switch_on_data.encode()).read().decode()
response = re_response.findall(data_switch_on)[0]
print(response)
else:
csrf_token = opener.open(main_url).read().decode()
g_requestVerificationToken = re_csrf_token.findall(csrf_token)
opener.addheaders = [('__RequestVerificationToken', g_requestVerificationToken[0])]
data_switch_off = opener.open(dataswitch_url, switch_off_data.encode()).read().decode()
response = re_response.findall(data_switch_off)[0]
print(response)
分析网关网页的JavaScript代码,发现设备只验证动态生成的csrf_token,通过正则表达式获取之,添加至头信息,之后就是常规GET/POST操作了
修改代码的第16-17行,输入自己设备的账号密码
这是一篇启蒙级的PyQt5教程,其目的是让你在很短的时间内入门PyQt.需要具备一些Python的基本知识.
PyQt是跨平台GUI工具包Qt的Python版本.是Python的GUI编程众多选择之一.其余的选择有PySide, PyGTK, wxPython, 和 Tkinter.
PyQt是开发非营利性(GPL协议)程序的利器.而如果你需要开发营利性程序,PySide很适合你,并且它是遵循LGPL协议的
你需要Python的最新版本(现在是 3.3.3 注1 ).确认安装完毕且添加了环境变量,添加环境变量在安装Python的时候可以选择添加.
以上完成以后,去Riverbank官网下载合适版本的可执行文件,安装的时候选择默认安装.
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
class Form(QWidget):
def __init__(self, parent=None):
super(Form, self).__init__(parent)
nameLabel = QLabel("Name:")
self.nameLine = QLineEdit()
self.submitButton = QPushButton("&Submit")
buttonLayout1 = QVBoxLayout()
buttonLayout1.addWidget(nameLabel)
buttonLayout1.addWidget(self.nameLine)
buttonLayout1.addWidget(self.submitButton)
self.submitButton.clicked.connect(self.submitContact)
mainLayout = QGridLayout()
# mainLayout.addWidget(nameLabel, 0, 0)
mainLayout.addLayout(buttonLayout1, 0, 1)
self.setLayout(mainLayout)
self.setWindowTitle("Hello Qt")
def submitContact(self):
name = self.nameLine.text()
if name == "":
QMessageBox.information(self, "Empty Field",
"Please enter a name and address.")
return
else:
QMessageBox.information(self, "Success!",
"Hello %s!" % name)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
screen = Form()
screen.show()
sys.exit(app.exec_())
1-2行: import必要的模块
第4行: QWidget是PyQt5里面关于用户界面的基类,所以你通过继承QWidget这个基类来创建一个新Form类.
5-6行: QWidget的构造函数.构造函数无父类,这将被定义为一个窗口.
7-9行: 添加一个标签,一个文本编辑框和一个提交按钮.
12-15行: 添加一个QVBoxLayout.QVBoxLayout类可以使widgets竖直显示.
第17行: 为提交按钮添加一个事件,事件为函数submitContact().
19-21行: 添加一个QGridLayout.
23-24行: 在设置完窗口标题之后设置QGridLayout为主窗口默认布局.
第27行: 使用nameLine
变量表示文本输入框内容.
29-35行: 当nameLine
无内容时通过弹窗提示,当其有内容时则弹窗输出内容文本.
至于剩下的代码就容易理解了.我们实例化一个Form
对象叫做screen
.使用show()
方法在屏幕上显示窗口.
然后我们开始程序主循环.这个循环会等待事件来处理,直到程序调用exit()
方法或主窗口被销毁.sys.exit()
注2 方法可以完成一个完美的退出,释放内存资源.
执行这个脚本只需要输入
python
来完成:
这是一篇入门级的教程.想要看看综合参考请点我
注1. 原文发表于January 23, 2014