
python之多进程
写在前面
前面学习了多线程今天来学习一下多进程,相对于多线程,其实多进程的使用在日常生活中会使用更多,我们可以使用多进程来加速自己的爬虫。所以多进程是必须要学习的,但是其实多进程和多线程其实很相似,建议没有学习过多线程的先去将多线程学习一下。
什么是多进程
多进程 Multiprocessing 和多线程 threading 类似, 他们都是在 python 中用来并行运算的.
用多进程比多线程更加方便,我们为了方便所以使用多进程
开始多进程
多进程和多线程是真的很相似,建议先学多线程那样学这个也会很快的。
python的多进程支持
python是支持多进程的,而且是python的内置库,所以我们在使用的时候只需要将其引入就好,就像是这样:
import multiprocessing as mp
引入multiprocessing然后给它一个别名叫做mp,因为太长了后面不好使用所以起一个别名。
multiprocessing
常用函数:
myprocess = mp.Process() #添加一个新的进程,
myprocess.start()#开始一个新进程
myprocess.join() # 将进程加入阻塞
提示: 在使用的时候一定要记得加上main函数
进程池
这是一个新的概念,你可以使用进程池来快速生成进程。这个比线程要方便很多。
添加进程池:
import multiprocessing as mp
def job(x):
return x**x
def multiprocess():
pool = mp.Pool()
res = pool.map(job, range(10))
print(res)
if __name__ == '__main__':
multiprocess()
"""
结果:
[1, 1, 4, 27, 256, 3125, 46656, 823543, 16777216, 387420489]
"""
然后说到创建进程池就要详细讲述一下创建的方法和进程池的方法:
Pool函数
创建进程池的方法:其实很简单,但是像上面的那样去创建的话是使用你所有的进程,就是你有几个核心就创建相应的进程数,如果想要创建指定进程数的进程池的话可以在Pool函数中加上processes=2就像是这样“pool = Pool(processes=2)”
map函数
进程池的map函数,这是一个讲自己的任务分配给进程池的函数,job指定工作的函数,后面是一个参数
apply_async函数
如果你不想将所有将一个分开给进程任务传递参数那么你就需要apply_async函数,这个函数时只能传递一个参数:
res = pool.apply_async(job, (i,))
这里要注意的是参数必须传递一个可迭代对象,所以我们建议在传递的时候传递元祖类型的数据。
这样我们还可以将值取出来:
res.get()
这个时候如果你有新的想法,我想传递多个参数怎么办?那我们可以使用python中的生成器来传递参数,就像是这样:
res = [pool.apply_async(job, (i)) for i in range(10)]
当然在取值的时候也是需要一个一个的取出来。
实例
我在这里面呢爬取了豆瓣电影的top10,先演示普通的方法以及用时:
import multiprocessing as mp
from lxml import etree
import requests
import time
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit"
"/537.36 (KHTML, like Gecko) "
"Chrome/72.0.3626.121 Safari/537.36"}
r_url = "https://movie.douban.com/chart"
def crawl(surl):
res = requests.get(url=surl, headers=headers).content.decode()
response = etree.HTML(res)
return response
def get_url_list(response):
return response.xpath('//*[@id="content"]/div/div[1]/div/div/table/tr/td[2]/div/a/@href')
def get_content(resonse):
name = resonse.xpath('//*[@id="content"]/h1/span[1]//text()')
author = resonse.xpath('//*[@id="info"]/span[1]/span[2]//text()')
print(name, author)
if __name__ == '__main__':
t1 = time.time()
home = crawl(r_url)
url_list = get_url_list(home)
for url in url_list:
now = crawl(url)
get_content(now)
print("use time:" , time.time()-t1)
"""
['谁先爱上他的 誰先愛上他的'] ['徐誉庭', ' / ', '许智彦']
['小偷家族 万引き家族'] ['是枝裕和']
['波西米亚狂想曲 Bohemian Rhapsody'] ['布莱恩·辛格']
['神奇动物:格林德沃之罪 Fantastic Beasts: The Crimes of Grindelwald'] ['大卫·叶茨']
['地球最后的夜晚'] ['毕赣']
['无敌破坏王2:大闹互联网 Ralph Breaks the Internet'] ['菲尔·约翰斯顿', ' / ', '瑞奇·摩尔']
['海王 Aquaman'] ['温子仁']
['宠儿 The Favourite'] ['欧格斯·兰斯莫斯']
['调音师 Andhadhun'] ['斯里兰姆·拉格万']
['摇摆狂潮 스윙키즈'] ['姜炯哲']
use time: 10.657264709472656
"""
我在这里的思路是首先获取到每一个电影的详情页面的URL,然后循环请求输出电影名称和导演,没做其他处理。
下面展示一下使用多进程的情况:
import multiprocessing as mp
from lxml import etree
import requests
import time
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit"
"/537.36 (KHTML, like Gecko) "
"Chrome/72.0.3626.121 Safari/537.36"}
r_url = "https://movie.douban.com/chart"
def crawl(surl):
res = requests.get(url=surl, headers=headers).content.decode()
response = etree.HTML(res)
return response
def get_url_list(response):
return response.xpath('//*[@id="content"]/div/div[1]/div/div/table/tr/td[2]/div/a/@href')
def get_content(resonse):
name = resonse.xpath('//*[@id="content"]/h1/span[1]//text()')
author = resonse.xpath('//*[@id="info"]/span[1]/span[2]//text()')
print(name, author)
return (name, author)
if __name__ == '__main__':
t1 = time.time()
home = crawl(r_url)
url_list = get_url_list(home)
pool = mp.Pool()
now_crawl = [pool.apply_async(crawl, (urls,)) for urls in url_list]
contents = [j for j in now_crawl]
cont = [pool.apply_async(get_content, (c,))for c in contents]
cont = [k for k in cont]
for s in cont:
print(s)
print("use time:" , time.time()-t1)
'''
(['谁先爱上他的 誰先愛上他的'], ['徐誉庭', ' / ', '许智彦'])
(['小偷家族 万引き家族'], ['是枝裕和'])
(['波西米亚狂想曲 Bohemian Rhapsody'], ['布莱恩·辛格'])
(['神奇动物:格林德沃之罪 Fantastic Beasts: The Crimes of Grindelwald'], ['大卫·叶茨'])
(['地球最后的夜晚'], ['毕赣'])
(['无敌破坏王2:大闹互联网 Ralph Breaks the Internet'], ['菲尔·约翰斯顿', ' / ', '瑞奇·摩尔'])
(['海王 Aquaman'], ['温子仁'])
(['宠儿 The Favourite'], ['欧格斯·兰斯莫斯'])
(['调音师 Andhadhun'], ['斯里兰姆·拉格万'])
(['摇摆狂潮 스윙키즈'], ['姜炯哲'])
use time: 3.403460741043091
'''
然后至于为什么没输出值我也还在折腾,但是看一下使用的时间:第一个使用了10.7秒、第二个使用了3.0秒,这大概是3倍的差距,虽然这个因为爬取的东西比较少有一定水分但是还是有一定的差距,所以好好学习吧。
写在后面
当我在使用multiprocessing时遇到的坑
当我在使用的时候,我遇到了这样一个问题,那就是:

出现了这个错误,同时我被这个困扰了很久。最后终于找到了解决问题,那就是不要在函数中做一些数据处理,只要传递数据就好,其他的数据处理请在自定义函数中处理。上面那个代码就是错误示范,因为我是将数据处理后在函数中间传递,所以之前一直出现问题。关于正确的代码我会放在我的git中,欢迎去看:传送门
网友评论