Python的进程和线程(二)——IO密集型任务

一、什么是IO密集型任务?

主要的核心任务是进行IO操作,比如写文件,进行磁盘的读写等等。

上一篇博客,对计算密集型任务,多线程并没有体现它的好处,反而话费的时间更长。对IO密集型任务多线程会有明显的优势

二、举例:

任务:爬取韩寒的博客

1、获取urls,

2、根据文章的url,爬取内容,保存文件

3、将urls分给不同的进程/线程

4、多进程/多线程爬取

step1:爬取urls

思路:用requests库来爬取页面,用beautifulSoup库来获取目标的urls值

import requests
from bs4 import BeautifulSoup

def get_urls(url):
    '''
    获取目标URL
    :param url: 列表页面url
    :return: 目标url列表
    '''
    res=requests.get(url).text
    soup = BeautifulSoup(res, features="html.parser")
    #通过css selector解析页面,获取元素
    artile_urls=soup.select(".atc_title > a")
    url_list=list(i.get("href") for i in artile_urls)
    return(url_list)

step2:根据文章的url,爬取内容,保存文件

import time,os
import requests
from bs4 import BeautifulSoup

def get_content(urls,dirpath):
    '''
    获取文章内容
    :param urls: 要获取文章的url列表
    :param dirpath: 文章内容文件保存路径
    :return: 
    '''

    for url in urls:
        # print("要抓取的url是%s" % url)
        res = requests.get(url).content.decode("utf-8")

        soup = BeautifulSoup(res, features="html.parser")
        paragraphs = soup.select("#sina_keyword_ad_area2 > p")
        content=""
        for i in paragraphs:
            content+=i.get_text()
        if not os.path.exists(dirpath):
            os.makedirs(dirpath)
        open(dirpath + r'/' + url[-26:], 'w').write(content)

step3:将urls分给不同的进程/线程 

思路:

  假设我们启动n个进程,一共要爬取的url列表是urls,列表的长度为url_len

  我们先把列表整除n得到长度为L(不一定能够整除,所以最后一个进程为总数-前面n-1个进程的),则第1个进程要爬取的url列表是urls[0:L],第2个进程要爬取的url列表是url[L: 2L],依次类推。。。。,最后一个进程要爬取的url列表是url[i*n:url_len]

    for n in [8, 4, 2, 1]:
        # 将urls分割到url_list
        url_list = []
        url_split_len = url_len // n
        for i in range(n):
            if i == n - 1:
                url_list.append(urls[i * url_split_len:url_len])
            else:
                url_list.append(urls[i * url_split_len:(i + 1) * url_split_len])
           

 

参照上一篇博客,多进程和多线程函数。

我们的目标函数是,get_content,这个函数需要2个参数,一个是url列表,一个是保存文件的路径

import time

def thread_process_job(n, Thread_or_Process, url_list, job):
    """
    n: 多线程或多进程数
    Thread_Process: Thread/Process类
    job: countdown任务
    """
    local_time = time.time()

    # 实例化多线程或多进程
    threads_or_processes = [Thread_or_Process(target=job, args=(url_list[j],str(n)+Thread_or_Process.__name__)) for j in range(n)]
    for t in threads_or_processes:
        t.start()  # 开始线程或进程,必须调用
    for t in threads_or_processes:
        t.join()  # 等待直到该线程或进程结束
    print(n, Thread_or_Process.__name__, " run job need ", time.time() - local_time)

 step4:多进程或者多线程爬取

我们爬取前6页的数据,代码如下

if __name__=="__main__":
    t = time.time()

    urls = []
    for i in range(7):
        url='http://blog.sina.com.cn/s/articlelist_1191258123_0_' + str(i + 1) + '.html'
        page_urls=get_urls(url)
        urls.extend(page_urls)
    url_len = len(urls)
    print("total urls number is ", url_len)

    for n in [8, 4, 2, 1]:
        # 将urls分割到url_list
        url_list = []
        url_split_len = url_len // n
        for i in range(n):
            if i == n - 1:
                url_list.append(urls[i * url_split_len:url_len])
            else:
                url_list.append(urls[i * url_split_len:(i + 1) * url_split_len])
        # 分割任务后创建线程
        thread_process_job(n, Thread, url_list, get_content)
        thread_process_job(n, Process, url_list, get_content)

    print("All done in ", time.time() - t)

代码可能存在的问题:

代码在运行中,可能会存在的问题是,调用get_content函数时,可能一个进程正在创建文件夹,一个进程正好在判断文件不存在要创建,在创建的时候会报错文件已存在。

 

posted @ 2019-03-17 22:43  一秒一看  阅读(572)  评论(0编辑  收藏  举报