DEVOPS:多线程、多进城编程,urllib模块,paramiko模块

多进程编程

  • windows系统不支持
  • 通过os.fork()实现子进程
  • os.fork()它的返回值是数字,在父进程中,这个数字是非0值(子进程的pid);在子进程中,值是0
  • 多进程编程思路
    • 父进程只负责fork子进程
    • 子进程做具体的工作
    • 子进程工作结束后,务必彻底退出

forking工作原理

什么是forking

在这里插入图片描述

进程的生命周期

在这里插入图片描述

僵尸进程

  • 当子进程没有任何可执行代码后,就变成了僵尸进程
  • 短暂存在的程序不需要处理僵尸进程,因为systemd会处理
    在这里插入图片描述

forking编程

forking编程思路

对于python3
在这里插入图片描述

解决zombie问题

在这里插入图片描述
在这里插入图片描述

import os
import time

print('starting')

rc = os.fork()
if rc:
    print('父进程')
    # 挂起父进程,处理僵尸进程后才会继续执行
    result = os.waitpid(-1, 0)  # 处理僵尸进程后的返回值是: (子进程pid, 0)
    print(result)
    time.sleep(15)
    print('父进程结束')
else:
    print('子进程')
    time.sleep(15)
    print('子进程结束')
import os
import time

print('starting')

rc = os.fork()
if rc:
    print('父进程')
    # 不挂起父进程,如果子进程刚好是僵尸进程,则处理;如果不是,父进程也要继续向下执行
    result = os.waitpid(-1, 1)  # 子进程不是僵尸进程,返回值是: (0, 0)
    print(result)
    time.sleep(30)
    print('父进程结束')
else:
    print('子进程')
    time.sleep(15)
    print('子进程结束')

forking应用示例

import os
print('starting')
rc = os.fork()
if rc:  # 返回值rc是一个数字,非0为真,0为假
    print('父进程')
else:
    print('子进程')

print('Hello World!')
  • ping主机
import subprocess
import time
import os


def ping_pong(ip):
    res = subprocess.run('ping -c 2 %s &> /dev/null' % ip,shell=True)
    if res.returncode == 0:
        print('%s:up' % ip)
    else:
        print('%s:down' % ip)


if __name__ == '__main__':
    host = ['192.168.1.%s' % i for i in range(1, 255)]
    print(time.ctime())
    for ip in host:
        rc=os.fork()
        if not rc:
            ping_pong(ip)
            exit()
    print(time.ctime())

多线程编程

  • 程序就是存储在磁盘上的一些可执行文件
  • 当程序运行时,就会产生一到多个进程。可以认为进程是加载到内存的一系列指令
  • 进程之中又可以包含一到多个线程
  • 每个进程都拥有自己独立的运行空间
  • 所有的线程共享进程的运行空间
  • 各种系统都支持多线程

时分复用:将CPU时间分成很多小的片段,叫时间片。每个程序都分得一些时间片,当它的时间片用光了,就要重新排队,等待下一次运行机会。

多线程编程思路:

  • 主线程(类似于父进程)只负责生成工作线程(类似于子进程)
  • 工作线程做具体的工作

多线程工作原理

多线程的动机

在这里插入图片描述

多线程任务的工作特点

在这里插入图片描述

什么是进程

在这里插入图片描述

什么是线程

在这里插入图片描述

多线程编程

多线程相关模块

在这里插入图片描述

传递函数给Thread类

在这里插入图片描述

传递可调用类给Thread类

在这里插入图片描述

多线程ping主机示例

import subprocess
import threading


def ping_pong(ip):
    res = subprocess.run('ping -c 2 %s &> /dev/null' % ip,shell=True)
    if res.returncode == 0:
        print('%s:up' % ip)
    else:
        print('%s:down' % ip)


if __name__ == '__main__':
    host = ['192.168.1.%s' % i for i in range(1, 255)]
    for ip in host:
        t=threading.Thread(target=ping_pong,args=(ip,))
        t.start()
  • 使用oop(面向对象编程)ping主机
import subprocess
import time
import threading

class Ping:
    def __call__(self, ip):
        res = subprocess.run('ping -c 2 %s &> /dev/null' % ip, shell=True)
        if res.returncode == 0:
            print('%s:up' % ip)
        else:
            print('%s:down' % ip)


if __name__ == '__main__':
    host = ['192.168.1.%s' % i for i in range(1, 255)]
    print(time.ctime())
    for ip in host:
        t = threading.Thread(target=Ping(), args=[ip])
        t.start()
    print(time.ctime())
