【问题标题】:Scraping multiple Web Pages at once with selenium使用 selenium 一次抓取多个网页
【发布时间】:2021-03-10 09:40:24
【问题描述】:

我正在使用selenium 和 Python 做一个大项目。我必须一个一个地浏览 320.000 个网页 (320K) 并抓取详细信息,然后睡一秒钟继续前进。

如下:

links = ["https://www.thissite.com/page=1","https://www.thissite.com/page=2", "https://www.thissite.com/page=3"]

for link in links:
    browser.get(link )
    scrapedinfo = browser.find_elements_by_xpath("*//div/productprice").text
    open("file.csv","a+").write(scrapedinfo)
    time.sleep(1)

最大的问题:太慢了!

有了这个脚本需要几天或者可能几周

  • 有没有办法提高速度?例如,通过访问多个 同时链接并一次抓取所有链接?

我花了几个小时在 google 和 Stackoverflow 上寻找答案,但只找到了 multiprocessing

但是,我无法在我的脚本中应用它。

【问题讨论】:

  • 使用 requests + lxml/BeautifulSoup 代替 Selenium 会减少抓取时间
  • @JaSON 该网站使用 javascript,因此要求工作
  • 您可以删除睡眠时间并将其替换为implicit wait? this 帖子中给出了另一个选项,使用无头铬或多处理。

标签: python python-3.x selenium selenium-webdriver selenium-chromedriver


【解决方案1】:

您可以使用并行执行。为例如十个将使用相同代码的 TC 中的站点列表,只是方法名称会不同(method1、method2、method3、...)。你会提高速度。浏览器的数量取决于您的硬件性能。 查看更多关于https://www.guru99.com/sessions-parallel-run-and-dependency-in-selenium.html

主要是使用Test NG并编辑.xml文件并设置要使用的线程数。像这样:

<suite name="TestSuite" thread-count="10" parallel="methods" >

【讨论】:

【解决方案2】:

线程方法

  • 您应该threading.Thread开始,它会给您带来显着的性能提升 (explained here)。线程也比进程轻。您可以使用futures.ThreadPoolExecutor 与每个线程使用自己的网络驱动程序。还可以考虑为您的网络驱动程序添加headless 选项。下面是使用 chrome-webdriver 的示例:
from concurrent import futures

def selenium_work(url):
    chromeOptions = webdriver.ChromeOptions()
    chromeOptions.add_argument("--headless") 
    driver = webdriver.Chrome(options=chromeOptions)  
    #<actual work that needs to be done be selenium>

# default number of threads is optimized for cpu cores 
# but you can set with `max_workers` like `futures.ThreadPoolExecutor(max_workers=...)`
with futures.ThreadPoolExecutor() as executor:     
    # store the url for each thread as a dict, so we can know which thread fails
    future_results = { url : executor.submit(selenium_work, links) for url in links }
    for url, future in future_results.items(): 
        try:        
           future.result() # can use `timeout` to wait max seconds for each thread  
      except Exception as exc: # can give a exception in some thread
           print('url {:0} generated an exception: {:1}'.format(url, exc))

  • 考虑同时存储使用threading.local() 在每个线程上初始化的chrome-driver 实例。 From here 他们报告了合理的性能改进。

  • 考虑在 selenium 的页面上直接使用 BeautifulSoup 是否可以提供其他一些加速。这是一个非常快速且稳定的软件包。例如 driver.get(url) ... soup = BeautifulSoup(driver.page_source,"lxml") ... result = soup.find('a')

其他方法

  • 虽然我个人没有看到使用concurrent.futures.ProcessPoolExecutor() 的好处,但您可以尝试一下。事实上,它比我在 Windows 上的实验中的线程慢。同样在 Windows 上,您对 python Process许多限制。

  • 考虑使用arsenic 构建在asyncio 上的异步网络驱动程序 客户端是否可以满足您的用例。 这听起来很有希望,尽管有很多限制。

  • 考虑 Requests-Html 是否可以解决您的 javascript 加载问题。因为它声称​​完全支持 JavaScript! 在这种情况下,您可以在标准数据 scraping 方法上将它与 BeautifulSoup 一起使用。

【讨论】:

    【解决方案3】:

    如果您不是针对机器人抓取过于安全的网站,最好使用请求,它将您的时间从几天减少到几个小时,并通过多处理实现多线程。步骤太长,无法完成,这里只是一些想法:

    def threader_run(data):
        futures = []
        with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:
            for i in data:
                futures.append(executor.submit(scrapper,i))
            for future in concurrent.futures.as_completed(futures):
                print(future.result())
    
    
    
    data = {}
    data['process1'] = []
    data['process2'] = []
    data['process3'] = []
    
    if __name__ == "__main__":
        
        for x in data:
         
            jobs = []
            p = Process(target=threader_run,args(data[x],))
            jobs.append(p)
            p.start()
            print(f'Started - {x}')
    

    基本上,这样做是首先编译所有链接,然后将它们拆分为 3 个数组以同时运行 3 个进程(您可以运行更多进程,具体取决于您的 cpu 内核以及这些作业的数据密集程度)。之后,根据您的项目大小,进一步拆分这些数组可能会超过 10 个甚至 100 个。这将运行最多有 8 个工作人员的线程池,然后它将运行您的最终功能。

    这里有 3 个进程和 8 个工作人员,您正在查看 24 倍的速度提升。 但是,使用请求库是必要的 如果您为此使用 selenium ,普通电脑/笔记本电脑会死机。因为这意味着 24 个浏览器同时运行。

    【讨论】:

      猜你喜欢
      • 2018-05-31
      • 1970-01-01
      • 2019-04-06
      • 2019-06-23
      • 2021-12-17
      • 1970-01-01
      相关资源
      最近更新 更多