提问人:lollerskates 提问时间:11/17/2023 最后编辑:lollerskates 更新时间:11/17/2023 访问量:28
在 python 中将包装上下文管理器创建到装饰器中的正确方法?
Proper way to create a wrap context manager into a decorator in python?
问:
我有几个网页,我想使用硒抓取。我想自动化它并在远程机器上运行它。由于每个网站都不同,因此脚本需要不同的功能才能完成工作。我没有让每个脚本都具有相同的代码来启动虚拟显示和 webdriver,而是粗略地想到使用可以启动虚拟显示和 webdriver 的装饰器,如下所示:
def open_headless_browser(func: Callable) -> Callable:
disp = Display(visible=False, size=(100, 100))
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--dns-prefetch-disable")
def start(): -> None
with disp as display:
with webdriver.Chrome(options=self.options) as wd:
func()
return start
然后我可能会有我的脚本(实际执行抓取的脚本),如下所示:
@open_headless_browser
def scrape_abc(url_abc: str) -> None:
driver.get(url_abc)
driver.find_elements_by_xpath('abc')
@open_headless_browser
def scrape_xyz(url_xyz: str) -> None:
driver.get(url_xyz)
driver.find_elements_by_css('xyz')
但是,有几件事与我有关:
- 我的代码和函数中的代码有点尴尬,因为它不知道是什么(因为它是在装饰器中定义的)。
scrape_abc
scrape_xzy
driver
- 这甚至行得通吗?我是把事情搞得太复杂了,还是我只是错误地处理了这个想法?
- 这是pythonic吗
我在python3.10 selenium4.15 pyvirtualdisplay3.0上
编辑:经过一番思考,这种方法终究行不通。修饰的函数将无权访问修饰器中定义的 webdriver 对象
答:
0赞
RoadieRich
11/17/2023
#1
编辑:经过一番思考,这种方法终究行不通。修饰的函数将无权访问修饰器中定义的 webdriver 对象
当然可以,你只需要将参数作为参数传递给函数,如下所示:wd
def open_headless_browser(func: Callable) -> Callable:
disp = Display(visible=False, size=(100, 100))
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--dns-prefetch-disable")
def start(): -> None
with disp as display:
with webdriver.Chrome(options=options) as wd:
func(wd)
return start
然后,您的函数将如下所示:
@open_headless_browser
def scrape_abc(driver: webdriver.Chrome) -> None:
driver.get(url_abc)
driver.find_elements_by_xpath('abc')
@open_headless_browser
def scrape_abc(driver: webdriver.Chrome) -> None:
driver.get(url_xyz)
driver.find_elements_by_xpath('xyz')
如果希望能够传入 URL,还需要在包装函数中定义参数:
def open_headless_browser(func: Callable) -> Callable:
disp = Display(visible=False, size=(100, 100))
options = webdriver.ChromeOptions()
options.add_argument("--headless=new")
options.add_argument("--dns-prefetch-disable")
def start(url: str): -> None
with disp as display:
with webdriver.Chrome(options=options) as wd:
func(wd, url)
return start
@open_headless_browser
def scrape_abc(driver: webdriver.Chrome, url: str) -> None:
driver.get(url)
driver.find_elements_by_xpath('abc')
然后,请记住,尽管您将函数定义为具有两个参数,但您只使用一个参数调用它。
评论
0赞
lollerskates
11/17/2023
感谢您的回复。就最佳实践而言,将显示初始化和 chrome 选项移入或保留在原处更好?在功能方面,我认为两者都做同样的事情start
0赞
RoadieRich
11/17/2023
这实际上取决于您如何使用每个功能。如果您只使用每个装饰功能一次,则没关系。如果要将多个 URL 传递给函数,则需要考虑是否希望每个调用都有自己的 Web 驱动程序,或者是否对同一函数的所有调用都可以安全地共享。
评论