原理说明
在使用selenium时,每次启动生成一个driver对象都比较耗时间,大概需要4秒左右,因此可以创建一个全局的,在需要时直接调用,减少启动所耗时间。
打开新标签页后在selenium中并没有定位到新打开的标签页,仍在上一个,因此需要手动切换到新打开的标签页。
因为每次打开的新标签页都是在最后一个,因此只要获得所有标签页然后切换到最后一个即可实现
# 获取当前浏览器的所有标签页 handles = self.driver.window_handles # 定位到最后打开的标签页 driver.switch_to.window(handles[-1])
driver对象有driver.quit()和driver.close()方法,其中quit()是直接退出整个浏览器,close()是关闭当前标签页,但如果仅有一个标签页时则退出浏览器。
在被多次调用时,打开的网页如果不关闭则标签不多变多占用内存资源,但如果全部关闭了,浏览器又会直接退出。在这里我的解决办法是启动时打开一个空白页,然后每次打开新的页面都打开一个新的标签,操作完后关闭该标签。这样就保证了浏览器不被关闭,留着一个空白页面也不占用内存。
打开新标签页的方法也有多种,我选择的是执行js代码
driver.execute_script('window.open("http://www.baidu.com/")')
这里需要注意的是,如果打开的网页是本地文件,则不能一行直接打开新标签页并加载网页,因为js打开本地网页文件是不被允许的,这时需要打开一个空白页,然后切换到新打开的标签页,再使用driver.get()打开网页
open_new_tab_js = "window.open()" driver.execute_script(open_new_tab_js) # 获取当前浏览器的所有标签页 handles = self.driver.window_handles # 定位到最后打开的标签页 driver.switch_to.window(handles[-1]) # 打开模板网页文件 driver.get("file:///path".format()
因为我使用切换到最后一个标签页来切换到新的标签页的,这时如果线程p1刚打开新的标签页准备切换到这个标签页,这时由于系统调度被剥夺,并且这时又有一个线程p2,p2打开了一个新的标签页,然后被系统调度剥夺,轮到p1运行,p1执行下一行代码切换到最后一个标签页,进行后续操作。这时可以发现,p1后续操作的页面是p2打开的页面。
因此将driver对象当做一个临界区资源,从打开新标签页开始到操作结束都视作临界区,进入临界区则对driver加锁,执行完后释放锁。
因为我的方法都是async异步方法,所以使用的锁是asyncio.Lock(),如果是多线程则使用threading.Lock()。在进入临界区前调用acquire()方法加锁,执行完后release()方法释放锁
代码实现
这里代码我以网页截图为例
class HtmlCapture: def __init__(self): chrome_options = Options() # chrome_options.add_argument('--headless')# 不显示界面,调试的时候显示方便观察 chrome_options.add_argument('--disable-gpu') chrome_options.add_argument('--hide-scrollbars') # 隐藏滚动条 chrome_options.add_argument("start-maximized") # 最大化 path = 'C:/Program Files (x86)/Chromedriver/chromedriver.exe' print('正在创建全局HtmlCapture对象...') # 创建浏览器对象 self.driver = webdriver.Chrome(executable_path=path, options=chrome_options) # 锁 self.lock = asyncio.Lock() async def capture(self, save_file_path: str, template_name: str, js_code: str): # 加锁 await self.lock.acquire() try: png_path = "{}.png".format(save_file_path) jpg_path = "{}.jpg".format(save_file_path) # 打开一个新标签页,这里不能直接指定网址,因为用js打开本地html文件是chrome不允许的 open_new_tab_js = "window.open()" self.driver.execute_script(open_new_tab_js) # 获取当前浏览器的所有标签页 handles = self.driver.window_handles # 定位到最后打开的标签页 self.driver.switch_to.window(handles[-1]) # 打开模板网页文件 self.driver.get("file:///{}/awesome/data/{}.html".format(os.getcwd(), template_name)) # 延迟0.2s用于打开网页 await asyncio.sleep(0.2) self.driver.execute_script(js_code) # 注入js # 获取元素位置信息进行截图,并留白 table = self.driver.find_element_by_tag_name('table') left = table.location['x'] - 10 top = table.location['y'] - 10 right = left + table.size['width'] + 10 bottom = top + table.size['height'] + 30 # 截取全屏 self.driver.save_screenshot(png_path) self.driver.close() self.driver.switch_to.window(handles[0]) # 截取部分并转成jpg格式 Image.open("{}.png".format(save_file_path)).convert('RGB').crop((left, top, right, bottom)).save(jpg_path) finally: # 解除锁 self.lock.release()
文章评论