Python的进程和线程(二)——IO密集型任务
一、什么是IO密集型任務(wù)?
主要的核心任務(wù)是進(jìn)行IO操作,比如寫(xiě)文件,進(jìn)行磁盤(pán)的讀寫(xiě)等等。
上一篇博客,對(duì)計(jì)算密集型任務(wù),多線程并沒(méi)有體現(xiàn)它的好處,反而話費(fèi)的時(shí)間更長(zhǎng)。對(duì)IO密集型任務(wù)多線程會(huì)有明顯的優(yōu)勢(shì)
二、舉例:
任務(wù):爬取韓寒的博客
1、獲取urls,
2、根據(jù)文章的url,爬取內(nèi)容,保存文件
3、將urls分給不同的進(jìn)程/線程
4、多進(jìn)程/多線程爬取
step1:爬取urls
思路:用requests庫(kù)來(lái)爬取頁(yè)面,用beautifulSoup庫(kù)來(lái)獲取目標(biāo)的urls值
import requests from bs4 import BeautifulSoupdef get_urls(url):'''獲取目標(biāo)URL:param url: 列表頁(yè)面url:return: 目標(biāo)url列表'''res=requests.get(url).textsoup = BeautifulSoup(res, features="html.parser")#通過(guò)css selector解析頁(yè)面,獲取元素artile_urls=soup.select(".atc_title > a")url_list=list(i.get("href") for i in artile_urls)return(url_list)step2:根據(jù)文章的url,爬取內(nèi)容,保存文件
import time,os import requests from bs4 import BeautifulSoupdef get_content(urls,dirpath):'''獲取文章內(nèi)容:param urls: 要獲取文章的url列表:param dirpath: 文章內(nèi)容文件保存路徑: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分給不同的進(jìn)程/線程?
思路:
假設(shè)我們啟動(dòng)n個(gè)進(jìn)程,一共要爬取的url列表是urls,列表的長(zhǎng)度為url_len
我們先把列表整除n得到長(zhǎng)度為L(zhǎng)(不一定能夠整除,所以最后一個(gè)進(jìn)程為總數(shù)-前面n-1個(gè)進(jìn)程的),則第1個(gè)進(jìn)程要爬取的url列表是urls[0:L],第2個(gè)進(jìn)程要爬取的url列表是url[L: 2L],依次類(lèi)推。。。。,最后一個(gè)進(jìn)程要爬取的url列表是url[i*n:url_len]
for n in [8, 4, 2, 1]:# 將urls分割到url_listurl_list = []url_split_len = url_len // nfor 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])?
參照上一篇博客,多進(jìn)程和多線程函數(shù)。
我們的目標(biāo)函數(shù)是,get_content,這個(gè)函數(shù)需要2個(gè)參數(shù),一個(gè)是url列表,一個(gè)是保存文件的路徑
import timedef thread_process_job(n, Thread_or_Process, url_list, job):"""n: 多線程或多進(jìn)程數(shù)Thread_Process: Thread/Process類(lèi)job: countdown任務(wù)"""local_time = time.time()# 實(shí)例化多線程或多進(jìn)程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() # 開(kāi)始線程或進(jìn)程,必須調(diào)用for t in threads_or_processes:t.join() # 等待直到該線程或進(jìn)程結(jié)束print(n, Thread_or_Process.__name__, " run job need ", time.time() - local_time)?step4:多進(jìn)程或者多線程爬取
我們爬取前6頁(yè)的數(shù)據(jù),代碼如下
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_listurl_list = []url_split_len = url_len // nfor 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])# 分割任務(wù)后創(chuàng)建線程 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)代碼可能存在的問(wèn)題:
代碼在運(yùn)行中,可能會(huì)存在的問(wèn)題是,調(diào)用get_content函數(shù)時(shí),可能一個(gè)進(jìn)程正在創(chuàng)建文件夾,一個(gè)進(jìn)程正好在判斷文件不存在要?jiǎng)?chuàng)建,在創(chuàng)建的時(shí)候會(huì)報(bào)錯(cuò)文件已存在。
?
轉(zhuǎn)載于:https://www.cnblogs.com/yimiaoyikan/p/10485207.html
總結(jié)
以上是生活随笔為你收集整理的Python的进程和线程(二)——IO密集型任务的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 浏览器的缓存机制
- 下一篇: Pipenv和Python虚拟环境