import subprocess
import time
import threading


class Ping:
    def __init__(self, ip):
        self.ip = ip

    def __call__(self):
        res = subprocess.run('ping -c 2 %s &> /dev/null' % ip, shell=True)
        if res.returncode == 0:
            print('%s:up' % self.ip)
        else:
            print('%s:down' % self.ip)


if __name__ == '__main__':
    host = ['192.168.1.%s' % i for i in range(1, 255)]
    print(time.ctime())
    for ip in host:
        t = threading.Thread(target=Ping(ip))
        t.start()
    print(time.ctime())

urllib模块

urllib基础

>>> from urllib import request
>>> html = request.urlopen('http://www.163.com')
>>> html.read(10)
b' <!DOCTYPE'
>>> html.readline()
b' HTML>\n'
>>> html.readlines()

urllib简介

在这里插入图片描述

爬取网页

在这里插入图片描述

爬取网页示例

import sys
from urllib.request import urlopen
def get_web(url, fname):
    html = urlopen(url)    #使用urllib模块的urlopen函数打开url,赋值给html
    with open(fname, 'wb') as fobj:
        while True:
            data = html.read(4096)
            if not data:
                break
            fobj.write(data)
    html.close()
if __name__ == '__main__':
    get_web(sys.argv[1], sys.argv[2])   #让用户在命令行上提供网址和下载数据保存位置

下载网络资源

在这里插入图片描述

爬取网站图片示例

import os
import wget
import re

def get_patt(fname, patt, charset=None):
    result = []
    cpatt = re.compile(patt)

    with open(fname, encoding=charset) as fobj:
        for line in fobj:
            m = cpatt.search(line)
            if m:
                result.append(m.group())

    return result

if __name__ == '__main__':
    # 创建下载目录
    dst = '/tmp/164'
    if not os.path.exists(dst):
        os.mkdir(dst)

    # 下载首页文件
    fname = '/tmp/164/164.html'
    url = 'https://www.163.com/'
    if not os.path.exists(fname):
        wget.download(url, fname)

    # 在首页文件中找到所有的图片url
    img_patt = '(http|https)://[\w._/-]+\.(jpg|png|jpeg|gif)'
    img_list = get_patt(fname, img_patt, 'gbk')

    # 下载图片
    for img_url in img_list:
        wget.download(img_url, dst)

修改请求头

>>> js_url = 'http://www.jianshu.com'
>>> html = request.urlopen(js_url)  # 403错误
# 因为简书有基本的反爬虫功能,发现是机器爬虫程序,就拒绝

>>> js_url = 'http://www.jianshu.com'
>>> headers = {'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0'}
# 创建一个请求对象,访问简书服务器时,携带修改的请求头发请求
>>> r = request.Request(js_url, headers=headers)
>>> html = request.urlopen(r)

url编码

  • URL中,只允许一部分ascii码字符
  • 如果使用其他字符,需要转码
>>> url = 'https://www.sogou.com/web?query=北京'
>>> html = request.urlopen(url)  # 报错,url中含中文

>>> url = 'https://www.sogou.com/web?query=' + request.quote('武汉')
>>> url
'https://www.sogou.com/web?query=%E6%AD%A6%E6%B1%89'

异常处理

  • urllib模块的子模块error定义了可能出现的异常
  • 所以在捕获异常时,需要将它导入
>>> js_url = 'http://www.jianshu.com'
>>> from urllib import error
>>> try:
...   html = request.urlopen(js_url)
... except error.HTTPError:
...   print('无法访问')
...
无法访问

处理下载错误示例

起动一个web服务
在web服务器的文档目录下创建目录ban,权限设置为700
编写python程序访问不存在的路径和ban目录,处理404和403错误
404错误打印“无此页面”,403错误打印“无权访问”

步骤一:启动一个web服务

[root@localhost ~]# systemctl restart httpd

步骤二:在web服务器的文档目录下创建目录ban,权限设置为700

[root@localhost ~]# mkdir -m 700 /var/www/html/ban

步骤三:如果访问的页面不存在或拒绝访问,程序将抛出异常
执行案例2中get_web.py文件,访问不存在页面,抛出404异常如下:

[root@localhost day11]# python3 get_web.py http://127.0.0.1/abc/ /tmp/abc.html
Traceback (most recent call last):
...
...
    raise HTTPError(req.full_url, code, msg, hdrs, fp)
urllib.error.HTTPError: HTTP Error 404: Not Found

执行案例2中get_web.py文件,访问存在页面ban目录,抛出403权限异常如下:

