[英]Python Web Scraping with Selenium | Parallel execution (Multi-threading)
[英]Python parallel execution with selenium
我对使用 selenium 在 python 中的并行执行感到困惑。 似乎有几种方法可以 go 关于它,但有些似乎已经过时了。
有一个名为python-wd-parallel
的 python 模块似乎有一些功能可以做到这一点,但它是从 2013 年开始的,现在这还有用吗? 我也找到了这个例子。
有concurrent.futures
,这似乎更新了很多,但实现起来并不容易。 任何人都有在 selenium 中并行执行的工作示例?
还有只使用线程和执行程序来完成工作,但我觉得这会更慢,因为它没有使用所有内核并且仍然以串行形式运行。
使用 selenium 进行并行执行的最新方法是什么?
使用joblib 的 Parallel模块来做到这一点,它是一个很好的并行执行库。
假设我们有一个名为urls
的 url 列表,我们想并行截取每个urls
的屏幕截图
首先让我们导入必要的库
from selenium import webdriver
from joblib import Parallel, delayed
现在让我们定义一个将屏幕截图作为 base64 的函数
def take_screenshot(url):
phantom = webdriver.PhantomJS('/path/to/phantomjs')
phantom.get(url)
screenshot = phantom.get_screenshot_as_base64()
phantom.close()
return screenshot
现在要并行执行,你要做的是
screenshots = Parallel(n_jobs=-1)(delayed(take_screenshot)(url) for url in urls)
当这一行完成执行时,您将在screenshots
中看到来自所有运行进程的所有数据。
关于平行的说明
Parallel(n_jobs=-1)
意味着使用你可以使用的所有资源joblib
delayed(function)(input)
是joblib
为您尝试并行运行的函数创建输入的方式更多信息可以在joblib
文档中找到
通常最好使用SeleniumBase一个基于 selenium 和 pytest 的 Python 测试框架。 它非常完整地支持性能提升、并行线程等等的一切。 如果那不是你的情况......继续阅读。
threads
和processes
都将大大加快selenium 代码的速度。 下面给出了简短的例子。 selenium 工作由返回页面标题的selenium_title
function 完成。 这不处理每个线程/进程执行期间发生的异常。 对于那个看起来很长的答案-处理异常。
from selenium import webdriver
from concurrent import futures
def selenium_title(url):
wdriver = webdriver.Chrome() # chrome webdriver
wdriver.get(url)
title = wdriver.title
wdriver.quit()
return title
links = ["https://www.amazon.com", "https://www.google.com"]
with futures.ThreadPoolExecutor() as executor: # default/optimized number of threads
titles = list(executor.map(selenium_title, links))
concurrent.futures.ProcessPoolExecutor
。 只需要将上面代码中的ThreadPoolExecuter
替换为ProcessPoolExecutor
即可。 它们都源自Executor
基础 class。 此外,您必须保护main ,如下所示。if __name__ == '__main__':
with futures.ProcessPoolExecutor() as executor: # default/optimized number of processes
titles = list(executor.map(selenium_title, links))
Threads
有效? 由于 Python GIL 和即使线程将被上下文切换,即使是坚韧的 Python 对线程也有限制。 Selenium 的实现细节将带来性能提升。 Selenium 通过发送诸如POST
、 GET
( HTTP requests
)之类的命令来工作。 这些被发送到浏览器驱动程序服务器。 因此,您可能已经知道 I/O 绑定任务( HTTP requests
)释放 GIL,因此性能提升。
我们可以对上面的示例进行一些小修改,以处理产生的线程上的Exceptions
。 我们不使用executor.map
,而是使用executor.submit
。 这将返回包装在Future
实例上的标题。
要访问返回的标题,我们可以使用future_titles[index].result
where index size len(links)
,或者简单地使用 a for
like bellow。
with futures.ThreadPoolExecutor() as executor:
future_titles = [ executor.submit(selenium_title, link) for link in links ]
for future_title, link in zip(future_titles, links):
try:
title = future_title.result() # can use `timeout` to wait max seconds for each thread
except Exception as exc: # this thread migh have had an exception
print('url {:0} generated an exception: {:1}'.format(link, exc))
请注意,除了对future_titles
进行迭代之外,我们还会对links
进行迭代,因此如果某个线程中出现Exception
,我们知道哪个url(link)
对此负责。
futures.Future
class 很酷,因为它们可以让您控制从每个线程收到的结果。 就像它是否正确完成或有异常等等,更多关于这里。
同样重要的是,如果您不关心线程返回项目的顺序, futures.as_completed
会更好。 但由于控制异常的语法有点难看,我在这里省略了它。
首先为什么我一直使用线程来加速我的 selenium 代码:
我创建了一个项目来执行此操作,它重用 webdriver 实例以获得更好的性能:
对于并行运行 Python 测试,您可以考虑使用pytest-xdist
为您处理多个进程: https://github.com/pytest-dev/pytest-xdist 。 这是pytest
框架的插件。
And for running Python Selenium tests in parallel with pytest
, there's a framework that may simplify the Selenium test multithreading for you, SeleniumBase
: https://github.com/seleniumbase/SeleniumBase . It functions as a pytest plugin so you can use the pytest multi-threading args provided by pytest-xdist
, and run all your Selenium Python tests multithreaded as needed. 例如: pytest -n 4
用于 4 个并行线程。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.