python测试开发——测试工具开发GUI编程

1:json格式常见报错和关注点

import json
json1 = '{"information": False}'
print(json.loads(json1))    # json.decoder.JSONDecodeError: Expecting value: line 1 column 17 (char 16)
这里,python中转字典报错,在json里面true和false这两个关键字都是小写的,不会大写
json中一些关键字
  false  true  null(空值)
python中的一些关键字
  True  False  None
不同编程语言大小写空值表达方式注意
import json
json1 = '{"information": false, "information2": null}'
print(json.loads(json1), type(json.loads(json1)))    # {'information': False, 'information2': None} <class 'dict'>
false写成小写,python自己转字典的时候会转成自己的False大写格式
null表示空值,自己转化成None,空值和空字符串不同

json:
键值对的字符串,双引号

 2:列表和元组和字典关注点

列表元组值都可以重复,一个可以改变,一个不可以改变
服务端的ip 端口号 不希望通过接口可以修改--使用元组
('127.0.0.1',9999)

字典键值对

3:集合  set

定义:{1, 2, 3}
特点:去重,求交集并集很方便
a_list = [1, 2, 3, 4]
b_list = [2, 3, 4, 5]
# 求交集   & 符号,求交集    | 符号,求并集  - 符号,求差集
print(set(a_list) & set(b_list))    # {2, 3, 4}      交集
print(set(a_list) | set(b_list))    # {1, 2, 3, 4, 5}  并集
print(set(a_list) - set(b_list)) # {1}          差集

# 集合在数据处理时候数据过滤很方便

 4:def 函数  的参数类型

def test(a, b=0, *args, **kwargs):
    print(a, b, args, kwargs)
a:必填参数
b: 可缺省参数,不传递默认
args:可变数量参数,args是一个元组
kwargs:关键字可变数量参数,kwargs是一个字典
装包/封包:在函数定义的时候,args会把所有的位置参数封装成一个元组kwargs把所有关键字参数打包成字典
解包:在函数调用的时候,传递的实参需要解包:test(*[1, 2, 3, 4, 5, 6], **{"aaa":1, "bbb":2})   把数据类型解成每个元素,然后再传递进去   1 2 (3, 4, 5, 6) {'aaa': 1, 'bbb': 2}
装包:传进来的3 4 5 6打包成一个元组,后面的 变量=值 封装成一个字典 
解包:*list **dict
函数返回值:return
函数装饰器:不改源代码扩展目前代码功能

5:class 面向对象

1:类的定义  class
2:实例方法,静态方法,类方法
3:继承
4:多态--方法重写:父类与子类,一种方法两种不同表现,父类tear方法,子类重写tear方法,名字一样保证代码调用结构一样,类似一种方法在不同的子类和父类里有不同表现

 6:python常用的库

标准的库
datetime      为日期和时间处理同时提供了简单和复杂的方法。 zlib        直接支持通用的数据打包和压缩格式: zlib, gzip, bz2, zipile, 以及tarfile random       提供了生成随机数的工具。 math        为浮点运算提供了对底层C函数库的访问。 sys        工具脚本经常调用命令行参数。这些命令行参数以链表形式存储于sys模块的argy变量。 glob        提供了一个函数用于从目录通配符搜索中生成文件列表。 os         提供了不少与操作系统相关联的函数。
第三方库
Scrapy       爬虫工具常用的库
Requests     http库
Pillow       是PIL (Python图形库)的一个分支。适用于在图形领域工作的
matplotlib    绘制数据图的库。对于数据科学家或分析师非常有用。
OpenCV       图片识别常用的库,通常在练习人脸识别时会用到
pytesseract    图片文字识别,即OCR识别
wxPython      Python的一个GUI (图形用户界面)工具
Twisted      对于网络应用开发者最重要的工具。
SymPy        SymPy可以做代数评测、差异化、扩展复数等等。
SQLAlchemy    数据库的库。
SciPy        Python的算法和数学工具库。
Scapy        数据包探测和分析库。
pywin32      提供和windows交互的方法和类的Python库。
pyQT        Python的GUl工具。给Python脚本开发用户界面时次于wxPython的选择。
pyGtk        也是Python GUI库
Pyglet       3D动画和游戏开发引擎。
Pygame       开发2D游戏的时候使用会有很好的效果
NumPy        为Python提供了很多高级的数学方法。
nose         Python的测试框架。
nltk          自然语言工具包。
lPython       Python的提示信息。包括完成信息、历史信息、shel功能, 以及
BeautifulSoup    xmI和html的解析库,对于新手非常有用。

7:软件开发架构

应用类:C/S架构

web类:B/S架构,浏览器

8:socket网络编程简介

网络通信三要素:
  1:ip地址  互联网协议地址
  2:端口号  设备与外界通信交流的出口
ip地址精准定位到一天具体电脑机器,端口精准定位到某个应用程序
  3:传输协议
OSI七层:
  应用层
  表示层
  会话层
  传输层
  网络层
  数据链路层
  物理层
socket是位于传输层应用层之间的一个抽象层
socket通信流程:
tcp个udp两种套接字
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口
在设计模式中, Socket其实就是一 个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据, 以符合指定的协议
TCP和UDP协议:

  TCP协议:

 

  UDO协议:

socketserver实现多人聊天:
  server:
import socketserver
# 1- 需要继承一个类
class sqServer(socketserver.BaseRequestHandler):
    def handle(self):
        print('----聊天服务器上线了----')
        # 处理逻辑,不需要处理监听连接什么,都封装好了,只需要处理收发逻辑
        while True:
            # 接收数据
            client_data = self.request.recv(1024)
            print(client_data.decode('utf-8'))
            # if  xxx: break
            # 发数据
            send_data = input('请输入>>> ')
            self.request.sendall(send_data.encode('utf-8'))
        self.request.close()
# 2- 创建服务
server = socketserver.ThreadingTCPServer(('127.0.0.1', 8888), sqServer)
# 3- 一直在线
server.serve_forever()

  client:

import socket
sk = socket.socket()
sk.connect(('127.0.0.1', 8888))
while True:
    send_data = '\na: ' + input('请输入>>> ')
    sk.sendall(send_data.encode('utf-8'))  # 要求是byte
    server_data = sk.recv(1024).decode('utf-8')
    print('接收的客户端数据>>> ', server_data)
sk.close()
socket套接字家族:

9:编写一个端口扫描工具   v1.0

