You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PyQt5 QtWebEngineView加载网页时Python崩溃、视频加载失败问题

运行环境信息
  • 操作系统:Windows 10
  • Python版本:3.9.6
  • PyQt5版本:5.15.6
  • PyQtWebEngine版本:5.15.5
  • 所有依赖包均通过pip工具安装
故障表现

访问Twitter站点时存在两类异常:

  1. 页面内图片可正常加载,但视频无法加载,视频区域显示错误提示
  2. 仅能滚动浏览少量帖子,就会触发Python进程无任何报错直接崩溃:并非浏览器组件自行关闭,而是整个Python进程直接退出。最初在VS Code终端运行时未捕获到任何错误输出,切换到CMD终端运行后捕获到如下错误日志:
[1436:6100:0617/142809.070:ERROR:cache_util_win.cc(21)] Unable to move the cache: AccÞs refusÚ. (0x5)
[1436:6100:0617/142809.084:ERROR:cache_util.cc(139)] Unable to move cache folder C:\Users\fabie\AppData\Local\python\QtWebEngine\Default\GPUCache to C:\Users\fabie\AppData\Local\python\QtWebEngine\Default\old_GPUCache_000
[1436:6100:0617/142809.096:ERROR:disk_cache.cc(184)] Unable to create cache
[1436:6100:0617/142809.096:ERROR:shader_disk_cache.cc(606)] Shader Cache Creation failed: -2
[1436:6100:0617/142812.075:ERROR:service_worker_storage.cc(1575)] Failed to delete the database: Database IO error
[1436:1208:0617/142837.652:ERROR:leveldb_factory.cc(91)] Failed to open LevelDB database from C:\Users\fabie\AppData\Local\python\QtWebEngine\Default\IndexedDB\https_twitter.com_0.indexeddb.leveldb,IO error: C:\Users\fabie\AppData\Local\python\QtWebEngine\Default\IndexedDB\https_twitter.com_0.indexeddb.leveldb/LOCK: File currently in use. (ChromeMethodBFE: 15::LockFile::2)
[1436:1208:0617/142839.674:ERROR:leveldb_factory.cc(91)] Failed to open LevelDB database from C:\Users\fabie\AppData\Local\python\QtWebEngine\Default\IndexedDB\https_twitter.com_0.indexeddb.leveldb,IO error: C:\Users\fabie\AppData\Local\python\QtWebEngine\Default\IndexedDB\https_twitter.com_0.indexeddb.leveldb/LOCK: File currently in use. (ChromeMethodBFE: 15::LockFile::2)

视频加载错误效果如下:
视频加载错误示例

初步排查线索

故障疑似与本地存储/localForage相关:项目中另外运行了一个QWebEngineView实例,用于加载TradingView提供的高级行情图表组件,该实例抛出如下JS警告/错误,与前述故障存在关联:

js: 2022-06-29T15:25:55.559Z:Common.LocalForage:IndexedDB is NOT available
js: 2022-06-29T15:25:55.816Z:ChartApi.Core:Cannot get studies_metadata, reason: Error: No available storage method found.
js: 2022-06-29T15:25:56.679Z:ChartApi.Core:Cannot update studies_metadata cache, reason: Error: No available storage method found.
复现代码
import sys
from PyQt5.QtWidgets import *
from PyQt5 import QtCore, QtGui, QtWebEngineWidgets


