进程 + 数据共享 + 锁(进程) + 进程池 + 模块(爬虫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 模块

     该模块提供了很多的方法来解析网页中的源码.

posted @ 2018-09-12 15:32  葡萄想柠檬  Views(165)  Comments(0)    收藏  举报
目录代码