我正在嘗試多行程 selenium,其中每個行程都生成一個 selenium 驅動程式和一個會話(每個行程都與不同的帳戶連接)。
我有一個要訪問的 URL 串列。每個 URL 都需要被其中一個帳戶訪問一次(無論是哪個)。
為了避免一些討厭的全域變數的管理,我試圖用一個類物件初始化每個行程使用initializer的multiprocessing.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 和決議,但這會非常討厭。
有沒有辦法實作這一目標?
uj5u.com熱心網友回復:
我不完全確定這是否能解決您的問題。
如果每個 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())
uj5u.com熱心網友回復:
由于 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()
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/391054.html
下一篇:如何改進我的多執行緒代碼
