我有一个程序可以创建一个多处理池来处理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框中运行。
答案 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.Pool
和timeout()
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()