输入一个ip地址,获取对应机器开放的端口
  性能安全测试,项目端口被预留后面,没有及时关闭,有时候可能被扫码到通过技术手段去攻破项目
现成的有 nmap 扫描工具
socket发送请求到服务器扫码端口是否开放
端口6w多个,一个个扫很慢,多线程技术去扫,使用多线程,多进程和协程等技术优化扫码效率
socket变成+多线程技术实现

端口扫码原理:
  通过端口访问服务,端口开启存在很多风险,一些应用运行自带开启端口
  pc机器外部查看服务器开启的端口
  nmap端口扫码工具,windows和linux版本,注入,端口异常开放影响安全性
  协议方式访问目标服务的端口,看是不是open的有返回端口就是open的没有返回或者返回异常端口就是close
v1.0设计方案规划:
  1:本机和服务器建立tcp连接
  2:查看连接返回
  3:判断连接返回值
  4:循环扫描剩余端口
import socket
def scan_tool(ip, port):
    sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 网络tcp套接字
    sk.settimeout(0.5)  # 设置连接超时时间
    # sk.connect()  # 初始化tcp连接,连接出错返回socket.error错误
    try:
        res = sk.connect_ex((ip, port))  # connect() 扩展版本,连接出错时返回错误码,不抛出异常  0:连接成功,否则连接失败
        if res == 0:
            print(f"服务器:{ip},端口:{port}  开放")
        else:
            print(f"服务器:{ip},端口:{port}  未开放")
    except Exception as e:
        print("扫描异常", e)
if __name__ == '__main__':
    scan_tool("220.181.38.251", 80)

10: 端口扫描工具   v1.1

import socket
import re
def check_ip(ip): """IP检测""" ip_address = re.compile(r'^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$') if ip_address.match(ip) and len(ip) >= 5: return True else: return False
def check_domainName(domain): """域名检测""" domain_address = re.compile(r'[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?') if domain_address.match(domain): return True else: return False
def domain_scan(domain): server_ip = socket.gethostbyname(domain)  # 域名转ip使用gethostbyname print(f"域名:{domain},ip:{server_ip}") return server_ip
def scan_tool(ip, port): startPort, endPort = port.split(",") for one in range(int(startPort), int(endPort)+1): sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 网络tcp套接字 sk.settimeout(0.5) # 设置连接超时时间 # sk.connect() # 初始化tcp连接,连接出错返回socket.error错误 try: res = sk.connect_ex((ip, one)) # connect() 扩展版本,连接出错时返回错误码,不抛出异常 0:连接成功,否则连接失败 if res == 0: print(f"服务器:{ip},端口:{one} 开放") else: print(f"服务器:{ip},端口:{one} 未开放") except Exception as e: print("扫描异常", e) sk.close() # 关闭不然占用资源

def assert_ip_domain(ip, port): if check_ip(ip): ip = ip elif check_domainName(ip): ip = domain_scan(ip) else: print("请输入正常的ip或者域名地址") scan_tool(ip, port)
if __name__ == '__main__': assert_ip_domain("220.181.38.251", "1,100") assert_ip_domain("www.baidu.com", "1,30")

11:多线程并发编程

并发:指的线程,进程相互切换运行
并行:多进程,完全独立一起运行

I/O密集型(阻塞):不干啥,一直sleep,request,socket 各种操作需要等待的场景比较合适
计算密集型:多线程和协程不会省时间,线程切换也需要耗费时间

端口扫码工具就是高I/O阻塞任务,可以使用多线程

全局解释器锁(GIL):多线程同一时间就一个线程运行,来回切换
多线程:不安全的并发,需要使用lock锁
守护线程:主线程退出进程的时候不需要等待子线程(设置了守护线程的子线程)运行结束,直接退出就行了,主线程结束整个进程就结束  
t1.setDaemon(True)

t1.join():阻塞主线程,等t1子线程运行完了后再运行主线程,主线程退出前保证子线程都退出子线程才退出
# 多线程不安全的并发
import threading
import time

balance = 1000

def test(num):
    global balance
    user_balance = balance
    user_balance += num
    time.sleep(2)
    balance = user_balance

start_time = time.time()
t1 = threading.Thread(target=test, args=(500,))     # 赚500
t2 = threading.Thread(target=test, args=(-200,))    # 减少200
for t in [t1, t2]:
    t.start()
for t in [t1, t2]:
    t.join()
print(balance)  # 打印800或者1500
print(time.time() - start_time)  # 2.005124807357788
理论上需要是1300,多条账目异常,无法保证金额正确
# lock锁解决不安全的并发
# 函数1操作全局变量的时候上锁数据不让别人改变数据的值,我操作完之后解锁别人才能进来操作,这样就不会交叉了
import threading
import time

balance = 1000

def test(num, in_lock):
    global balance
    in_lock.acquire()   # 上锁
    user_balance = balance
    user_balance += num
    time.sleep(2)
    balance = user_balance
    in_lock.release()   # 计算完结果释放锁

start_time = time.time()
lock1 = threading.Lock()
t1 = threading.Thread(target=test, args=(500, lock1))  # 赚500
t2 = threading.Thread(target=test, args=(-200, lock1))  # 减少200
for t in [t1, t2]:
    t.start()
for t in [t1, t2]:
    t.join()
print(balance)  # 1300
print(time.time() - start_time)  # 4.010311841964722
类似变成顺序执行了,降低了执行速度但是增加了安全性
# 死锁  同步锁造成死锁
import threading
import time

lockA = threading.Lock()  # 面试官的锁
lockB = threading.Lock()  # 小明的锁

# 面试官的任务函数
def foo1():
    lockA.acquire()  # 面试官先上一把锁,对a资源上个锁
    print("请回答问题")
    time.sleep(1)

    lockB.acquire()  # 小明也上了一把锁,对b资源上一个锁
    print("发offer")
    time.sleep(1)

    lockA.release()  # 解锁操作
    lockB.release()  # 解锁操作

# 小明的任务函数
def foo2():
    lockB.acquire()
    print("发offer")  # 请给面试官的资源
    time.sleep(1)

    lockA.acquire()
    print("请回答问题")  # 请给小明的资源
    time.sleep(1)

    lockA.release()
    lockB.release()

