Beautifulsoup

from bs4 import BeautifulSoup
import requests,re

res1=requests.get("http://www.baidu.com")
soup=BeautifulSoup(res1.content,"html.parser") #将网页源码变成一个BeautifulSoup对象
print(soup.prettify()) #格式化网页源码
# print(soup.head) # 获取head标签内容
# print(soup.head.title)  #获取head标签内的title标签的内容
# print(soup.head.title.string) #获取title标签之间的字符串,返回值为unicode类型
# print(soup.find_all("head")) #找到所有的匹配的标签,返回值是个对象数组,不是字符串
# print(soup.link['href']) #取标签的属性值,因为我们返回的内容都不是一个字符串而是一个对象,所以可以这样用
# print(soup.find_all(id="cp",class_="test")[0].a['href']) #根据标签的id取值,可以限定多个标签属性
# print(soup.title.name) #返回当前标签的名字
# print(soup.title.parent.name) #返回当前标签父标签的名字
# print(soup.find_all(id=True)[0]) #找出有id的标签
# print(soup.find_all(['a','b'])) #找出所有a标签和b标签的内容
# 支持正则表达式
# print(soup.find_all(re.compile("^a"))[1]) #找出所有标签名字满足正则表达式的标签
# print(soup.find_all("div",id="ftConw"))
print("aaaa\\122acvea".rfind('1'))

socket

关于socket

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

也就是说socket给我们提供了各种各样的接口,而让我们忽略了socket后面的复杂的协议内容。下面是python socket给我们提供的一部分api函数:

socket()
bind()
listen()
accept()
connect()
connect_ex()
send()
recv()
close()

sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)

第一个参数:协议簇
  socket.AF_INET IPv4(默认)
  socket.AF_INET6 IPv6
  socket.AF_UNIX 只能够用于单一的Unix系统进程间通信
第二个参数:通信类型
  socket.SOCK_STREAM  流式socket , for TCP (默认)
  socket.SOCK_DGRAM   数据报式socket , for UDP

  socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
  socket.SOCK_RDM 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
  socket.SOCK_SEQPACKET 可靠的连续数据包服务

sk.blind(adress)

s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。

sk.listen(backlog)

开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。
backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5
这个值不能无限大,因为要在内核中维护连接队列

sk.setblocking(bool)

是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。
阻塞增加了等待功能?

阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态.

阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

还是上面的例子,
你打电话问书店老板有没有《分布式系统》这本书,你如果是阻塞式调用,你会一直把自己“挂起”,直到得到这本书有没有的结果,如果是非阻塞式调用,你不管老板有没有告诉你,你自己先一边去玩了, 当然你也要偶尔过几分钟check一下老板有没有返回结果。
在这里阻塞与非阻塞与是否同步异步无关。跟老板通过什么方式回答你结果无关。

sk.accept()

接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。
接收TCP 客户的连接(阻塞式)等待连接的到来

sk.connect(address)

连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。

sk.connect_ex(address)

同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061

sk.close()

关闭套接字

sk.recv(bufsize[,flag])

接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。

sk.recvfrom(bufsize[.flag])

 与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。

sk.send(string[,flag])

将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。

sk.sendall(string[,flag])

将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。

sk.sendto(string[,flag],address)

将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。

sk.settimeout(timeout)

设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )

sk.getpeername()

返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。

sk.getsockname()

返回套接字自己的地址。通常是一个元组(ipaddr,port)

sk.fileno()

套接字的文件描述符

socket的工作过程:

服务器端会先创建一个socket对象,并且支持 上下文管理器,你可以使用 with 语句,这样你就不用再手动调用 s.close() 来关闭 socket了 。对于上下文管理器:

with open('test.txt') as f:
    print f.readlines()
# 基本语法
with EXPR as VAR:
    BLOCK
1. 上下文表达式:with open('test.txt') as f:
2. 上下文管理器:open('test.txt')
3. f 不是上下文管理器,应该是资源对象。

要自己实现这样一个上下文管理,要先知道上下文管理协议。简单点说,就是在一个类里,实现了__enter____exit__的方法,这个类的实例就是一个上下文管理器。

class Resource():
    def __enter__(self):
        print('===connect to resource===')
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print('===close resource connection===')

    def operate(self):
        print('===in operation===')

with Resource() as res:
    res.operate()
/*
===connect to resource===
===in operation===
===close resource connection===
*/

因此可以看出,我们可以将资源的链接放在__enter__中,然后将资源的关闭放在__exit__中。大概socket类也实现了这两个方法,可以忽略socket.close()方法。

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    pass  # Use the socket object without calling s.close().

