Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/list/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/jsf-2/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python Web scraper在进行多处理时静默挂起_Python_Multiprocessing - Fatal编程技术网

Python Web scraper在进行多处理时静默挂起

Python Web scraper在进行多处理时静默挂起,python,multiprocessing,Python,Multiprocessing,我正在抓取一个包含几十个基本URL的站点,这些基本URL最终链接到我解析的数千个xml页面,转换成一个Pandas数据框架,并最终保存到SQLite数据库。为了节省时间,我对下载/解析阶段进行了多处理,但是脚本在一定数量的页面(不确定有多少;在100到200之间)之后会自动挂起(停止收集页面或解析XML) 使用相同的解析器,但按顺序执行所有操作(无多处理)不会产生任何问题,因此我怀疑我在多处理方面做错了什么。可能是创建了太多的Parse_url类实例并阻塞了内存 以下是流程概述: engine

我正在抓取一个包含几十个基本URL的站点,这些基本URL最终链接到我解析的数千个xml页面,转换成一个Pandas数据框架,并最终保存到SQLite数据库。为了节省时间,我对下载/解析阶段进行了多处理,但是脚本在一定数量的页面(不确定有多少;在100到200之间)之后会自动挂起(停止收集页面或解析XML)

使用相同的解析器,但按顺序执行所有操作(无多处理)不会产生任何问题,因此我怀疑我在多处理方面做错了什么。可能是创建了太多的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\u url,url\u list\u slice)


我的多处理设置是否存在明显问题?是否有可能我生成了太多的Parse_url类实例?

很确定这并不理想,但它确实有效。假设问题是多进程创建的对象太多,我添加了一个明确的“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 = []

批处理完成后,仍然存在挂起。在第二个循环中,您在每个不理想的迭代中创建一个
池。PythonGC非常懒惰,因此您的软件在迭代过程中积累了大量资源

多处理.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)

我看不到任何地方定义了
collect\u xml\u链接
start\u URL
,修复程序在哪里?仍然没有
def collect\u xml\u链接
或iterable
start\u url
。猜测类实例正在累积,我在
write\u info\u to\u sql(result,db\u name,engine)
之后添加了
del(result)
。这似乎已经解决了问题,但我想知道是否有更好的解决方案实际上这里的信息太少了。代码过于精简;可能是您正在以死锁的方式使用XML解析器。为什么
Parse\u url
是一个上下文管理器?Python GC只关注循环引用;通过引用计数,立即清除资源。此外,这些资源是在单独的进程中创建的,而不是在主进程中创建的。我用错了词。我指的资源是池资源(管道、线程、映射结果等)。创建和销毁池的快速循环会给人一种资源泄漏的感觉,因为池中的线程延长了相关对象的生命周期。
join
方法确保逻辑将等待所有资源释放,但OP不会调用它。尽管如此,推荐的方法是创建一个
,并在整个软件生命周期中使用它。值得一提的是,按照这个建议,停止了挂起,刮板现在正确地完成了~5000个XML页面,据我所知这很有帮助!