t1 = threading.Thread(target=foo1)
t2 = threading.Thread(target=foo2)
t1.start()
t2.start()
# 递归锁解决死锁问题  Rlock,计数锁,内部维护一把锁和一个计数器,每次上锁计数器+1,解锁计数器-1,计时器可以大于0等于0不能小于0,计数器未0可以别的线程就可以进来Rlock上锁了
import threading
import time
lockR = threading.RLock()  # 递归锁,也称之为可重入锁
'''
递归锁特点:递归锁内部维护一个计数器和一把锁,每次上锁计时器+1,每次解锁计数器-1
计数器可以大于0也可以等于0,不能小于0(无锁可解除会出问题)
'''

# 面试官的函数
def food1():
    lockR.acquire()
    print("请回答问题")
    time.sleep(1)

    lockR.acquire()
    print("发offer")
    time.sleep(1)

    lockR.release()
    lockR.release()

def food2():
    lockR.acquire()
    print("发offer")
    time.sleep(1)

    lockR.acquire()
    print("请回答问题")
    time.sleep(1)

    lockR.release()
    lockR.release()

t1 = threading.Thread(target=food1)
t2 = threading.Thread(target=food2)

'''
t1启动,t2启动,foo1--locka上锁,print后等待1s,这时候cpu去执行t2,loob上锁,print后等待1s,又取执行food1,
loob还没来得及解锁,lockb又要上锁(所以需要上递归锁,两个任务需要占有你需要的资源,)
我需要你的资源,我占有你需要的资源,你占有我需要资源,互相想要对方的资源,
数据就是一个资源,上锁后就是我占用资源,需要解锁后这个资源才能别人使用
'''
t1.start()
t2.start()
Rlock代码又变成顺序执行了。先执行t1再执行t2,t1线程Rlock上锁2次后必须Rlock解锁两次t2线程才能操作数据
# 守护线程  t.setDaemon(True)
import threading
import time

balance = 1000

def test(num, in_lock):
    global balance
    in_lock.acquire()   # 上锁
    user_balance = balance
    user_balance += num
    time.sleep(2)
    balance = user_balance
    in_lock.release()   # 计算完结果释放锁

start_time = time.time()
lock1 = threading.Lock()
t1 = threading.Thread(target=test, args=(500, lock1))  # 赚500
t2 = threading.Thread(target=test, args=(-200, lock1))  # 减少200
for t in [t1, t2]:
    t.setDaemon(True)
    t.start()

print(balance)  # 1000
print(time.time() - start_time)  # 0.0019948482513427734
主线程不会等t1和t2这两个子线程,主线程运行完后退出进程,整个进程结束,子线程t1和t2这时候没有运行结束被迫gg
主线程结束子线程也结束,直接退出不需要等待子线程

设置守护线程需要在线程start启动之前
使用场景:
1:只有start没有join和setDaemon    所有的线程全部运行完成,主线程结束后,子线程继续运行,直到所有线程都结束进程才结束
2:有start+join,没有setDaemon     主线程结束前会等待加了join的全部子线程全部运行完成再结束-----join阻塞
3:有start+setDaemon,没有join     守护线程,主线程结束所有子线程全部中止运行,子线程中断 

11:端口扫描工具   v1.2  多线程+解决域名版本

import socket
import re
import time
import threading

def check_ip(ip):
    """IP检测"""
    ip_address = re.compile(r'^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$')
    if ip_address.match(ip) and len(ip) >= 5:
        return True
    else:
        return False

def check_domainName(domain):
    """域名检测"""
    domain_address = re.compile(r'[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?')
    if domain_address.match(domain):
        return True
    else:
        return False

def domain_scan(domain):
    server_ip = socket.gethostbyname(domain)
    print(f"域名:{domain},ip:{server_ip}")
    return server_ip

def scan_tool(ip, port, port_list):
    """扫码函数, 只需要ip和端口就行了,做个异常捕获捕获异常"""
    sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 网络tcp套接字
    sk.settimeout(0.5)  # 设置连接超时时间,socket连接超时连接不上很慢
    # sk.connect()  # 初始化tcp连接,连接出错返回socket.error错误
    try:
        res = sk.connect_ex((ip, port))  # connect() 扩展版本,连接出错时返回错误码,不抛出异常  0:连接成功,否则连接失败
        if res == 0:
            print(f"服务器:{ip},端口:{port}  开放")
            port_list.append(port)
    except Exception as e:
        print("扫描异常", e)
    sk.close()  # 关闭不然占用资源

def thread_scan(ip, ports='0,65535', port_list=[]):
    """多线程函数封装,开启多个线程"""
    thread_list = []    # 存放线程对象
    start_time = time.time()
    startPort, endPort = ports.split(",")
    for one in range(int(startPort), int(endPort) + 1):
        t = threading.Thread(target=scan_tool, args=(ip, one, port_list))
        thread_list.append(t)   # 线程对象放到列表
    [t.start() for t in thread_list]    # 启动所有的线程
    [t.join() for t in thread_list]     # 阻塞主线程等待所有子线程运行完成
    export_data([f"服务器ip:{ip},端口:{port},已开放!" for port in port_list])
    end_time = time.time()
    print(f"开发的端口数目{len(port_list)}:开发的端口{port_list}")
    print(end_time - start_time)

def assert_ip_domain(in_data, port):
    """in_data可能输入ip或者域名"""
    if check_ip(in_data):   # 如果in_data输入的是ip
        thread_scan(in_data, port)
    elif check_domainName(in_data):  # 如果in_data输入的是域名,那么域名转ip
        thread_scan(domain_scan(in_data), port)
    else:
        print("请输入正常的ip或者域名地址")

def export_data(info_list):
    with open("port.txt", 'a') as fo:
        for data in info_list:
            fo.write(data + ".\n")

if __name__ == '__main__':
    # assert_ip_domain("220.181.38.251", "1,100")
    assert_ip_domain("www.baidu.com", "1,65535")
# 当前版本还是存在问题,多线程版本,漏了很多端口没有放到列表里,可能请求刚发出去来不及响应就认为超时了,设置了0.5s超时
# 当前多线程版本,6w多个线程而且扫码端口不稳定,很多端口没有扫到
# 多线程使用线程池去做,开6w多个线程造成扫码不稳定
# 开了几万个线程,都已经跑起来了,还没轮询到其他的几万个就判断超时了,所以漏了

12:并发编程之——协程  gevent

协程:只有1个线程,协程内部完成切换,更轻量级的线程,不需要开辟线程执行的时候来回切换线程,节省资源
  协程的切换由程序自身控制,不需要借助操作系统时间片轮转,比起线程更底开销,性能优势更加明显
  协程的运行因为是在一个线程里,不存在锁的概念
  协程无法使用多核,多线程也无法使用多核

  python最高效的使用多核cpu的方法是:多进程+协程