bind() 用来关联socket到指定的网络接口(IP 地址)和端口号:

HOST = '127.0.0.1'
PORT = 65432

# ...

s.bind((HOST, PORT))

bind() 方法的入参取决于 socket 的地址族,在这个例子中我们使用了 socket.AF_INET (IPv4),它将返回两个元素的元组:(host, port)。

listen() 方法调用使服务器可以接受连接请求,这使它成为一个「监听中」的 socket

s.listen()
conn, addr = s.accept()

accept() 方法阻塞并等待传入连接。当一个客户端连接时,它将返回一个新的 socket 对象,对象中有表示当前连接的 conn 和一个由主机、端口号组成的IPv4/v6连接的元组。

accept() 获取客户端 socket 连接对象 conn 后,使用一个无限 while 循环来阻塞调用 conn.recv(),无论客户端传过来什么数据都会使用 conn.sendall() 打印出来。如果 conn.recv() 方法返回一个空 byte 对象(b''),然后客户端关闭连接,循环结束,with 语句和 conn 一起使用时,通信结束的时候会自动关闭 socket 链接。

一个服务端的demo

#!/usr/bin/env python3

import socket

HOST = '127.0.0.1'  # 标准的回环地址 (localhost)
PORT = 65432        # 监听的端口 (非系统级的端口:大于 1023)

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    s.listen()
    conn, addr = s.accept()
    with conn:
        print('Connected by', addr)
        while True:
            data = conn.recv(1024)
            if not data:
                break
            conn.sendall(data)

一个客户端demo

#!/usr/bin/env python3

import socket

HOST = '127.0.0.1'  # 服务器的主机名或者 IP 地址
PORT = 65432        # 服务器使用的端口

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((HOST, PORT))
    s.sendall(b'Hello, world')
    data = s.recv(1024)

print('Received', repr(data))

与服务器程序相比,客户端程序简单很多。它创建了一个 socket 对象,连接到服务器并且调用 s.sendall() 方法发送消息,然后再调用 s.recv() 方法读取服务器返回的内容并打印出来。

参考链接:

https://keelii.gitbooks.io/socket-programming-in-python-cn/content/05-communication-breakdown.html
https://www.cnblogs.com/fanweibin/p/5053328.html

多线程

进程是资源分配的单位,在进程的切换伴随着资源的释放和重新分配,所以速度会慢一点。但是不同的线程之间是共享资源的,所以一个线程完成后不需要再去释放资源。所以线程要比进程快一点。线程是在单核上运作的,会跑满单核,进程可以运作在多核上。那么说,是不是跑满单核之后再重启一个核心。

因为线程是比较轻量级的,所以这里就只看看线程。这里的多线程是基于当前的脚本线程是主线程的情况下,然后主线程又开启了许多子线程。

多线程的实现主要是靠threading模块:

import threading
import time

def run(n):
    print("task", n)
    time.sleep(1)
    print('2s')
    time.sleep(1)
    print('1s')
    time.sleep(1)
    print('0s')
    time.sleep(1)

if __name__ == '__main__':
    t1 = threading.Thread(target=run, args=("t1",))
    t2 = threading.Thread(target=run, args=("t2",))
    t1.start()
    t2.start()

这里的Thread()方法其实是Thread类的构造函数:

参数:
group=None, target=None, name=None,args=(), kwargs=None, *, daemon=None
target:执行的函数名字
args:参数,需要是数组形态
name:别名
daemon:是否是守护进程
kwargs:传递的是字典形式组织的变量

start之后才会执行这个线程,线程的别名并不是t1,需要在name参数里面定义。有一种t1t2都是代称的意思,只是为了启动线程,而启动之后t1等就不再关心启动的线程了。

继承threading.Thread来自定义线程类,其本质是重构Thread类中的run方法:

import threading
import time

class MyThread(threading.Thread):
    def __init__(self, n):
        super(MyThread, self).__init__()  # 重构run函数必须要写
        self.n = n

    def run(self):
        print("task", self.n)
        time.sleep(1)
        print('2s')
        time.sleep(1)
        print('1s')
        time.sleep(1)
        print('0s')
        time.sleep(1)

if __name__ == "__main__":
    t1 = MyThread("t1")
    t2 = MyThread("t2")
    t1.start()
    t2.start()

这里的run函数应该就是start启动的函数,我们想要他执行的函数应该就是写在run里面了。

