Python selenium多处理

rob*_*txt 16 python selenium multiprocessing web-scraping python-3.x

我在python中编写了一个与selenium结合使用的脚本,以便从其着陆页中抓取不同帖子的链接,最后通过跟踪通向其内页的URL来获取每个帖子的标题.虽然我在这里解析的内容是静态内容,但我使用selenium来了解它在多处理中的工作原理.

但是,我的意图是使用多处理进行抓取.到目前为止,我知道selenium不支持多处理,但似乎我错了.

我的问题:当使用selenium进行多处理运行时,如何减少执行时间?

This is my try (it's a working one):

import requests
from urllib.parse import urljoin
from multiprocessing.pool import ThreadPool
from bs4 import BeautifulSoup
from selenium import webdriver

def get_links(link):
  res = requests.get(link)
  soup = BeautifulSoup(res.text,"lxml")
  titles = [urljoin(url,items.get("href")) for items in soup.select(".summary .question-hyperlink")]
  return titles

def get_title(url):
  chromeOptions = webdriver.ChromeOptions()
  chromeOptions.add_argument("--headless")
  driver = webdriver.Chrome(chrome_options=chromeOptions)
  driver.get(url)
  sauce = BeautifulSoup(driver.page_source,"lxml")
  item = sauce.select_one("h1 a").text
  print(item)

if __name__ == '__main__':
  url = "https://stackoverflow.com/questions/tagged/web-scraping"
  ThreadPool(5).map(get_title,get_links(url))
Run Code Online (Sandbox Code Playgroud)

mir*_*ixx 9

当使用selenium进行多处理运行时,如何减少执行时间

解决方案中的大量时间花在为每个URL启动webdriver上.您可以通过每个线程仅启动一次驱动程序来减少此时间:

(... skipped for brevity ...)

threadLocal = threading.local()

def get_driver():
  driver = getattr(threadLocal, 'driver', None)
  if driver is None:
    chromeOptions = webdriver.ChromeOptions()
    chromeOptions.add_argument("--headless")
    driver = webdriver.Chrome(chrome_options=chromeOptions)
    setattr(threadLocal, 'driver', driver)
  return driver


def get_title(url):
  driver = get_driver()
  driver.get(url)
  (...)

(...)
Run Code Online (Sandbox Code Playgroud)

在我的系统上,这将时间从1m7s缩短到24.895s,提高了约35%.要测试自己,请下载完整的脚本.

注意:ThreadPool使用受Python GIL约束的线程.如果在大多数情况下任务是I/O绑定的话,那就没问题.根据您对搜索结果进行的后处理,您可能希望使用multiprocessing.Pool替代.这将启动并行进程,这些进程作为一个组不受GIL约束.其余代码保持不变.


Boo*_*boo 5

我看到一个聪明的每线程一个驱动程序答案的一个潜在问题是它省略了任何“退出”驱动程序的机制,从而留下了进程挂起的可能性。我将进行以下更改:

  1. 使用类Driver来创建驱动程序实例并将其存储在线程本地存储上,但也有一个析构函数,quit当线程本地存储被删除时,驱动程序将执行:
class Driver:
    def __init__(self):
        options = webdriver.ChromeOptions()
        options.add_argument("--headless")
        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".')
Run Code Online (Sandbox Code Playgroud)
  1. create_driver 现在变成:
threadLocal = threading.local()

def create_driver():
    the_driver = getattr(threadLocal, 'the_driver', None)
    if the_driver is None:
        the_driver = Driver()
        setattr(threadLocal, 'the_driver', the_driver)
    return the_driver.driver
Run Code Online (Sandbox Code Playgroud)
  1. 最后,在您不再使用该ThreadPool实例之后但在它终止之前,添加以下行以删除线程本地存储并强制Driver调用实例的析构函数(希望如此):
del threadLocal
import gc
gc.collect() # a little extra insurance
Run Code Online (Sandbox Code Playgroud)