可以理解成线程里面微型的子线程,不存在线程切换,所有的协程都是在同一个线程里面,本质上类似一个线程里执行多个任务而已
t1 = gevent.spawn(f1)    # 创建一个greenlet协程对象
t2 = gevent.spawn(f2)    # 创建一个greenlet协程对象
gevent.joinall([t1, t2])    # 等待所有传入的greenlet协程运行结束后再退出(所有的协程执行完成之后线程才能退出)

from gevent import monkey;monkey.patch_all()  # 把所有其他的库打包成协程库,遇到I/O阻塞切换协程运行
import gevent
import time
from gevent import monkey
monkey.patch_all()  # 对下面的标准库打上猴子补丁,将下面所有标准库都替换,变成非阻塞模式,遇到阻塞切换到其他协程运行
# monkey.patch_all() 这里让下面的的I/O等待变成非阻塞,下面的sleep变成非阻塞,碰到sleep和I/O等待切换到其他协程运行

def f(num, in_data):
    for i in range(0, 5):
        print(f"f{num}正在运行:{i}", end="")
        print(in_data)
        time.sleep(1)

start_time = time.time()
g1 = gevent.spawn(f, 1, "牛皮")  # 这就是创建协程并且开启
g2 = gevent.spawn(f, 2, "就是牛皮")  # 这就是创建协程并且开启
gevent.joinall([g1, g2])  # join阻塞,每个协程运行完成后主线程才能运行,这里协程运行完成之后才运行后面的时间
print(time.time() - start_time)  
# 协程池  gevent.pool.Pool
g = gevent.pool.Pool(200)  # 限制协程并发数量,200左右个就可以,其他协程提交的时候阻塞等待
for port in range(1, 65535 + 1):

  runList.append(g.spawn(scan_port, ip, port))

端口扫码工具协程池设置太大可能导致异常,扫描不到端口,线程池搞小一点,几百就行

13:端口扫描工具   v1.3  协程版本

# 协程池   pool,限制最多200个协程一起运行
"""
测试反馈: 1、是否可以自动识别ip或者域名!---在输入的时候判断! 2、如果全扫描--发现很慢!---------因为单线程的跑,效率低---建议使用多线程/协程! 3、如果出现扫描结果不一样,可以使用取并集 set1 |set 2----一个思路! 优化方案: 1- 协程---并发操作 """ # 1- ip的有效判断-re 0.0.0.0~255.255.255.255 import socket import re import timeXQ import gevent import gevent.pool from gevent import monkey monkey.patch_all() def check_ip(ip): """ :param ip: 输入 :return: 是否符合规则 """ ip_address = re.compile('((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}') # 正则表达式对象 if ip_address.match(ip) and len(ip) != 0: # 满足ip规则并且是有数字 return True else: return False def check_domain(hostname): """ :param ip: 输入 :return: 是否符合规则 """ domainName = re.compile('[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?') # 正则表达式对象 if domainName.match(hostname) and len(hostname) != 0: # 满足ip规则并且是有数字 return True else: return False # 2- 端口扫描函数 def scan_port(ip, port): # 分段 # 访问成功是有返回值--是0 # 使用ip 实现 tcp连接 sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.settimeout(0.5) try: conn = sk.connect_ex((ip, port)) # 有返回值('127.0.0.1',int型) if conn == 0: print(f'主机:{ip},端口:{port}已开放') except: pass sk.close() def gevent_scan_port(ip): start_time = time.time() # 开始计时 g = gevent.pool.Pool(200) # 限制协程并发数量,200左右个就可以 runList = [] # 运行的协程 for port in range(1, 65535 + 1): runList.append(g.spawn(scan_port, ip, port)) gevent.joinall(runList) # 运行完,主线程退出 阻塞 end_time = time.time() print('端口扫描总共耗时>>> ', end_time - start_time) # 3- 直接使用ip-扫描 def scan_ip(ip): # 2- 使用ip判断函数--判断 gevent_scan_port(ip) # 4- 使用域名扫描---需要解析ip地址! def domain_name_scan(domainName): # 1- 输入域名 # 2- 可以做域名判断 if "http://" in domainName or "https://" in domainName: domainName = domainName[domainName.find("://") + 3:] # 取前写后下标 print("正在解析的域名>>> ", domainName) # 2- 获取ip server_ip = socket.gethostbyname(domainName) # print(f"该域名{domainName}的ip>>> {server_ip}") # 3- 进入扫描端口 gevent_scan_port(server_ip) # 5- 主入口 def main(): info = input("请选择ip或者域名>>> ") if check_ip(info): # ip扫描 scan_ip(info) elif check_domain(info): domain_name_scan(info) else: print("输入ip或者域名有误!") if __name__ == '__main__': main()
# 不使用协程池,就一直开启6w多个协程
import
socket import re import time import gevent from gevent import monkey monkey.patch_all() def check_ip(ip): """IP检测""" ip_address = re.compile(r'^((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}$') if ip_address.match(ip) and len(ip) >= 5: return True else: return False def check_domainName(domain): """域名检测""" domain_address = re.compile(r'[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?') if domain_address.match(domain): return True else: return False def domain_scan(domain): server_ip = socket.gethostbyname(domain) print(f"域名:{domain},ip:{server_ip}") return server_ip def scan_tool(ip, port, port_list): """扫码函数, 只需要ip和端口就行了,做个异常捕获捕获异常""" sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 网络tcp套接字 sk.settimeout(0.5) # 设置连接超时时间,socket连接超时连接不上很慢 # sk.connect() # 初始化tcp连接,连接出错返回socket.error错误 try: res = sk.connect_ex((ip, port)) # connect() 扩展版本,连接出错时返回错误码,不抛出异常 0:连接成功,否则连接失败 if res == 0: print(f"服务器:{ip},端口:{port} 开放") port_list.append(port) except Exception as e: print("扫描异常", e) sk.close() # 关闭不然占用资源 def gevent_scan(ip, ports='0,65535', port_list=[]): """多线程函数封装,开启多个线程""" print(111) greenlet_list = [] # 存放协程对象 start_time = time.time() startPort, endPort = ports.split(",") for one in range(int(startPort), int(endPort) + 1): g = gevent.spawn(scan_tool, ip, one, port_list) greenlet_list.append(g) # 协程对象放到列表 gevent.joinall(greenlet_list) export_data([f"服务器ip:{ip},端口:{port},已开放!" for port in port_list]) end_time = time.time() print(f"开发的端口数目{len(port_list)}:开发的端口{port_list}") print(end_time - start_time) def assert_ip_domain(in_data, port): """in_data可能输入ip或者域名""" if check_ip(in_data): # 如果in_data输入的是ip gevent_scan(in_data, port) elif check_domainName(in_data): # 如果in_data输入的是域名,那么域名转ip gevent_scan(domain_scan(in_data), port) else: print("请输入正常的ip或者域名地址") def export_data(info_list): with open("port.txt", 'a') as fo: for data in info_list: fo.write(data + ".\n") if __name__ == '__main__': assert_ip_domain("220.181.38.251", "1,65535")
# 协程+文件
"""
测试反馈: 优化方案: 1- 协程---并发操作 2- 增加导出结果文件 """ #-------------------------------------- #1- ip的有效判断-re 0.0.0.0~255.255.255.255 import socket import re def check_ip(ip): """ :param ip: 输入 :return: 是否符合规则 """ ip_address = re.compile('((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})(\.((2(5[0-5]|[0-4]\d))|[0-1]?\d{1,2})){3}')#正则表达式对象 if ip_address.match(ip) and len(ip)!=0:# 满足ip规则并且是有数字 return True else: return False def check_domain(hostname): """ :param ip: 输入 :return: 是否符合规则 """ domainName = re.compile('[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?')#正则表达式对象 if domainName.match(hostname) and len(hostname)!=0:# 满足ip规则并且是有数字 return True else: return False #导出文件 def export_file(info): fo = open("scanPort.txt","a",encoding="utf-8") fo.write(info) fo.close() #2- 端口扫描函数 def scan_port(ip,port):#分段 #访问成功是有返回值--是0 # 使用ip 实现 tcp连接 sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sk.settimeout(0.5) try: conn = sk.connect_ex((ip,port))#有返回值('127.0.0.1',int型) if conn == 0: print(f'主机:{ip},端口:{port}已开放') export_file(f'主机:{ip},端口:{port}已开放\n') except: pass sk.close() #多线程-扫描 import time import gevent from gevent import monkey monkey.patch_all() import gevent.pool def gevent_scan_port(ip): start_time = time.time()#开始计时 g = gevent.pool.Pool(200)#限制协程并发数量 建议不要太多! 200左右 export_file(f'主机:{ip},开始扫描......\n') runList= []#运行的协程 for port in range(1,65535+1): runList.append(g.spawn(scan_port,ip,port)) gevent.joinall(runList)#运行完,主线程退出 阻塞 end_time = time.time() print('端口扫描总共耗时>>> ',end_time-start_time) export_file(f'主机:{ip},扫描结束......\n') #3- 直接使用ip-扫描 def scan_ip(ip): #2- 使用ip判断函数--判断 gevent_scan_port(ip) #4- 使用域名扫描---需要解析ip地址! def domain_name_scan(domainName): #1- 输入域名 #2- 可以做域名判断 if "http://" in domainName or "https://" in domainName: domainName = domainName[domainName.find("://")+3:]#取前写后下标 print("正在解析的域名>>> ",domainName) #2- 获取ip server_ip = socket.gethostbyname(domainName)# print(f"该域名{domainName}的ip>>> {server_ip}") #3- 进入扫描端口 gevent_scan_port(server_ip) #5- 主入口 def main(): info = input("请选择ip或者域名>>> ") if check_ip(info):#ip扫描 scan_ip(info) elif check_domain(info): domain_name_scan(info) else: print("输入ip或者域名有误!") if __name__ == '__main__': main()

