如果运行时间超过x分钟,请重新启动进程

时间:2014-10-22 20:20:29

标签: python selenium multiprocessing

我有一个程序可以创建一个多处理池来处理webextraction工作。实质上,产品ID列表被输入到处理队列的10个进程池中。代码非常简单:

import multiprocessing

num_procs = 10

products = ['92765937', '20284759', '92302047', '20385473', ...etc]

def worker():
    for workeritem in iter(q.get, None):
        time.sleep(10)
        get_product_data(workeritem)
        q.task_done()
    q.task_done()

q = multiprocessing.JoinableQueue()

procs = []

for i in range(num_procs):
    procs.append(multiprocessing.Process(target=worker))
    procs[-1].daemon = True
    procs[-1].start()

for product in products:
    time.sleep(10)
    q.put(product)

q.join()

for p in procs:
    q.put(None)

q.join()

for p in procs:
    p.join()

get_product_data()函数获取产品,打开Selenium实例,导航到站点,登录,并收集产品的详细信息并输出到csv文件。问题是,随机(字面意思......它发生在网站的导航或提取过程的不同点)Selenium将停止做它正在做的事情,只是坐在那里停止做它的工作。不会抛出异常或任何异常。我已经在get_product_data()函数中完成了所有工作,以避免发生这种情况,但它似乎只是Selenium的一个问题(我尝试使用Firefox,PhantomJS和Chrome作为它的驱动程序,仍然遇到同样的问题,无论如何)。

基本上,这个过程永远不会超过10分钟。有没有办法杀死进程并使用相同的产品ID重新启动它,如果它已运行超过指定的时间?

这一切都在使用Python 2.7的Debian Wheezy框中运行。

2 个答案:

答案 0 :(得分:1)

您需要让Selenium等待一段明确的时间,或者等待某些隐式DOM对象可用。 Take a quick look at the selenium docs about that

在链接中,这是一个等待10 seconds DOM元素myDynamicElement出现的过程。

from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait # available since 2.4.0
from selenium.webdriver.support import expected_conditions as EC # available since 2.26.0

ff = webdriver.Firefox()
ff.get("http://somedomain/url_that_delays_loading")
try:
    element = WebDriverWait(ff, 10).until(EC.presence_of_element_located((By.ID, "myDynamicElement")))
except TimeoutException as why:
    # Do something to reject this item, possibly by re-adding it to the worker queue.
finally:
    ff.quit()

如果在给定时间段内没有任何内容可用,则会引发selenium.common.exceptions.TimeoutException,您可以在上面的try / except循环中捕获它。

修改

另一个选择是让multiprocessing在一段时间后超时。这是使用内置库signal完成的。 Here's an excellent example of doing this但是,当您检测到某个进程已被终止时,仍然需要将该项添加回工作队列。您可以在代码的def handler部分执行此操作。

答案 1 :(得分:1)

您可以使用multiprocessing.Pooltimeout() function suggested by @VooDooNOFX编写代码。未经测试,将其视为可执行的伪代码:

#!/usr/bin/env python
import signal
from contextlib import closing
from multiprocessing import Pool

class Alarm(Exception):
    pass

def alarm_handler(*args):
    raise Alarm("timeout")

def mp_get_product_data(id, timeout=10, nretries=3):
    signal.signal(signal.SIGALRM, alarm_handler) #XXX could move it to initializer
    for i in range(nretries):
        signal.alarm(timeout) 
        try:
            return id, get_product_data(id), None
        except Alarm as e:
            timeout *= 2 # retry with increased timeout
        except Exception as e:
            break
        finally:
            signal.alarm(0) # disable alarm, no need to restore handler
    return id, None, str(e) 

if __name__=="__main__":
   with closing(Pool(num_procs)) as pool:
       for id, result, error in pool.imap_unordered(mp_get_product_data, products):
           if error is not None: # report and/or reschedule
              print("error: {} for {}".format(error, id))
   pool.join()