Python selenium多处理

Python selenium多处理,python,python-3.x,selenium,web-scraping,multiprocessing,Python,Python 3.x,Selenium,Web Scraping,Multiprocessing,我已经用python结合selenium编写了一个脚本,从其登录页中刮取不同帖子的链接,并通过跟踪指向其内部页面的url,最终获得每篇帖子的标题。虽然我在这里解析的内容是静态的,但我使用selenium来了解它在多处理中的工作方式 然而,我的意图是使用多处理来进行刮削。到目前为止,我知道selenium不支持多处理,但似乎我错了 我的问题:当selenium使用多处理运行时,如何使用selenium减少执行时间? 这是我的尝试(这是一个有效的尝试): import requests from u

我已经用python结合selenium编写了一个脚本,从其登录页中刮取不同帖子的链接,并通过跟踪指向其内部页面的url,最终获得每篇帖子的标题。虽然我在这里解析的内容是静态的,但我使用selenium来了解它在多处理中的工作方式

然而,我的意图是使用多处理来进行刮削。到目前为止,我知道selenium不支持多处理,但似乎我错了

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

这是我的尝试(这是一个有效的尝试)

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))
我的问题:如何减少执行时间

Selenium似乎是一个错误的web抓取工具——尽管我很欣赏YMMV,尤其是当您需要模拟用户与web站点的交互或者存在一些JavaScript限制/要求时

对于没有太多交互的抓取任务,我使用开放源代码Python包进行大规模抓取任务取得了良好的效果。它可以进行开箱即用的多处理,很容易编写新脚本并将数据存储在文件或数据库中,而且速度非常快

当作为完全并行的Scrapy spider实现时,您的脚本看起来像这样(注意,我没有测试这个,请参阅)

要运行,请将其放入
blogspider.py
并运行

$ scrapy runspider blogspider.py
有关完整教程,请参见

请注意,由于@SIM的指针,Scrapy还通过支持JavaScript。到目前为止,我还没有接触过它,所以除了它看起来与“刮痧”的工作原理很好地结合在一起之外,我无法谈论它

当使用多处理运行selenium时,如何使用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)
  (...)

(...)
在我的系统上,这将时间从1m7缩短到了24.895秒,提高了约35%。要测试您自己,请下载


注意:
ThreadPool
使用受Python GIL约束的线程。如果大部分任务都是I/O绑定的,那么这也没关系。根据您对刮取的结果所做的后处理,您可能希望改用
多处理.Pool
。这将启动并行流程,这些流程作为一个整体不受GIL的约束。代码的其余部分保持不变。

对于聪明的“每个线程一个驱动程序”回答,我看到的一个潜在问题是,它忽略了任何“退出”驱动程序的机制,从而留下了进程悬而未决的可能性。我会作出以下更改:

  • 改为使用class
    Driver
    ,该类将装入驱动程序实例并将其存储在线程本地存储中,但也有一个析构函数,当删除线程本地存储时,该析构函数将退出驱动程序:
  • 创建驱动程序
    现在变成:
  • 最后,在不再使用
    ThreadPool
    实例后,但在其终止之前,添加以下行以删除线程本地存储,并强制调用
    驱动程序
    实例的析构函数(希望如此):

  • 任何时候多处理都会成为一个很好的机会来考虑从硒切换到无头铬。@ Qharr -有像PutpEdEnter和NavaReReS这样的节点库,比硒更适合这样的事情。Selenium更受欢迎,因为它已经存在很久了,但它有点像恐龙,而且大多适用于更简单的脚本。至少是这样。不,实际上我是建议从python切换到节点。@pguardiario谢谢。目前也在学习JS,所以这很方便。这是一个错误的网页抓取工具。改用开源Python包。它可以进行开箱即用的多处理,很容易编写新脚本并将数据存储在文件或数据库中。当我看到我有一个关于scrapy的解决方案时,我感到非常惊讶。我的文章标题还不够明确,我想实现什么目标?
    Selenium是一个错误的网页抓取工具。使用开源Scrapy
    @robots.txt不,你的问题是如何减少执行时间。正如我在前面的评论中所问的,请指定您尝试vv期望的结果,您可能会得到更好的答案。为什么Selenium是错误的工具?如何使用scrapy处理javascript?Selenium绝对不是从网站上抓取内容的错误工具,不管这些内容是否是动态的。然而,对于scrapy,有一个轻量级的工具也可以用来做这件事。现在你的解决方案看起来非常有希望@miraclixx。如果你愿意粘贴完整的脚本,我很乐意接受你的回答,因为我非常怀疑我自己能否实现它。这个答案绝对值得你投票。@robots.txt很高兴你喜欢它:-)链接到完整的脚本作为一个要点添加(这样这里的答案可以保持简短,即只指出你的脚本的差异)让我们等待,只要悬赏是开着的。已经接受了您的解决方案@miraclixx。为什么您决定使用threading.local而不是队列?
    线程本地数据是值特定于线程的数据(来源:python3 docs)——这就是我们希望在这里每个线程存储一次驱动程序实例的原因。队列没有帮助,因为我们不想在多个进程之间传递数据,事实上恰恰相反。
    
    (... 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)
      (...)
    
    (...)
    
    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".')
    
    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
    
    del threadLocal
    import gc
    gc.collect() # a little extra insurance