我正在抓取一个包含几十个基本网址的网站,最终链接到我解析的几千个xml页面,变成一个Pandas数据帧,最后保存到SQLite数据库。我对下载/解析阶段进行了多处理以节省时间,但是脚本在一定数量的页面之后静默挂起(停止收集页面或解析XML)(不确定多少;在100到200之间)。
使用相同的解析器但按顺序执行所有操作(没有多处理)不会产生任何问题,所以我怀疑我在多处理方面做错了。也许创建Parse_url类的太多实例并堵塞内存?
以下是该流程的概述:
engine = create_engine('sqlite:///path_to_db') # sqlalchemy
class Parse_url():
def __init__(self, url):
self.url = url
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
return True
def parse(self):
# parse xml, return dataframes
def collect_xml_links(start_url):
# collect and return a list of links to XML pages on this starting URL
def parse_urls(url):
with Parse_url(url) as parser:
collection_of_dfs = parser.parse()
return collection_of_dfs
def write_info_to_sql(result, db_name, engine):
# write info to SQLite database
start_urls = [url1, url2, url3, ... ]
with Pool(4) as pool:
results = pool.map(collect_xml_links, start_urls)
for urls in results:
url_list.extend(urls) # This works and returns urls
for i in range(0, len(url_list), 50): # Chunks of 50 to report progress
url_list_slice = url_list[i:i+50]
with Pool(4) as pool:
results = pool.map(parse_urls, url_list_slice)
for result in results:
write_info_to_sql(result, db_name, engine)
当脚本挂起时,它似乎总是在刮取相似数量的页面时这样做,但我不确定它是否完全相同。杀死脚本会产生一个无用的追踪,指向results = pool.map(parse_urls, url_list_slice)
行。
我的多处理设置是否存在明显问题?我是否有可能生成过多的Parse_url类实例?
答案 0 :(得分:1)
在第二个循环中,您在每次迭代时创建Pool
,这不是理想的。 Python gc非常懒惰,所以你的软件会在迭代过程中积累大量资源。
multiprocessing.Pool
专为重复使用而设计,因此您只需在脚本中创建一次。
with Pool(4) as pool:
results = pool.map(collect_xml_links, start_urls)
for urls in results:
url_list.extend(urls) # This works and returns urls
for i in range(0, len(url_list), 50): # Chunks of 50 to report progress
url_list_slice = url_list[i:i+50]
results = pool.map(parse_urls, url_list_slice)
for result in results:
write_info_to_sql(result, db_name, engine)
答案 1 :(得分:0)
非常确定这不是理想的,但它确实有效。假设问题是多进程创建了太多对象,我添加了一个明确的" del"像这样的步骤:
for i in range(0, len(url_list), 50): # Chunks of 50 to report progress
url_list_slice = url_list[i:i+50]
with Pool(4) as pool:
results = pool.map(parse_urls, url_list_slice)
for result in results:
write_info_to_sql(result, db_name, engine)
del(result) # explicitly delete the dataframes when done with them
我不确定为什么这些对象会持久存在,因为它似乎没有任何引用留给它们所以它们应该被垃圾收集。我尝试了其他一些方法来删除对它们的引用,例如
results = []
在批处理全部写完之后,但仍然有人使用它。