[root@localhost day11]# python3 get_web.py http://127.0.0.1/ban/ /tmp/abc.html
Traceback (most recent call last):
...
...
    raise HTTPError(req.full_url, code, msg, hdrs, fp)
urllib.error.HTTPError: HTTP Error 403: Forbidden

步骤三:编写python程序捕获异常
创建get_web3.py文件,实现访问不存在的路径和ban目录时,捕获404和403错误
,同时404错误打印“无此页面”,403错误打印“无权访问”,代码如下:

    import sys
    from urllib.request import urlopen
    from urllib.error import HTTPError    #导入urllib.error模块,用HTTPError捕获异常信息
    def get_web(url, fname):
        try:
            html = urlopen(url)    #打开网址时即可知道是否有异常,所以将本语句放入try语句
        except HTTPError as e:     #捕获返回HTTPError类的实例e
            print(e)
            if e.code == 403:        #捕获异常状态码如果等于403
                print('权限不足')    #输出'权限不足'
            elif e.code == 404:    #捕获异常状态码如果等于404
                print('没有那个地址')    #输出'没有那个地址'
            return                        #return后面代码均不执行
            
        with open(fname, 'wb') as fobj:
            while True:
                data = html.read(4096)
                if not data:
                    break
                fobj.write(data)
        html.close()
    if __name__ == '__main__':
        get_web(sys.argv[1], sys.argv[2])

测试脚本执行:

访问不存在页面:

[root@localhost day11]# python3 get_web.py http://127.0.0.1/abc/ /tmp/abc.html
HTTP Error 404: Not Found
没有那个地址

访问ban目录:

[root@localhost day11]# python3 get_web.py http://127.0.0.1/ban/ /tmp/abc.html
HTTP Error 403: Forbidden
权限不足

paramiko模块

基础使用说明

paramiko实现的是ssh功能
在这里插入图片描述

# 本地安装
yum install -y gcc gcc-c++ python-devel
tar -xf paramiko-1.15.4.tar.gz
python3 setup.py install
# 在线安装
[root@localhost day02]# pip3 install paramiko

>>> import paramiko
>>> ssh = paramiko.SSHClient()  # 创建客户端实例
# 设置自动接受密钥
>>> ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# 登陆服务器
>>> ssh.connect('192.168.113.133', username='root', password='redhat')
# 执行命令
>>> result = ssh.exec_command('id root; id dingjie')
>>> len(result)
3
# 执行命令的返回值是一个3元素元组。这3项分别是输入、输出和错误的类文件对象。类文
件对象提供了read方法,可以读取其中的内容
>>> stdin, stdout, stderr = ssh.exec_command('id root; id dingjie')
>>> out = stdout.read()
>>> err = stderr.read()
>>> out
b'uid=0(root) gid=0(root) \xe7\xbb\x84=0(root)\n'
>>> err
b'id: dingjie: no such user\n'
>>> ssh.close()   # 关闭
>>> out.decode()  # 将bytes类型转为str类型
'uid=0(root) gid=0(root) 组=0(root)\n'

paramiko示例

  • 使用多线程ssh连接主机执行命令
import paramiko
import sys
import getpass
import os
import threading


def rcmd(host, user, passwd, port=22, cmds=None):
    ssh = paramiko.SSHClient()
    ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    ssh.connect(host, username=user, password=passwd, port=port)
    stdin, stdout, stderr = ssh.exec_command(cmds)
    out = stdout.read()
    err = stderr.read()
    if out:  # 如果命令有输出
        print('[%s] \033[32;1mOUT\033[0m:\n%s' % (host, out.decode()))
    if err:  # 如果命令执行错误
        print('[%s] \033[31;1mERROR\033[0m:\n%s' % (host, err.decode()))
    ssh.close()


if __name__ == '__main__':
    if len(sys.argv) != 3:
        print("Usage: %s ipfile 'commands'" % sys.argv[0])
        exit(1)  # 1就是$?值
    if not os.path.isfile(sys.argv[1]):
        print('No such file:', sys.argv[1])
        exit(2)
    ipfile = sys.argv[1]
    passwd = getpass.getpass()
    cmds = sys.argv[2]
    with open(ipfile) as fobj:
        for line in fobj:
            ip = line.strip()  # srtip()字段去除前后的空格,文件每一行是一个ip地址,但是要去除行尾的\n
            t = threading.Thread(target=rcmd, args=(ip, 'root', passwd, 22, cmds))
            t.start()
# python3 rcmd.py servers.txt 'sleep 3; id root'
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值