【问题标题】:Python多处理一个类
【发布时间】:2022-01-23 13:54:52
【问题描述】:

我正在尝试多进程 selenium,其中每个进程都由一个 selenium 驱动程序和一个会话产生(每个进程都与不同的帐户连接)。

我有一个要访问的 URL 列表。 每个 URL 需要由其中一个帐户访问一次(无论是哪个帐户)。

为了避免一些讨厌的全局变量管理,我尝试使用 initializermultiprocessing.pool 的类对象初始化每个进程。

在那之后,我不知道如何将任务分配给进程,因为我知道每个进程使用的函数必须在类中。

这是我正在尝试做的简化版本:

from selenium import webdriver
import multiprocessing

account =  [{'account':1},{'account':2}]

class Collector():

    def __init__(self, account):

        self.account = account
        self.driver = webdriver.Chrome()

    def parse(self, item):

        self.driver.get(f"https://books.toscrape.com{item}")

if __name__ == '__main__':
    
    processes = 1
    pool = multiprocessing.Pool(processes,initializer=Collector,initargs=[account.pop()])

    items = ['/catalogue/a-light-in-the-attic_1000/index.html','/catalogue/tipping-the-velvet_999/index.html']
    
    pool.map(parse(), items, chunksize = 1)

    pool.close()
    pool.join() 

问题出现在pool.map 行,子进程内没有对实例化对象的引用。 另一种方法是在初始化期间分发 URL 和解析,但这会非常讨厌。

有没有办法做到这一点?

【问题讨论】:

  • 你试过用multiprocessing.Queue吗?
  • 我不熟悉队列,你有什么建议?
  • multiprocessing.Queue 的主要噱头是它可以被多个进程安全使用,我希望示例对您有所帮助,请参阅一个示例,展示如何使用队列将任务提供给收集工作进程并收集结果: 可在multiprocessing docs

标签: python python-3.x multithreading selenium multiprocessing


【解决方案1】:

我不完全确定这是否能解决您的问题。

如果您每个 URL 有一个帐户,那么您可以这样做:

from selenium import webdriver
from multiprocessing import Pool

items = ['/catalogue/a-light-in-the-attic_1000/index.html',
         '/catalogue/tipping-the-velvet_999/index.html']
accounts = [{'account': 1}, {'account': 2}]
baseurl = 'https://books.toscrape.com'

def process(i, a):
    print(f'Processing account {a}')
    options = webdriver.ChromeOptions()
    options.add_argument('--headless')

    with webdriver.Chrome(options=options) as driver:
        driver.get(f'{baseurl}{i}')


def main():
    with Pool() as pool:
        pool.starmap(process, zip(items, accounts))


if __name__ == '__main__':
    main()

如果帐户数量与 URL 数量不匹配,您已经说过,哪个帐户从哪个 URL 获取并不重要。因此,在这种情况下,您可以随机选择要使用的帐户 (random.choice())

【讨论】:

    【解决方案2】:

    由于 Chrome 启动了自己的进程,当多线程就足够了时,确实没有必要使用多处理。我想提供一个更通用的解决方案来处理您想要检索 N 个 URL 的情况,其中 N 可能非常大,但您想将并发 Selenium 会话的数量限制为 MAX_DRIVERS,其中 MAX_DRIVERS 明显更小数字。因此,您只想为池中的每个线程创建一个驱动程序会话并在必要时重用它。然后问题就变成了在您使用完池后在驱动程序上调用 quit,这样您就不会留下任何 Selenium 进程在运行。

    以下代码使用每个线程唯一的threadlocal存储,为每个池线程存储当前驱动实例,并在类实例被销毁时使用类析构函数调用驱动的quit方法:

    from selenium import webdriver
    from multiprocessing.pool import ThreadPool
    import threading
    
    items = ['/catalogue/a-light-in-the-attic_1000/index.html',
             '/catalogue/tipping-the-velvet_999/index.html']
    accounts = [{'account': 1}, {'account': 2}]
    baseurl = 'https://books.toscrape.com'
    
    threadLocal = threading.local()
    
    class Driver:
        def __init__(self):
            options = webdriver.ChromeOptions()
            options.add_argument("--headless")
            options.add_experimental_option('excludeSwitches', ['enable-logging'])
            self.driver = webdriver.Chrome(options=options)
    
        def __del__(self):
            self.driver.quit() # clean up driver when we are cleaned up
            print('The driver has been "quitted".')
    
        @classmethod
        def create_driver(cls):
            the_driver = getattr(threadLocal, 'the_driver', None)
            if the_driver is None:
                the_driver = cls()
                threadLocal.the_driver = the_driver
            return the_driver.driver
    
    
    def process(i, a):
        print(f'Processing account {a}')
        driver = Driver.create_driver()
        driver.get(f'{baseurl}{i}')
    
    
    def main():
        global threadLocal
    
        # We never want to create more than
        MAX_DRIVERS = 8 # Rather arbitrary
        POOL_SIZE = min(len(accounts), MAX_DRIVERS)
        with ThreadPool(POOL_SIZE) as pool:
            pool.starmap(process, zip(items, accounts))
        # ensure the drivers are "quitted":
        del threadLocal
        import gc
        gc.collect() # a little extra insurance
    
    if __name__ == '__main__':
        main()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-08-04
      • 1970-01-01
      相关资源
      最近更新 更多