线程池正常,改为进程池就报错,求多进程大神解答一下
代码比较多, 这里就节选了一点说明,大家都知道ThreadPoolExecutor和ProcessPoolExecutor用法几乎是一致的。然后我就把下面的多线程ThreadPoolExecutor()更改为多进程ProcessPoolExecutor(),但是就直接报错了。
错误代码为:RecursionError: maximum recursion depth exceeded while getting the str of an object
问题:我并没有写递归函数,仅仅是爬取了250个电源的排名信息,为什么会报递归超过深度错误呢?如果要修改,应该如何修改呢,跪求大神!
多线程代码:
...这里有很多代码
def many_thread(self):
with ThreadPoolExecutor() as pool: #-----本身是正常运行的,这里更改为ProcessPoolExecutor就会报错!----------
jiexi_html = pool.map(self.x_get_html,self.url_list)#获取每一页的解析页面列表
result = pool.map(self.get_everypage_info,jiexi_html) #获取每一页页面的电影信息
for i in result:
for a in i:
print(a,end='\n')
....这里也有很多代码
多进程代码:
其他都没有更改,就更改了下面一处。
def many_thread(self):
with ProcessPoolExecutor() as pool: #-----本身是正常运行的,这里更改为ProcessPoolExecutor就会报错!----------
jiexi_html = pool.map(self.x_get_html,self.url_list)#获取每一页的解析页面列表
result = pool.map(self.get_everypage_info,jiexi_html) #获取每一页页面的电影信息
for i in result:
for a in i:
print(a,end='\n') 使用多进程后,你的程序入口必须要置于if __name__ == "__main__"分支下才能保证创建新进程后不会发生递归。
例如:
# any code
# ...
def main():
# 入口
...
if __name__ == "__main__":
main() 因为你是windows系统{:10_331:} Linux铁定不报错 hrpzcf 发表于 2021-11-15 15:12
使用多进程后,你的程序入口必须要置于if __name__ == "__main__"分支下才能保证创建新进程后不会发生递归 ...
我是这样写的哦, 从入门到富豪 发表于 2021-11-15 21:47
我是这样写的哦,
没有全代码不好分析,总之在windows上创建新进程是要重新导入整个文件,如果程序入口不在name==main分支下,文件会被再次运行,再次创建新进程,再次导入,再次运行...无限递归,你从这方面分析一下你的程序吧,看看有没有零散的语句在导入的时候直接被执行的,把它们都包装成函数或者其他,再定义一个唯一入口放在name==main下。 hrpzcf 发表于 2021-11-15 22:01
没有全代码不好分析,总之在windows上创建新进程是要重新导入整个文件,如果程序入口不在name==main分 ...
非常感谢你的答复,我附上完整的代码。的确都是写在 name==main下面的。
import requests
import time
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
class Get_Movie():
def __init__(self,url_list):
self.url_list = url_list
pass
#获取网页,并解析网页为text模式,通用函数前面加x
def x_get_html(self,url):
try:
# header = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36'}
ua = UserAgent(verify_ssl=False)
header = {"User-Agent": ua.random}
requests.packages.urllib3.disable_warnings()
response = requests.get(url,verify=False,headers=header)
if response.status_code != 200:
raise Exception('链接网页服务器不成功')
else:
response.encoding = response.apparent_encoding# 获取网页字体编码,防止乱码
html = response.text
soup = BeautifulSoup(html,'lxml')#解析一个网页
return soup
except:
print('网页不存在')
def get_everypage_info(self,x):
everyone_moive = x.find('ol',class_='grid_view').find_all('div',class_='item')
a = [(i.find('em').string,i.find('span',class_='title').string,i.find('span',class_='rating_num').string) for i in everyone_moive]
return a
def many_thread(self):
with ProcessPoolExecutor() as pool:
jiexi_html = pool.map(self.x_get_html,self.url_list)#获取每一页的解析页面列表
result = pool.map(self.get_everypage_info,jiexi_html) #获取每一页页面的电影信息
for i in result:
for a in i:
print(a,end='\n')
# print(f'{i}+\n')
if __name__=='__main__':
start = time.process_time()
getmovie = Get_Movie()
getmovie.many_thread()
end = time.process_time()
print(f'一共用时:{end-start}秒') 从入门到富豪 发表于 2021-11-17 10:10
非常感谢你的答复,我附上完整的代码。的确都是写在 name==main下面的。
import requests
import time
...
因为进程的返回值要求是可序列化的对象(你的返回值是列表套元组),把它改正就好(字符串列表),为了方便我把他合成一个函数了。
import requests
import time
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
from bs4 import BeautifulSoup
from fake_useragent import UserAgent
class Get_Movie:
def __init__(self, url_list):
self.url_list = url_list
pass
# 获取网页,并解析网页为text模式,通用函数前面加x
def x_get_html(self, url):
try:
# header = {'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36'}
ua = UserAgent(verify_ssl=False)
header = {"User-Agent": ua.random}
requests.packages.urllib3.disable_warnings()
response = requests.get(url, verify=False, headers=header)
if response.status_code != 200:
raise Exception("链接网页服务器不成功")
else:
response.encoding = response.apparent_encoding# 获取网页字体编码,防止乱码
html = response.text
soup = BeautifulSoup(html, "lxml")# 解析一个网页
return self.get_everypage_info(soup)
except:
print("网页不存在")
def get_everypage_info(self, x):
everyone_moive = x.find("ol", class_="grid_view").find_all("div", class_="item")
a = [
", ".join(
(i.find("em").string,
i.find("span", class_="title").string,
i.find("span", class_="rating_num").string)
)
for i in everyone_moive
]
return a
def many_thread(self):
with ProcessPoolExecutor() as pool:
jiexi_html = pool.map(self.x_get_html, self.url_list)# 获取每一页的解析页面列表
# result = pool.map(self.get_everypage_info, jiexi_html)# 获取每一页页面的电影信息
for i in jiexi_html:
for a in i:
print(a, end="\n")
# print(f'{i}+\n')
if __name__ == "__main__":
start = time.perf_counter() # 用这个才是秒数
getmovie = Get_Movie(
[
f"https://movie.douban.com/top250?start={x}&filter="
for x in range(0, 250, 25)
]
)
getmovie.many_thread()
end = time.perf_counter()
print(f"一共用时:{end-start}秒")
hrpzcf 发表于 2021-11-17 12:03
因为进程的返回值要求是可序列化的对象(你的返回值是列表套元组),把它改正就好(字符串列表),为了 ...
感谢大神,能运行了,但是我对
进程的返回值 需要是 可序列化对象,这个不太理解。 也没有找到相关对进程返回值相关的要求的文献,方便详细指导一下吗,非常感谢! 从入门到富豪 发表于 2021-11-17 16:10
感谢大神,能运行了,但是我对
进程的返回值 需要是 可序列化对象,这个不太理解。 也没有找到相关对进 ...
https://docs.python.org/zh-cn/3.9/library/concurrent.futures.html?highlight=processpoolexecutor#concurrent.futures.ProcessPoolExecutor 从入门到富豪 发表于 2021-11-17 16:10
感谢大神,能运行了,但是我对
进程的返回值 需要是 可序列化对象,这个不太理解。 也没有找到相关对进 ...
https://www.jianshu.com/p/53c2e732d974 hrpzcf 发表于 2021-11-17 16:27
https://www.jianshu.com/p/53c2e732d974
大神, 我看了3遍了, 感觉是匹配下面这句话,但是我进程间并不需要通信啊, 也没有通信呀。
函数参数和返回值必须兼容pickle,因为要使用到进程间的通信,所有解释器之间的交换数据必须被序列化 从入门到富豪 发表于 2021-11-17 22:34
大神, 我看了3遍了, 感觉是匹配下面这句话,但是我进程间并不需要通信啊, 也没有通信呀。
函数参数 ...
列表套元组也是序列化的一种呀,为啥就不行了呢? 而且弹出的提示是超出迭代深度呀。 是不是内容迭代的内容太多了。 从入门到富豪 发表于 2021-11-17 22:34
大神, 我看了3遍了, 感觉是匹配下面这句话,但是我进程间并不需要通信啊, 也没有通信呀。
函数参数 ...
子进程把函数结果返回到主进程不是进程间通信?至于嵌套元组的列表为什么不行我也不了解其原理,按理说是可以被序列化的。 从入门到富豪 发表于 2021-11-17 22:42
列表套元组也是序列化的一种呀,为啥就不行了呢? 而且弹出的提示是超出迭代深度呀。 是不是内容迭代的内 ...
按理说是不会发生递归的,但它就是发生了。只要发生递归而且没有出口,那肯定是会超出最大深度的。 hrpzcf 发表于 2021-11-17 23:59
按理说是不会发生递归的,但它就是发生了。只要发生递归而且没有出口,那肯定是会超出最大深度的。
非常感谢, 我再试试, 看能不能挖掘出啥信息。挖到了这里答复哈,{:5_109:} 从入门到富豪 发表于 2021-11-18 10:13
非常感谢, 我再试试, 看能不能挖掘出啥信息。挖到了这里答复哈,
我刚刚尝试了一下, 把第二行用多线程, 还是会报错递归超过深度, 那就感觉是第一行代码的问题了。
def many_thread(self):
with ProcessPoolExecutor() as pool:
jiexi_html = pool.map(self.x_get_html,self.url_list)#获取每一页的解析页面列表
with ThreadPoolExecutor() as pool:
result = pool.map(self.get_everypage_info,jiexi_html) #获取每一页页面的电影信息
for i in result:
for a in i:
print(a,end='\n')
# print(f'{i}+\n') hrpzcf 发表于 2021-11-17 23:54
子进程把函数结果返回到主进程不是进程间通信?至于嵌套元组的列表为什么不行我也不了解其原理,按理说是 ...
测试成功了, 不用这样写也行 ,多进程会传递空值给到函数,导致连环报错,但是不知道为什么会导致传递空值给函数。 加了一个if语句过滤了一下。
def many_thread(self):
with ProcessPoolExecutor() as pool:#标记点1
jiexi_html = pool.map(self.x_get_html, self.url_list)# 获取每一页的解析页面列表
with ProcessPoolExecutor() as pool1: #标记点2 这一步是负优化,仅仅用来测试 因为数据已经get到本地了。直接单线程
# #就可以搞定了,这里使用多线程能正常运行,使用多进程报错:AttributeError: 'NoneType' object has no attribute 'picklable'
# #推测报错原因是有函数或者类无法被序列化。所以无法进入进程池。None的类型就是NoneType,所以没有序列化成功,没有东西进入进程池。
result = pool1.map(self.get_everypage_info, jiexi_html)# 获取每一页页面的电影信息
print(result)
过滤的if语句
def get_everypage_info(self, x):
if x != None: #写这句话,是因为多进程map函数,有时候有传值None进来。
everyone_moive = x.find('ol', class_='grid_view').find_all('div', class_='item')
jieguo1 = [
for i in everyone_moive]
return jieguo1
页:
[1]