class BrowserWindow(QMainWindow):

    def __init__(self, *args, **kwargs):
        super(BrowserWindow, self).__init__(*args, **kwargs)

        # ADD WINDOW ELEMENTS
        # ADD TAB WIGDETS TO DISPLAY WEB TABS
        self.tabs = QTabWidget()
        self.tabs.setDocumentMode(True)
        self.tabs.setTabsClosable(True)
        self.setCentralWidget(self.tabs)

        # ADD DOUBLE CLICK EVENT LISTENER
        self.tabs.tabBarDoubleClicked.connect(self.tab_open_doubleclick)
        # ADD TAB CLOSE EVENT LISTENER
        self.tabs.tabCloseRequested.connect(self.close_current_tab)
        # ADD ACTIVE TAB CHANGE EVENT LISTENER
        self.tabs.currentChanged.connect(self.current_tab_changed)

        # ADD NAVIGATION TOOLBAR
        navtb = QToolBar("Navigation")
        navtb.setIconSize(QtCore.QSize(16, 16))
        self.addToolBar(navtb)

        # ADD BUTTONS TO NAVIGATION TOOLBAR
        # PREVIOUS WEB PAGE BUTTON
        back_btn = QAction(QtGui.QIcon('arrow back.png'), "Back", self)
        back_btn.setStatusTip("Back to previous page")
        navtb.addAction(back_btn)
        # NAVIGATE TO PREVIOUS PAGE
        back_btn.triggered.connect(lambda: self.tabs.currentWidget().back())

        # NEXT WEB PAGE BUTTON
        next_btn = QAction(QtGui.QIcon('arrow next.png'), "Forward", self)
        next_btn.setStatusTip("Forward to next page")
        navtb.addAction(next_btn)
        # NAVIGATE TO NEXT WEB PAGE
        next_btn.triggered.connect(lambda: self.tabs.currentWidget().forward())

        # REFRESH WEB PAGE BUTTON
        reload_btn = QAction(QtGui.QIcon('refresh.png'), "Reload", self)
        reload_btn.setStatusTip("Reload page")
        navtb.addAction(reload_btn)
        # RELOAD WEB PAGE
        reload_btn.triggered.connect(lambda: self.tabs.currentWidget().reload())

        # HOME PAGE BUTTON
        home_btn = QAction(QtGui.QIcon('home.png'), "Home", self)
        home_btn.setStatusTip("Go home")
        navtb.addAction(home_btn)
        # NAVIGATE TO DEFAULT HOME PAGE
        home_btn.triggered.connect(self.navigate_home)

        # ADD SEPARATOR TO NAVIGATION BUTTONS
        navtb.addSeparator()

        # ADD LINE EDIT TO SHOW AND EDIT URLS
        self.urlbar = QLineEdit()
        navtb.addWidget(self.urlbar)
        # LOAD URL WHEN ENTER BUTTON IS PRESSED
        self.urlbar.returnPressed.connect(self.navigate_to_url)
        self.urlbar.mousePressEvent = lambda _ : self.urlbar.selectAll()

        # ADD STYLESHEET TO CUSTOMIZE YOUR WINDOWS
        self.setStyleSheet("""QWidget{
        background-color: white;
        color: rgb(48, 48, 48);
        }
        QTabWidget::pane { /* The tab widget frame */
            border-top: 2px solid rgb(255, 255, 255);
            position: absolute;

            color: rgb(90, 90, 90);
            padding: 5px;
        }

        QTabWidget::tab-bar {
            alignment: left;
        }

        /* Style the tab using the tab sub-control. Note that
            it reads QTabBar _not_ QTabWidget */
        QLabel, QToolButton {
            background: rgb(255, 255, 255);
            border: 2px solid rgb(255, 255, 255);
            /*border-bottom-color: #C2C7CB; /* same as the pane color */
            border-radius: 3px;
            min-width: 8ex;
            padding: 5px;
            margin-right: 2px;
            color: rgb(50, 50, 50);
        }

        QLabel:hover, QToolButton::hover {
            background: white;
            border: 2px solid rgb(0, 36, 36);
            background-color: white;
        }

        QTabBar::tab{
            border-radius: 3px;
            min-width: 8ex;
            padding: 5px;
            margin-right: 2px;
            color: rgb(50, 50, 50);
            background:#e0e0e0;
        }

        QTabBar::tab:selected, QTabBar::tab:hover:selected{
            background:white;
            background-color:white;
            border-bottom-width:0px;
            border-bottom-left-radius:0px;
            border-bottom-right-radius:0px;
        }
        
        QTabBar::tab:hover {
            background:#ededed;
            background-color:#ededed;
            border-color:#ededed;
        }

        QLineEdit {
            border: 2px solid rgb(0, 36, 36);
            border-radius: 3px;
            padding: 5px;
            background-color: white;
            color: rgb(50, 50, 50);
        }
        QLineEdit:hover {
            border: 2px solid rgb(0, 66, 124);
        }
        QLineEdit:focus{
            border: 2px solid rgb(0, 136, 255);
        }
        QPushButton{
            background: rgb(49, 49, 49);
            border: 2px solid rgb(0, 36, 36);
            background-color: rgb(0, 36, 36);
            padding: 5px;
            border-radius: 10px;
        }""")

        #label = Homepage
        self.add_new_tab(QtCore.QUrl('https://duckduckgo.com/'), 'Homepage')

        # SHOW MAIN WINDOW
        self.show()

    # ############################################
    # FUNCTIONS
    ##############################################
    # ADD NEW WEB TAB
    def add_new_tab(self, qurl=None, label="Blank"):
        # Check if url value is blank
        if qurl is None:
            qurl = QtCore.QUrl('')#pass empty string to url

        # Load the passed url
        browser = QtWebEngineWidgets.QWebEngineView()
        browser.setUrl(qurl)

        # ADD THE WEB PAGE TAB
        i = self.tabs.addTab(browser, label)
        self.tabs.setCurrentIndex(i)

        # ADD BROWSER EVENT LISTENERS
        # On URL change
        browser.urlChanged.connect(lambda qurl, browser=browser:
                                self.update_urlbar(qurl, browser))
        # On loadfinished
        browser.loadFinished.connect(lambda _, i=i, browser=browser:
                                    self.tabs.setTabText(i, browser.page().title()))

    # ADD NEW TAB ON DOUBLE CLICK ON TABS
    def tab_open_doubleclick(self, i):
        if i == -1:  # No tab under the click
            self.add_new_tab()

    # CLOSE TABS 
    def close_current_tab(self, i):
        if self.tabs.count() < 2: #Only close if there is more than one tab open
            return

        self.tabs.removeTab(i)

    # UPDATE URL TEXT WHEN ACTIVE TAB IS CHANGED
    def update_urlbar(self, q, browser=None):
        #q = QURL
        if browser != self.tabs.currentWidget():
            # If this signal is not from the current tab, ignore
            return

        self.urlbar.setText(q.toString())
        self.urlbar.setCursorPosition(0)

    # ACTIVE TAB CHANGE ACTIONS
    def current_tab_changed(self, i):
        # i = tab index
        # GET CURRENT TAB URL
        qurl = self.tabs.currentWidget().url()
        # UPDATE URL TEXT
        self.update_urlbar(qurl, self.tabs.currentWidget())
        # UPDATE WINDOWS TITTLE
        self.update_title(self.tabs.currentWidget())

    # UPDATE WINDOWS TITTLE
    def update_title(self, browser):
        if browser != self.tabs.currentWidget():
            # If this signal is not from the current ACTIVE tab, ignore
            return

        title = self.tabs.currentWidget().page().title()
        self.setWindowTitle(title)

    # NAVIGATE TO PASSED URL
    def navigate_to_url(self):  # Does not receive the Url
        # GET URL TEXT
        q = QtCore.QUrl(self.urlbar.text())
        if q.scheme() == "":
            q = QtCore.QUrl('https://www.google.com/search?q='+self.urlbar.text().replace(' ', '+'))

        self.tabs.currentWidget().setUrl(q)

    # NAVIGATE TO DEFAULT HOME PAGE
    def navigate_home(self):
        self.tabs.currentWidget().setUrl(QtCore.QUrl("https://duckduckgo.com/"))

