进程 + 数据共享 + 锁(进程) + 进程池 + 模块(爬虫requests bs4)
进程
进程 需要导入模块 import multiprocessing (线程是导入 threading)
创建进程和线程的用法是类似的, 其中的写法也是相同的.
但是 由于操作系统的不同 ,
在 linux下和mac 下, 创建的步骤和线程是一样的.
在 windows 下,创建的过程中,需要将主程序放在 if __name__ == '__main__': 下运行.
在进程中数据不能够共享的.
1. 通过 模块 创建进程 (常用)
1) linux / mac 下
这种情况在linux系统中是正确的, 跟线程的写法是一样的. 在windows 中会报错.
import multiprocessing # 导入模块 def task(arg): print(arg) for i in range(10): p = multiprocessing.Process(target=task,args=(i,)) # 创建线程 传值都是一样的 p.start()
2) 在 windows 下, 主程序放入到 main 中
import multiprocessing def task(arg): print(arg) if __name__ == '__main__': # windows 下将程序放入main下即可 for i in range(10): p = multiprocessing.Process(target=task,args=(i,)) p.start()
2. 通过 继承 创建进程
创建一个自己的类,继承 multiprocessing.Process, 里面添加 run 方法 为任务方法
import multiprocessing class MyProcess(multiprocessing.Process): def run(self): print('当前进程',multiprocessing.current_process()) # 获取当前进程 def run(): p1 = MyProcess() p1.start() p2 = MyProcess() p2.start() if __name__ == '__main__': run() # 当前进程 < MyProcess(MyProcess - 2, started) > # 当前进程 < MyProcess(MyProcess - 1, started) >
3. 进程中的常用功能
p = multiprocessing.Process()
p.name = ("....")
p.deamon = True
p.strat()
p.join()
p.ident() / p.pid()
p1 = multiprcocess.current_process()
进程 数据共享
1. 进程之间的数据是不能共享的
线程可以共享进程里的资源,但是进程之间的数据是不共享的.
示例1 : 通过列表验证 进程之间数据不共享
创建了10个进程,每个进程都在 data_list列表 中放入值, 但是最终10个进程会产生10个列表,这是因为进程间的数据(列表)是不共享的.
# windows 下
import multiprocessing import threading data_list = [] # 创建列表用于存放数据, 进程用的时候,每个进程都会生成自己的列表 def task(arg): data_list.append(arg) print(data_list) def run(): for i in range(10): p = multiprocessing.Process(target=task,args=(i,)) # 创建进程 # p = threading.Thread(target=task,args=(i,)) # 创建线程 p.start() if __name__ == '__main__': run()
[1] [0] [5] [3] [4] [2] [6] [8] [7] [9]
2. 进程之间要想数据共享 可以通过 multiprocessing.Queue 和 Manager
multiprocessing.Queue : 是特殊的队列,在进程中,如果用的是该队列,那么每个进程都会往这个队列中放值和取值.
queue.Queue 队列在进程中会类似列表,每个进程都会产生自己的队列放数据.
Manager
1) mutilprocessing.Queue()
import multiprocessingimport queue q = multiprocessing.Queue() # 创建队列 # q = queue.Queue() # 用该方法创建的队列 不能做数据之间的共享 def task(arg,q): # 在队列中添加值 q.put(arg) def run(): for i in range(10): p = multiprocessing.Process(target=task, args=(i,q)) # 需要导入队列 p.start() while True: # 循环取出队列中所有的值, 不循环则只能取队列中的一个值 v = q.get() # 从队列中获取值 print(v) if __name__ == '__main__': # windows下用main run()
0
4
3
5
2
8
9
1
6
7
2) Manager
m = multiprocessing.Manager()
dic = m.dict() # Manager对象中可以创建字典, 不同进程访问的时候,也是访问的同一个,
# 在windows下要将全局变量放入 __main__ 下才能执行,否则报错
其中: 当主进程访问的时候,要是主进程运行完,那么dic会关闭,其他的子进程还没运行完,再访问dic时会提示,找不到文件,而报错.
因此有两种方法来使进程运行完. a.使用join()等待子进程运行完 b.判断子线程是否还存活 p.is_alive
方式一:
import multiprocessing def task(arg,dic): dic[arg] = 100 def run(): for i in range(10): p = multiprocessing.Process(target=task,args=(i,dic,)) p.start() p.join() # 等待子进程放数据放完,否则主进程运行完,dic将关闭, # 子进程将不能够再继续在dic中放数据,会报错,找不到指定的文件. print(dic) if __name__ == '__main__': m = multiprocessing.Manager() dic = m.dict() # 这里是字典 也会有列表等 run() # {0: 100, 1: 100, 2: 100, 3: 100, 4: 100, 5: 100, 6: 100, 7: 100, 8: 100, 9: 100}
方式二:
该方式相对第一种较麻烦, 但也是一种方法,也能解决问题
import time import multiprocessing def task(arg,dic): # 进程要处理的任务 time.sleep(2) dic[arg] = 100 if __name__ == '__main__': m = multiprocessing.Manager() # 全局变量Manager要写在main中 dic = m.dict() # Manager对象创建dict process_list = [] for i in range(10): p = multiprocessing.Process(target=task, args=(i,dic,)) p.start() process_list.append(p) # 子进程添加到列表中 while True: count = 0 for p in process_list: if not p.is_alive(): # 统计子线程是否存活, count += 1 if count == len(process_list): # 都不存货时跳出循环 再打印dic即可 break print(dic) # {5: 100, 2: 100, 6: 100, 7: 100, 1: 100, 3: 100, 4: 100, 8: 100, 0: 100, 9: 100}
3. 进程间的数据在其他电脑上,需要通过网络获取
简单的模型如下:
import multiprocessing def task(arg,dic): pass if __name__ == '__main__': while True: # 连接上指定的服务器 # 去机器上获取url url = 'adfasdf' # 假如设个值 p = multiprocessing.Process(target=task, args=(url,)) p.start()
进程 锁
在进程中 锁 的类型和用法 都与线程是一致的.
进程中用 multiprocessing 调用锁, 线程中用 Threading 调用锁
Lock
RLock
BoundedSemaphore
Contion
Event
注意:
无论是线程还是进程,当操作同一个东西的时候,需要加锁.
但是进程之间的数据是不共享的,一般的情况下是不需要加锁的,
但是当数据共享的时候,需要加锁.
下面以 RLock 为例, 展示一下锁的应用,
import time import threading import multiprocessing lock = multiprocessing.RLock() def task(arg): print('进程来了') lock.acquire() time.sleep(2) print(arg) lock.release() if __name__ == '__main__': p1 = multiprocessing.Process(target=task,args=(1,)) p1.start() p2 = multiprocessing.Process(target=task, args=(2,)) p2.start()
进程来了 进程来了 1 # 会停2秒再执行 2
进程池
进程池的作用和线程池的作用是一样的.
进程的个数推荐为CPU的个数(即几核的电脑).
首先导入模块:
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor # 进程池/线程池
submit()
示例1: 进程池和线程池的对比
在 windows 下,进程池必须写在 if __name__ == '__main__': 下, linux / mac 下跟线程池是一样的.
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor import time def task(arg): time.sleep(1) print(arg) pool = ThreadPoolExecutor(5) # 线程池 for i in range(10): pool.submit(task,i) if __name__ == '__main__': pool = ProcessPoolExecutor(5) # 进程池 for i in range(10): pool.submit(task, i) # task为函数名, i为参数
模块 (爬虫 requests / bs4)
1. 安装模块
在cmd终端运行:
pip3 install requests
pip3 install beautifulsoup4
可能出现的问题:
1) 找不到pip3内部指令?
方式一:
C:\Users\Administrator\AppData\Local\Programs\Python\Python36\Scripts\pip3 install requests
方式二:
添加环境变量
C:\Users\Administrator\AppData\Local\Programs\Python\Python36\Scripts
2. 小爬虫示例
模块的导入:
import requests
from bs4 import BeautifulSoup
线程池和进程池时导入:
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
示例: 爬取 抽屉 的一段新闻内容 文本.
import requests from bs4 import BeautifulSoup from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor def task(url): # r1接收到的是网页的源码 r1 = requests.get( url=url, headers={ 'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36' } ) # 查看下载下来的文本信息 soup = BeautifulSoup(r1.text,'html.parser') content_list = soup.find('div',attrs={'id':'content-list'}) for item in content_list.find_all('div',attrs={'class':'item'}): title = item.find('a').text.strip() target_url = item.find('a').get('href') print(title,target_url) def run(): pool = ThreadPoolExecutor(5) for i in range(1,50): pool.submit(task,'https://dig.chouti.com/all/hot/recent/%s' %i) if __name__ == '__main__': run()
总结:
1. 以上示例采用线程好还是进程好?
线程好,因为网络请求是IO操作, 不占用cup开线程可以提高效率.
使用线程的过程中,最好是使用线程池.
2. requests模块 模拟浏览器发送请求
本质: ret = requests(url)
原理: - 创建socket客户端
- 连接 (会阻塞)
- 发送请求
- 接收请求 (会阻塞) 返回ret
- 断开连接
3. bs4 中的 BeautifulSoup 模块
该模块提供了很多的方法来解析网页中的源码.

浙公网安备 33010602011771号