如果当前python线程是守护线程,那么意味着这个线程是“不重要”的,“不重要”意味着如果他的主进程结束了但该守护线程没有运行完,守护进程就会被强制结束。如果线程是非守护线程,那么父进程只有等到守护线程运行完毕后才能结束。
在python中,线程通过threadName.setDaemon(True|False)来设置是否为守护线程。

因为之前说了,脚本下启动的许多线程都是子线程,假如说我们的主线程执行完之后,到底需不需要等待子线程执行完毕的。其实只要设置一个参数就可以的,需要注意这里的设置需要在start之前:

import time
import threading


def fun():
    print("start fun")
    time.sleep(2)
    print("end fun")

def main():
    print("main thread")
    t1 = threading.Thread(target=fun,args=())
    t1.setDaemon(True)# True 即为守护线程,False说明不是守护线程
    t1.start()
    time.sleep(1)
    print("main thread end")

if __name__ == '__main__':
    main()

或者也可以使用t1.join()来设置主线程需要等待子线程执行完成之后再结束,这里的join方法放在start后面。我的理解是这里的join函数将子线程从不重要的线程队列中扔掉了。

互斥锁

假如说我们让多个线程并行对一个全局变量做递增并且读取的操作,举个例子:

"——————" 时间间隔
线程1:递增——————读取
线程2:    递增——————读取

这样就出现了读脏数据的问题,我们想要读的是递增多1的数据,但是我们却读到了递增2的数据。所以这个时候就需要一个互斥锁。如果加了互斥锁就同一时刻就只有一个线程可以进行递增,读取的操作。可能会质疑这个时候会不会和并行的概念矛盾,其实我们只是把互斥用在了一个小地方上,可能后续还有函数的执行等等时间较长的操作,牺牲了一点时间换取了数据的不混乱还是性价比很高的。

由于线程之间是进行随机调度,如果有多个线程同时操作一个对象,如果没有很好地保护该对象,会造成程序结果的不可预期,我们也称此为“线程不安全”。

from threading import Thread,Lock
import os,time
def work():
    global n
    lock.acquire()
    temp=n
    time.sleep(0.1)
    n=temp-1
    lock.release()
if __name__ == '__main__':
    lock=Lock()
    n=100
    l=[]
    for i in range(100):
        p=Thread(target=work)
        l.append(p)
        p.start()
    for p in l:
        p.join()
lock.acquire()  创建锁
lock.release()  释放锁

RLcok类的用法和Lock类一模一样,但它支持嵌套,在多个锁没有释放的时候一般会使用RLcok类。

import threading
import time

def Func(lock):
    global gl_num
    lock.acquire()
    gl_num += 1
    time.sleep(1)
    print(gl_num)
    lock.release()

if __name__ == '__main__':
    gl_num = 0
    lock = threading.RLock()
    for i in range(10):
        t = threading.Thread(target=Func, args=(lock,))
        t.start()

事件(Event类)

python线程的事件用于主线程控制其他线程的执行,事件是一个简单的线程同步对象,其主要提供以下几个方法:

  • clear 将flag设置为“False”
  • set 将flag设置为“True”
  • is_set 判断是否设置了flag
  • wait 会一直监听flag,如果没有检测到flag就一直处于阻塞状态

事件处理的机制:全局定义了一个“Flag”,当flag值为“False”,那么event.wait()就会阻塞,当flag值为“True”,那么event.wait()便不再阻塞。

#利用Event类模拟红绿灯
import threading
import time

event = threading.Event()


def lighter():
    count = 0
    event.set()     #初始值为绿灯
    while True:
        if 5 < count <=10 :
            event.clear()  # 红灯,清除标志位
            print("\33[41;1mred light is on...\033[0m")
        elif count > 10:
            event.set()  # 绿灯,设置标志位
            count = 0
        else:
            print("\33[42;1mgreen light is on...\033[0m")

        time.sleep(1)
        count += 1

def car(name):
    while True:
        if event.is_set():      #判断是否设置了标志位
            print("[%s] running..."%name)
            time.sleep(1)
        else:
            print("[%s] sees red light,waiting..."%name)
            event.wait()
            print("[%s] green light is on,start going..."%name)

light = threading.Thread(target=lighter,)
light.start()

car = threading.Thread(target=car,args=("MINI",))
car.start()

参考链接:

https://www.cnblogs.com/luyuze95/p/11289143.html
https://zhuanlan.zhihu.com/p/46368084
说点什么
支持Markdown语法
好耶,沙发还空着ヾ(≧▽≦*)o
Loading...