14:GUI编程

Tkinter:python最简单的图形化模块,14种组件,简单图形化使用这个
Pyqt:python最复杂也是最广泛的图形化,第三方的闭源的
Wx:居中的图形化
Pywin:python windows下的模块,摄像头控制(opencv),用于外挂制作
PySide2:LGPL协议,作为库使用程序可以是闭源商用
  不做商业项目使用PyQt,开发商用软件使用PySide,
  
Pyqt和PySide2语法百分之80类似,基本一样
gui编程:
  1:界面
  2:逻辑代码,后台
  界面代码和逻辑代码分离
  ui设计+py逻辑处理独立,相互调用
PySide2环境搭建:
  1:pip install PySide2
  2:进入python目录找到PySide2目录,双击designer.exe  C:\Users\A22477\AppData\Local\Programs\Python\Python39\Lib\site-packages\PySide2
  3:pycham关联
designer  pycham——>file——>setting——>Tools——>External Tools——>
    QtD
esigner
    
C:\Users\A22477\AppData\Local\Programs\Python\Python39\Lib\site-packages\PySide2\designer.exe
    
$FileDir$
  4:pycham关联PyUIC  设计了gui的时候可以直接转python代码,不建议使用    C:\Users\A22477\AppData\Local\Programs\Python\Python39\Scripts
    pycham——>file——>setting——>Tools——>External Tools——>
      
PyUIC
      -o $FileNameWithoutExtension$.py $FileName$
      
C:\Users\A22477\AppData\Local\Programs\Python\Python39\Scripts\pyside2-uic.exe
      $FileDir$
  5:
designer  设计页面保存到项目目录里比如一个 登录.ui
  6:
登录.ui  右键——>External Tools——>PyUIC    登录的ui会自己生成 登录.py 代码
    这种方式ui文件设计的东西全部都标记了,很多不必要属性都开发出来了,控制不了逻辑,维护麻烦,不建议使用这种自动生成代码方式

15:Designer的使用

https://www.jianshu.com/p/60fe3edb3fd8
http://www.bl186.net/
Qt Designer常用的组件:
  Layouts:布局
  Spaces:可缩放的空间
  Buttons:按钮

编辑元素的动作:

 主界面3个元素

 可以在查看对象器看到

 信号编辑器编辑元素之间的动作,pushbutton这个按钮click点击后会清除lineedit这个组件的内容

# ui文件导入到py代码里成功一个ui对象8步骤,固定写法
from PySide2.QtWidgets import QApplication   # 导入应用对象
# 方式一:ui转py代码文件或者代码导入ui文件
# 方式二:ui文件加载到代码读取,识别ui对象    界面ui文件需要导入到代码里面
from PySide2.QtUiTools import QUiLoader
from PySide2.QtCore import QFile    # 导入读取ui文件的库


# 需要一个应用程序对象
app = QApplication([])  # sys.argv