app = QApplication(sys.argv)
w = BrowserWindow()
app.exec_()
根因定位与解决方案

核心根因

  1. 多实例存储锁冲突:所有默认创建的QWebEngineView实例会共享同一个全局Default配置文件,共用同一份磁盘缓存、IndexedDB、GPUCache存储路径。同时运行浏览器标签页和TradingView组件两个QWebEngineView实例时,多个渲染进程并发读写同一份存储文件,触发LevelDB文件锁占用错误,直接导致IndexedDB不可用,页面本地存储读写失败。
  2. GPU缓存权限异常:PyQtWebEngine 5.15.x在Windows下默认存储路径位于用户AppData目录,普通权限下偶尔会出现缓存目录移动、创建失败的问题,GPU Shader缓存初始化失败会直接导致硬件视频解码异常,出现视频加载失败。
  3. 版本不匹配:当前PyQt5版本为5.15.6,PyQtWebEngine版本为5.15.5,小版本不一致会提升渲染进程崩溃概率。
  4. 存储文件锁冲突、缓存初始化失败会触发QtWebEngine渲染进程的未捕获异常,直接导致整个Python进程无报错退出。

修复方案

按优先级执行即可:

  1. 对齐依赖版本:执行命令将PyQtWebEngine版本升级到和PyQt5一致的5.15.6:
    pip install --upgrade PyQtWebEngine==5.15.6
    
  2. 清理损坏缓存:手动删除目录C:\Users\fabie\AppData\Local\python\QtWebEngine,重启程序后QtWebEngine会重建全新的缓存目录,可临时解决缓存损坏导致的启动报错。
  3. 为每个QWebEngine实例配置独立存储Profile:修改add_new_tab方法以及创建TradingView实例的代码,不要使用默认共享的Default Profile,为每个实例分配独立的存储路径,彻底解决文件锁冲突。参考修改代码:
    def add_new_tab(self, qurl=None, label="Blank"):
        if qurl is None:
            qurl = QtCore.QUrl('')
        # 为每个标签页创建独立Profile,避免锁冲突
        profile = QtWebEngineWidgets.QWebEngineProfile(f"browser_tab_{self.tabs.count()}", self)
        # 可自定义缓存路径到项目目录,避免系统目录权限问题
        # profile.setPersistentStoragePath("./cache/tab_"+str(self.tabs.count()))
        # profile.setCachePath("./cache/tab_"+str(self.tabs.count()))
        browser = QtWebEngineWidgets.QWebEngineView()
        page = QtWebEngineWidgets.QWebEnginePage(profile, browser)
        browser.setPage(page)
        browser.setUrl(qurl)
        # 后续原有逻辑不变
        i = self.tabs.addTab(browser, label)
        self.tabs.setCurrentIndex(i)
        browser.urlChanged.connect(lambda qurl, browser=browser:
                                self.update_urlbar(qurl, browser))
        browser.loadFinished.connect(lambda _, i=i, browser=browser:
                                    self.tabs.setTabText(i, browser.page().title()))
    
    TradingView的QWebEngineView实例也需要用独立的Profile创建,不要用默认配置。
  4. 无持久化需求可直接使用无痕模式:如果不需要保留登录状态、浏览记录、本地缓存,可以创建无持久化的离屏Profile,完全不读写磁盘存储,从根源上避免缓存和锁问题:
    # 不传存储名即为无痕模式,不会写入磁盘数据
    profile = QtWebEngineWidgets.QWebEngineProfile(self)
    

内容的提问来源于stack exchange,提问作者3V's

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 08:12:22