# 获取ui文件,找到ui文件
qFile = QFile('登录.ui')  # ui文件放到这类里创建一个页面对象

# 打开ui文件 QFile.ReadOnly只读模式打开
qFile.open(QFile.ReadOnly)

# 加载ui对象
ui = QUiLoader().load(qFile)

# 关闭qFile 文件,ui对象加载完成后打开的ui文件可以关闭,qFile文件加载到ui对象了可以关闭  
qFile.close()
# 文件导入到代码稳定8步骤,固定写法,ui文件名修改就行
# 文件导入到内存里代码来执行

# 显示ui对象,执行ui对象
ui.show()

# 运行应用对象
app.exec_()
# 主逻辑模板
# 编写一个简单的登录界面  使用Designer设计一个登录界面后使用下面代码导入界面对象,然后绑定各种事件函数
from PySide2.QtWidgets import QApplication   # 导入应用对象
# 方式一:ui转py代码文件或者代码导入ui文件
# 方式二:ui文件加载到代码读取,识别ui对象    界面ui文件需要导入到代码里面
from PySide2.QtUiTools import QUiLoader
from PySide2.QtCore import QFile    # 导入读取ui文件的库

# 需要一个应用程序对象
app = QApplication([])  # sys.argv

# 获取ui文件,找到ui文件
qFile = QFile('登录页面.ui')  # ui文件放到这类里创建一个页面对象

# 打开ui文件 QFile.ReadOnly只读模式打开
qFile.open(QFile.ReadOnly)

# 加载ui对象
ui = QUiLoader().load(qFile)

# 关闭qFile 文件,ui对象加载完成后打开的ui文件可以关闭,qFile文件加载到ui对象了可以关闭
qFile.close()
# 文件导入到代码稳定8步骤,固定写法,ui文件名修改就行

# 登录操作
def login():
    # 账号获取--从ui页面对应的对象获取
    # 当前整个ui页面是一个ui对象,整个文件加载进去生成一个ui对象ui
    # ui对象可以操作这个页面的lineEdit和lineEdit_2这些元素,ui.lineEdit.text     ui对象.原件.text内容
    # 元素的名字可以自己修改,如下password_lineEdit这些
    username = ui.username_lineEdit.text()   # 获取账号
    # 密码获取
    password = ui.password_lineEdit.text()   # 获取账号
    ui.textBrowser.append(f"用户{username},登录成功")
    # 显示登录结果

def exit():
    ui.textBrowser.clear()

# 动作关联  登录按钮的click操作关联登录函数,事件函数关联按钮
ui.login_pushButton.clicked.connect(login)

# 清除按钮绑定关联函数,清除textBrowser这个组件的内容
ui.pushButton_3.clicked.connect(exit)

# 显示ui对象,执行ui对象
ui.show()

# 运行应用对象
app.exec_()
# 下卡菜单设置和单选框选择
from PySide2.QtWidgets import QApplication

from PySide2.QtUiTools import QUiLoader
from PySide2.QtCore import QFile

app = QApplication([])
qFile = QFile('接口测试工具.ui')
qFile.open(QFile.ReadOnly)
ui = QUiLoader().load(qFile)
qFile.close()

# 下卡菜单设置
# 通过Designer也能设置,双击comboBox元素,点击右下角的+按钮
# ui.comboBox.addItems(["1", "2", "3"])
# 获取当前下拉菜单栏的选项
print(ui.comboBox.currentText())


# 单选框可以选择   radio button,这个框可以通过程序来选,set选中
ui.radioButton.setChecked(True)
print(ui.radioButton.text())

ui.show()
app.exec_()

16:Qt Designer工具使用

主界面如下:

主界面不同区域介绍:
  工具箱区域:提供GUI界面开发使用的各种基本控件,如单选框、文本框等。可以拖动到新创建的主程序界面
  主界面区域:用户放置各种从工具箱拖过来的各种控件。模板选项中最常用的就是Widget(通用窗口)和MainWindow(主窗口)
      二者区别主要是Widget窗口不包含菜单栏、工具栏等。可以分别创建对比看看。
  对象查看器区域:查看主窗口放置的对象列表
  属性编辑器区域: 提供对窗口控件布局的属性编辑功能。比如修改控件的显示文本、对象名、大小等
  信号/槽编辑器区域:编辑控件的信号和槽函数,也可以添加自定义的信号和槽函数

Qt Designer基本控件介绍:
Widget Box控件工具箱是按照控件作用类别进行划分的。这里作为实现入门级界面实现,主要介绍最常使用的控件及控件对象相关函数。函数方法知道怎么获取控
  件输入内容以及如何将后台操作结果输出到界面控件显示的主要函数就可以了

显示控件:
  Lable:文本标签,显示文本,可以用来标记控件
  Text Browser:显示文本控件。用于后台命令执行结果显示
输入控件,提供与用户输入交互:
  Line Edit:单行文本框,输入单行字符串。控件对象常用函数为Text() 返回文本框内容,用于获取输入。setText() 用于设置文本框显示
  Text Edit:多行文本框,输入多行字符串。控件对象常用函数同Line Edit控件
  Combo Box:下拉框列表。用于输入指定枚举值。
控件按钮,供用户选择与执行:
  Push Button:
命令按钮。常见的确认、取消、关闭等按钮就是这个控件。clicked信号一定要记住。clicked信号就是指鼠标左键按下然后释放时会发送信号,从而触发相应操作
  Radio Button:单选框按钮
  Check Box:多选框按钮

17:Designer控件常用方法

安装PySide2库:
  cmd里运行:pip install PySide2 -i https://pypi.douban.com/simple --trusted -host pypi.douban.com
Pycharm关联Designer:
    File->Settings->Tools->External Tools,打开页面。
    目的:用于快速设计、修改 ui 并生成 .ui 文件。
    Program 填写:PySide2安装路径下的 designer.exe路径
    Working directory 填写:项目路径 $FileDir$
Pycharm关联PyUIC:
    D:\Tools\Python-3.6.6\Scripts\pyside2-uic.exe
    $FileName$ -o $FileNameWithoutExtension$.py
    $FileDir$

18:Designer常用指令操作  参考文档:https://doc.qt.io/archives/qtforpython-5.12/PySide2/QtWidgets/index.html#module-PySide2.QtWidgets

QPushButton按钮:
  当按钮被点击就会发出 clicked 信号,可以这样指定处理该信号的函数  button.clicked.connect(函数)
  代码中可以使用 setText 方法来改变按钮文本,比如   button.setText(text) 
 
 禁用启用按钮
    禁用  button.setEnabled(False)
    启用  button.setEnabled(True)
QLineEdit单行文本框:
  获取文本    text = editUrl.text()
  
通过 setPlaceholderText 方法可以设置提示文本内容,比如    editUrl.setPlaceholderText('请在这里输入URL')
  通过 setText 方法设置编辑框内的文本内容为参数里面的文本字符串,原来的所有内容会被清除,比如  editUrl.setText('1 hello world')
  clear 方法可以清除编辑框内所有的文本内容         editUrl.clear()
QTextBrowser文本浏览框:
  通过 append 方法在编辑框末尾添加文本内容,比如      textBrowser.append('hello world')
  通过 insertPlainText 方法在编辑框末尾添加文本内容,这种方法 不会 在添加文本后自动换行     textBrowser.insertPlainText('hello world')   
 
QComboBox组合选择框:
  
代码中可以使用 addItem 方法来添加一个选项到 末尾,参数就是选项文本   boxMethod.addItem('get') 
  代码中可以使用 addItems 方法来添加多个选项到 末尾,参数是包含了多个选项文本的列表    boxMethod.addItems(['GET','POST','PUT','DELETE'])
  代码中可以使用 clear 方法来清空选项,也就是删除选择框内所有的选项     boxMethod.clear()  
  代码中可以使用 currentText 方法来获取当前 选中的选项 的文本     method = boxMethod.currentText()  
  
QPlainTextEdit多行的纯文本编辑框:
  当文本框中的内容被键盘编辑,被点击就会发出textChanged信号,可以这样指定处理该信号的函数    edit.textChanged.connect(函数)
  通过 toPlainText 方法获取编辑框内的文本内容,    text = edit.toPlainText()
  通过 setPlaceholderText 方法可以设置提示文本内容    edit.setPlaceholderText('请在这里输入请求体‘)
  通过 setPlainText 方法设置编辑框内的文本内容 为参数里面的文本字符串   edit.setPlainText('''hello world''') 
  通过 appendPlainText 方法在编辑框末尾添加文本内容       edit.appendPlainText('hello world')   

  通过 insertPlainText 方法在编辑框末尾添加文本内容,这种方法 不会 在添加文本后自动换行    edit.insertPlainText('内容')
  clear 方法可以清除编辑框内所有的文本内容        edit.clear()
  copy 方法可以清除编辑框内所有的文本内容        edit.copy()
  paste 方法可以把剪贴板内容,拷贝到编辑框当前光标所在处    edit.paste()
ui跳转和关闭:
  ui.show和ui.close

19:接口测试工具设计思路

接口GUI界面的设计:样式图
实现方案:
    1:GUI编程
    2:request库的使用
代码封装:
  1:GUI界面设计
  2:接口代码封装
  3:综合调试
gui编写方式:

  1:load加载ui文件,python调用对象的属性,调用控件
  2:ui转化成py代码然后调用
  一般使用第一种,ui文件代码分离,load方式加载元素,便于维护,查看元素操作手册

全部布局完成后选中整个界面的空白(不选中任何选项)选择"垂直布局",这样才能支持缩放,最大最小

19:接口测试工具——简易版postman

1:使用QT Designer编辑ui,保存一个ui文件
2:一些简单组件的关联可以使用信号来做,比如清除按钮清除某个组件的内容,这里点击清空数据会清空响应数据的内容
3:编写python逻辑代码,组件绑定动作
import json
import threading

from PySide2.QtWidgets import QApplication
from PySide2.QtUiTools import QUiLoader
from PySide2.QtCore import QFile
import requests
"""
处理逻辑:
    1:发送请求
        一:使用对应的request库操作
        二:请求方法
            if判断    不建议这样
            getattr(requests, "get")
            requests.Request(url, method)
        三:请求url:直接填写
        四:请求头:输入字典格式
        五:请求body:输入字典格式
        六:点击发送按钮
    2:显示响应:
    3:清空数据  ui界面就可以做

GUI的初始化操作使用类来封装
"""

class HttpClient:
    """加载ui对象,读取ui对象等操作"""

    def __init__(self, uiName="接口工具.ui"):
        self.qFile = QFile(uiName)
        self.qFile.open(QFile.ReadOnly)
        self.ui = QUiLoader().load(self.qFile)
        self.qFile.close()
        self.ui.pushButton.clicked.connect(self.request_send)  # 关联按钮调用

    def request_send(self):
        """封装发送方法"""
        method = self.ui.comboBox.currentText()  # 获取当前请求方法
        url = self.ui.lineEdit.text()  # 获取url
        requests_headers = self.ui.plainTextEdit.toPlainText()  # 请求头,控件获取到的数据都是str字符串类型,字符串转dict字典
        requests_body = self.ui.plainTextEdit_2.toPlainText()  # 请求体
        if requests_headers.strip() != "":
            requests_headers = json.loads(requests_headers)
        if requests_body.strip() != "":
            requests_body = json.loads(requests_body)
        # print(method, url, requests_headers, requests_body, sep="\n")
        # 创建请求对象,没有直接发出去,只是构建了请求对象,和request方法有些区别
        req = requests.Request(method, url, headers=requests_headers, data=requests_body)
        pre = req.prepare()  # 获取请求前的数据
        s = requests.Session()  # 创建会话

        # requests请求都是阻塞模式的,单线程的话如果url不存在发出去很久没响应,gui界面会卡住,阻塞,无法清除
        # 主线程(控制gui界面),正常输入清除都不会影响   子线程:发送请求是另外一条线程    单线程按钮可能出问题
        # resp = s.send(pre)    # 发送请求  resp响应对象  s会话对象  send发送pre数据    会话形式发送数据出去的
        threading.Thread(target=self._thread_send, args=(s, pre)).start()
        # 开启多线程这里不能加join,加了如果接口不通阻塞,主线程的gui界面就卡死无法点击了,界面控件无法继续操作,会等待这个接口请求有结果才可以操作

    def _thread_send(self, s, pre):
        """发送请求,另起线程"""
        resp = s.send(pre)
        self.show_response(resp)

    def show_response(self, resp):
        """显示响应在Gui界面   响应头 响应体"""
        resp.encoding = "utf-8"
        # print('\n'.join('{}:{}'.format(k, v) for k, v in resp.headers.items()))   # 一整个换行的字符串,大字符串
        self.ui.textBrowser.append(
            "HTTP/1.1 {} \n{} \n\n {}".format(
                resp.status_code,       # 状态码
                '\n'.join('{}:{}'.format(k, v) for k, v in resp.headers.items()),    # 响应头
                resp.text   # 响应体
            )
        )

app = QApplication([])
httpClient = HttpClient()
httpClient.ui.show()
app.exec_()
4:python代码打包成exe可执行文件
  1.安装pyinstaller库
    pip install pyinstaller -i httpS://pypi.douban.com/simple --trusted -host pypi.douban.com
  2.找到你python工程下输入cmd
    cmd
    输入:pyinstaller -F py文件名 --noconsole --hidden-import PySide2.Qtxml   
      -F:全部环境打包成一个exe还是不打包环境,不打包环境拿到这个包不可以单独运行,需要依赖环境
      
noconsole:没有控制台
      hidden-import PySide2.Qtxml:导入库,指定加载Pyside2的那个库
      -F后面可以打包几个py文件,打几个文件,第一个文件是主文件,空格后加其他文件名
打包的两种方式:
  1:不包含库,python库不打包进去,打包成很多文件,多个文件都需要一起复制给别人才能运行(依赖文件+exe才能运行)  不加-F
  2:打包成一个单独的exe,这个exe文件比较大,所有东西都打包在一起  加-F

pyinstaller -F HttpClient.py --noconsole --hidden-import PySide2.Qtxml
5:打包后ui文件需要复制到和exe文件同一目录才可以使用  loadui文件方式就是这样,ui文件保证能够加载到

6:如果想省了ui文件,需要使用PyUic把ui文件转成python代码然后再打包,这种是gui和代码都混合在一起
  但是ui和代码分离可以修改ui的样式,字体颜色,类似前后端代码分离

 20:数据挖掘常用库

 21:股票分析工具开发,预测股票的波动趋势

功能描述:
  1:雪球财经网股票数据爬取
  2:数据指标分析  怎么认为股是好股(交易量,成交量,股票波动,)
  3:
"""
数据挖掘的流程:
    1:获取数据
        1:从固有的地方本地获取
        2:互联网动态获取
    2:存储数据
        1:大量数据  数据库
        2:数据量不是太大,使用(txt,excel,csv)文件存储
            excel读写数据麻烦,通过单元格获取
            csv的读写很简单,处理数据比较合适,很多工具使用这个格式(jmeter)
    3:清洗数据
    4:算法的介入
    5:结果展示
    6:分析汇总
"""
import csv  # 自带的
import threading

import pandas
import requests
import time

# 1- 获取数据:1、本地获取  2、网上获取
# 创建数据存储文件csv
# newline="" 默认换行的,csv文件写的很丑,加了这个写入csv每行的数据不换行了
fo = open("股票数据.csv", mode="w", encoding="utf-8", newline="")
# i写入csv列名
csv_write = csv.DictWriter(fo, fieldnames=["股票名称", "股票代码", "当前价格", "成交量"])
# 写入列名
csv_write.writeheader()

urlList = [
    "https://xueqiu.com/service/v5/stock/screener/quote/list?page={}&size=30&order=desc&orderby=percent&order_by=percent&market=CN&type=sh_sz&_=1612010624368".format(
        page, round(time.time() * 1000)) for page in range(1, 2)]

def request_data(url):
    # 一定要请求头--网站有防扒操作
    header = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36'
    }
    resp = requests.get(url, headers=header)  # 发请求
    print(resp.json())  # 返回一个json数据
    dataList = resp.json()['data']['list']
    # 获取需要的信息,one就是一支股票
    for one in dataList:
        # 1- 股票名
        # 2- 股票代码
        # 3- 当前价位
        # 4- 成交量
        dictTmp = {"股票名称": one["name"], "股票代码": one["symbol"], "当前价格": one["current"], "成交量": one["volume"]}
        print(dictTmp)
        # 写进去  数据库  csv  excel
        csv_write.writerow(dictTmp)  # 按行写入csv文件

def request_thread():
    """多线程并发获取数据"""
    threads = []
    for url in urlList:
        threads.append(threading.Thread(target=request_data, args=(url,)))
    for one in threads:  # 启动线程
        one.start()
        time.sleep(1)  # 多调爬虫顺序有问题,加一个sleep让启动有顺序,顺序写入csv格式
    for one in threads:  # 阻塞主线程,直到子线程运行完
        one.join()

start_time = time.time()  # 计时开始
request_thread()  # 爬取数据,数据存储到csv文件,存储完了之后文件对象需要关闭,不然文件打开状态pandas库无法read文件
fo.close()  #
end_time = time.time()  # 结束计时
print("耗时>>> ", end_time - start_time)

# 4- 数据处理,不同场景需要不同数据---再过滤数据
data_df = pandas.read_csv("股票数据.csv")  # pandas读取csv,pandas大数据处理
df = data_df.dropna()  # 剔除缺失的行,拿掉无效的行
df1 = df[["股票名称", "当前价格"]]  # 获取两列数据,两列全部数据
df2 = df1.iloc[:10]  # 取df1筛选的内容的前面10行
# print(type(df1))
# print(df2)

# 5- 算法分析 :引入算法--行业内部--经验的操作    调用算法,行业机构算法规则

# 6- 展示效果,展示成图形web端--可视化接入平台    python图标化展示平台matplotlib,图表处理
import matplotlib.pyplot as plt
from pylab import mpl

mpl.rcParams['font.sans-serif'] = ['SimHei']
mpl.rcParams['axes.unicode_minus'] = False  # 负号中文无法显示,配置这个,处理字体

# 准备数据
# 绘制图形
plt.bar(df2['股票名称'].values, height=df2['当前价格'].values, label="股票当前价")  # 股票名称-横坐标,当前价格-纵坐标,label标签

for a, b in zip(df2['股票名称'].values, df2['当前价格'].values):
    print(a, b)  # a-股票名称, b-股票价格
    plt.text(a, b + 3, b, horizontalalignment="center", verticalalignment="bottom", fontsize=10, rotation=90)
# 1-
plt.legend()  # 设置生效,让每个柱形图上显示股票价格的设置生效,for循环生效
plt.xticks(rotation=90)  # 旋转90,横坐标旋转90度
plt.xlabel('股票名称')  # 设置x标签值
plt.ylabel('当前价格')  # 设置y标签值
plt.show()  # 展示

 

posted @ 2022-07-09 13:53  至高无上10086  阅读(571)  评论(0编辑  收藏  举报