_blank, _self, _parent, _top, _new

本文详细解释了HTML中链接的不同目标属性,包括_blank、_self、_parent和_top的作用及使用场景。介绍了_blank如何创建新窗口或标签页,_new属性的行为特性及其与_new的区别,并讨论了这些属性在不同HTML版本中的变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

_blank opens a new windows

_self opens within the same window

_parent is used with a frame, it will open within the inner frame

_top is also used with a frame, it will open using the entire window, not within the frame.


Using target="_blank" will instruct the browser to create a new browser tab or window when the user clicks on the link.

Using target="_new" is technically invalid according to the specifications, but as far as I know every browser will behave the same way:

  • it will search for a tab or window with the context name "_new"
  • if a "_new" tab/window is found, then the URL is loaded into it
  • if it's not found, a new tab/window is created with the context name "_new", and the URL loaded into it

Note target="_new" will behave exactly the same as target="new", and the latter is valid HTML while the former is invalid HTML.

Adding some confusion to this, in HTML4 the target attribute was deprecated. In HTML5 this decision was reversed, and it is an official part of the spec once again. All browsers support target no matter what version of HTML you are using, but some validators will flag the use as deprecated if your doctype is HTML4.

The target attribute of a link forces the browser to open the destination page in a new browser window. Using _blank as a target value will spawn a new window every time while using _new will only spawn one new window and every link clicked with a target value of _new will replace the page loaded in the previously spawned window

http://www.vodahost.com/vodatalk/adding-elements-website/22563-_blank-_self-_parent-_top.html

http://stackoverflow.com/questions/4964130/target-blank-vs-target-new


import sys import json import os import warnings from datetime import datetime from PyQt5.QtCore import QUrl, Qt, QDateTime from PyQt5.QtWidgets import ( QApplication, QMainWindow, QLineEdit, QPushButton, QListWidget, QTabWidget, QVBoxLayout, QWidget, QHBoxLayout, QAction, QToolBar, QStatusBar, QProgressBar, QInputDialog, QFileDialog, QMessageBox ) from PyQt5.QtWebEngineWidgets import QWebEngineView, QWebEngineProfile, QWebEnginePage, QWebEngineSettings from PyQt5.QtNetwork import QNetworkProxy, QNetworkCookie # 修复代理设置导入问题 # 忽略弃用警告 warnings.filterwarnings("ignore", category=DeprecationWarning) class WebEnginePage(QWebEnginePage): def __init__(self, profile=None, parent=None): """修复参数传递问题:支持带profile和不带profile两种初始化方式""" if profile: super().__init__(profile, parent) # 使用profile初始化 else: super().__init__(parent) # 标准初始化 self.parent_window = parent.parent_window if parent and hasattr(parent, 'parent_window') else None def acceptNavigationRequest(self, url, _type, isMainFrame): # 检测视频流协议并调用播放器 if url.scheme() in ['rtmp', 'http-flv', 'ws-flv']: if self.parent_window: self.parent_window.play_with_jessibuca(url.toString()) return False return super().acceptNavigationRequest(url, _type, isMainFrame) def createWindow(self, type): # 在新标签页打开链接 if type == QWebEnginePage.WebBrowserTab: if self.parent_window: new_tab = self.parent_window.add_browser_tab("加载中...") return new_tab.page return super().createWindow(type) def javaScriptConsoleMessage(self, level, message, lineNumber, sourceID): """修复:正确映射JavaScript控制台日志级别""" # 创建日志级别映射字典 level_map = { QWebEnginePage.InfoMessageLevel: "INFO", QWebEnginePage.WarningMessageLevel: "WARNING", QWebEnginePage.ErrorMessageLevel: "ERROR" } # 获取可读的日志级别名称 level_str = level_map.get(level, "UNKNOWN") log_msg = f"[JS {level_str}] {sourceID}:{lineNumber} - {message}" if self.parent_window: self.parent_window.status_bar.showMessage(log_msg, 5000) print(log_msg) # 同时输出到控制台 class WebEngineView(QWebEngineView): def __init__(self, parent=None): super().__init__(parent) self.parent_window = parent # 修复:正确传递profile参数 self.page = WebEnginePage(profile=self.parent_window.profile, parent=self) self.setPage(self.page) self.page.loadFinished.connect(self.inject_jessibuca) def inject_jessibuca(self): self.page.runJavaScript(""" if (typeof window.Jessibuca === 'undefined') { const script = document.createElement('script'); script.src = 'https://cdn.jsdelivr.net/npm/jessibuca@latest/dist/jessibuca.js'; script.onerror = () => console.error('Jessibuca加载失败'); document.head.appendChild(script); const container = document.createElement('div'); container.id = 'jessibuca-container'; container.style.position = 'fixed'; container.style.zIndex = '99999'; container.style.top = '0'; container.style.left = '0'; container.style.width = '100%'; container.style.height = '100%'; container.style.backgroundColor = 'black'; container.style.display = 'none'; document.body.appendChild(container); } """) def createWindow(self, type): # 创建新标签页 if type == QWebEnginePage.WebBrowserTab: new_tab = WebEngineView(self.parent_window) # 修复:直接返回page属性而非调用page() return new_tab.page return super().createWindow(type) class MainWindow(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle("B站多账号浏览器") self.setMinimumSize(1200, 800) # 初始化数据存储 self.account_db = {} self.cookies_db = [] # 创建持久化Cookie配置 self.cookie_storage_path = "cookies_storage" os.makedirs(self.cookie_storage_path, exist_ok=True) self.profile = QWebEngineProfile("BiliCookieProfile", self) self.profile.setPersistentCookiesPolicy(QWebEngineProfile.ForcePersistentCookies) self.profile.setPersistentStoragePath(self.cookie_storage_path) # 关键性能优化1: 全局禁用网络代理[8](@ref) QNetworkProxy.setApplicationProxy(QNetworkProxy(QNetworkProxy.NoProxy)) # 关键性能优化2: 启用HTTP内存缓存[9](@ref) self.profile.setHttpCacheType(QWebEngineProfile.MemoryHttpCache) # 启用HTML5支持的设置 profile_settings = self.profile.settings() profile_settings.setAttribute(QWebEngineSettings.PlaybackRequiresUserGesture, False) profile_settings.setAttribute(QWebEngineSettings.FullScreenSupportEnabled, True) profile_settings.setAttribute(QWebEngineSettings.WebGLEnabled, True) profile_settings.setAttribute(QWebEngineSettings.Accelerated2dCanvasEnabled, True) profile_settings.setAttribute(QWebEngineSettings.PluginsEnabled, True) profile_settings.setAttribute(QWebEngineSettings.AllowRunningInsecureContent, True) # 主界面布局 central_widget = QWidget() self.setCentralWidget(central_widget) main_layout = QVBoxLayout(central_widget) # 导航工具栏 navbar = QToolBar("导航栏") navbar.setMovable(False) # 禁止工具栏拖动提升性能 self.addToolBar(navbar) back_btn = QAction("←", self) back_btn.triggered.connect(self.navigate_back) navbar.addAction(back_btn) forward_btn = QAction("→", self) forward_btn.triggered.connect(self.navigate_forward) navbar.addAction(forward_btn) reload_btn = QAction("↻", self) reload_btn.triggered.connect(self.reload_page) navbar.addAction(reload_btn) home_btn = QAction("🏠", self) home_btn.triggered.connect(self.navigate_home) navbar.addAction(home_btn) navbar.addSeparator() self.url_bar = QLineEdit("https://www.bilibili.com") self.url_bar.setPlaceholderText("输入网址或搜索内容...") self.url_bar.returnPressed.connect(self.load_url) navbar.addWidget(self.url_bar) # 浏览器标签页 self.browser_tabs = QTabWidget() self.browser_tabs.setTabsClosable(True) self.browser_tabs.tabCloseRequested.connect(self.close_tab) self.browser_tabs.currentChanged.connect(self.update_url_bar) # 关键性能优化3: 启用标签页滚动 self.browser_tabs.setUsesScrollButtons(True) main_layout.addWidget(self.browser_tabs, 8) # 添加初始标签页 self.add_browser_tab("首页", "https://www.bilibili.com") # 管理面板 self.tabs = QTabWidget() self.cookie_list = QListWidget() self.tabs.addTab(self.cookie_list, "Cookie列表") self.account_list = QListWidget() self.tabs.addTab(self.account_list, "账号切换") self.account_list.itemClicked.connect(self.switch_account) main_layout.addWidget(self.tabs, 2) # 功能按钮 btn_layout = QHBoxLayout() self.import_btn = QPushButton("导入Cookie") self.import_btn.clicked.connect(self.import_cookies) self.save_btn = QPushButton("保存账号") self.save_btn.clicked.connect(self.save_account) self.export_btn = QPushButton("导出Cookie") self.export_btn.clicked.connect(self.export_cookies) self.new_tab_btn = QPushButton("新建标签页") self.new_tab_btn.clicked.connect(self.create_new_tab) for btn in [self.import_btn, self.save_btn, self.export_btn, self.new_tab_btn]: btn_layout.addWidget(btn) main_layout.addLayout(btn_layout) # 状态栏 self.status_bar = QStatusBar() self.setStatusBar(self.status_bar) self.progress_bar = QProgressBar() self.progress_bar.setMaximum(100) self.progress_bar.setVisible(False) self.progress_bar.setFixedWidth(200) self.status_bar.addPermanentWidget(self.progress_bar) # 加载数据 self.load_accounts() def add_browser_tab(self, title, url=None): browser = WebEngineView(self) browser.titleChanged.connect(lambda title, b=browser: self.update_tab_title(b, title)) browser.loadProgress.connect(self.update_progress) browser.loadFinished.connect(self.on_load_finished) index = self.browser_tabs.addTab(browser, title) self.browser_tabs.setCurrentIndex(index) # 关键性能优化4: 延迟加载提升启动速度 if url: QApplication.processEvents() # 确保UI更新 browser.load(QUrl(url)) return browser def update_tab_title(self, browser, title): index = self.browser_tabs.indexOf(browser) if index != -1: self.browser_tabs.setTabText(index, title[:15] + "..." if len(title) > 15 else title) def update_progress(self, progress): self.progress_bar.setVisible(progress < 100) self.progress_bar.setValue(progress) def close_tab(self, index): """修复资源释放问题:移除多余的括号""" if self.browser_tabs.count() > 1: widget = self.browser_tabs.widget(index) # 关键修复:直接访问page属性而非调用page() page = widget.page if page: # 关键优化: 正确的资源释放顺序 profile = page.profile() profile.cookieStore().deleteAllCookies() # 先删除页面再删除视图 page.deleteLater() widget.deleteLater() self.browser_tabs.removeTab(index) else: self.create_new_tab() def create_new_tab(self): self.add_browser_tab("新标签页", "about:blank") def navigate_back(self): current_browser = self.browser_tabs.currentWidget() if current_browser: current_browser.back() def navigate_forward(self): current_browser = self.browser_tabs.currentWidget() if current_browser: current_browser.forward() def reload_page(self): current_browser = self.browser_tabs.currentWidget() if current_browser: current_browser.reload() def navigate_home(self): self.load_url("https://www.bilibili.com") def on_load_finished(self, success): browser = self.sender() if browser and self.browser_tabs.currentWidget() == browser: current_url = browser.url().toString() self.url_bar.setText(current_url) self.status_bar.showMessage("页面加载完成" if success else "页面加载失败", 2000) # 关键优化: 登录页面自动处理 if "login" in current_url or "passport" in current_url: self.status_bar.showMessage("检测到登录页面,请完成登录", 5000) self.auto_handle_login_page(browser) def auto_handle_login_page(self, browser): """自动处理登录页面的JavaScript""" browser.page.runJavaScript(""" // 尝试自动填充已知的登录表单 const loginForm = document.querySelector('form[action*="login"]'); if (loginForm) { // 尝试填充测试账号 const usernameInput = loginForm.querySelector('input[name="username"], input[name="user"]'); const passwordInput = loginForm.querySelector('input[name="password"]'); if (usernameInput && passwordInput) { usernameInput.value = "test_account"; passwordInput.value = "test_password"; console.log("自动填充了登录表单"); } } """) def update_url_bar(self): current_browser = self.browser_tabs.currentWidget() if current_browser: current_url = current_browser.url().toString() self.url_bar.setText(current_url) # 登录页面特殊处理 if "login" in current_url or "passport" in current_url: self.url_bar.setStyleSheet("background-color: #FFF8E1;") else: self.url_bar.setStyleSheet("") def load_url(self, url_text=None): if url_text is None: url_text = self.url_bar.text().strip() if not url_text: return # 关键优化: 更智能的URL处理[10](@ref) if not url_text.startswith(("http://", "https://", "file://", "ftp://")): if "." in url_text: # 包含域名 url_text = "https://" + url_text else: # 可能是搜索内容 url_text = f"https://www.bilibili.com/search?keyword={url_text}" current_browser = self.browser_tabs.currentWidget() if current_browser: current_browser.load(QUrl(url_text)) self.status_bar.showMessage(f"正在加载: {url_text}", 3000) def play_with_jessibuca(self, stream_url): """使用Jessibuca播放视频流""" current_browser = self.browser_tabs.currentWidget() if not current_browser: return self.status_bar.showMessage("Jessibuca播放中...", 3000) current_browser.page.runJavaScript(f""" const container = document.getElementById('jessibuca-container'); if (container) {{ container.style.display = 'block'; if (!window.jessibucaPlayer) {{ window.jessibucaPlayer = new Jessibuca({{ container: container, videoBuffer: 0.2, isResize: true, text: '直播加载中...', decoder: 'ffmpeg.js', forceNoOffscreen: true }}); }} window.jessibucaPlayer.play('{stream_url}'); }} """) def import_cookies(self): """导入Cookie JSON文件""" file_path, _ = QFileDialog.getOpenFileName( self, "选择Cookie文件", "", "JSON文件 (*.json)" ) if file_path: try: with open(file_path, "r", encoding='utf-8') as f: cookies = json.load(f) if isinstance(cookies, list): self.cookies_db = cookies self.cookie_list.clear() self.cookie_list.addItems([f"{c['name']}: {c['value'][:10]}..." for c in cookies]) self.status_bar.showMessage(f"成功导入 {len(cookies)} 个Cookie", 3000) self.inject_cookies(cookies) else: QMessageBox.warning(self, "错误", "无效的Cookie格式") except Exception as e: QMessageBox.critical(self, "导入失败", f"错误: {str(e)}") def export_cookies(self): """导出Cookie到文件""" file_path, _ = QFileDialog.getSaveFileName( self, "保存Cookie文件", "", "JSON文件 (*.json)" ) if file_path: try: cookies = self.get_current_cookies() with open(file_path, "w", encoding='utf-8') as f: json.dump(cookies, f, indent=2, ensure_ascii=False) self.status_bar.showMessage("Cookie导出成功", 3000) except Exception as e: QMessageBox.critical(self, "导出失败", f"错误: {str(e)}") def get_current_cookies(self): """获取当前标签页的Cookie(模拟)""" # 实际实现需要异步获取,这里简化处理 return self.cookies_db if self.cookies_db else [] def inject_cookies(self, cookies): """将Cookie注入当前页面 - 修复登录问题[9](@ref)""" current_browser = self.browser_tabs.currentWidget() if current_browser: store = current_browser.page.profile().cookieStore() # 先清除现有Cookie store.deleteAllCookies() # 异步注入新的Cookie for cookie_data in cookies: # 创建Qt的Cookie对象 qt_cookie = QNetworkCookie( cookie_data['name'].encode('utf-8'), cookie_data['value'].encode('utf-8') ) # 设置Cookie属性 if 'domain' in cookie_data: qt_cookie.setDomain(cookie_data['domain']) if 'path' in cookie_data: qt_cookie.setPath(cookie_data['path']) if 'expiry' in cookie_data: # 转换为QDateTime expiry = QDateTime.fromSecsSinceEpoch(cookie_data['expiry']) qt_cookie.setExpirationDate(expiry) # 设置安全属性 qt_cookie.setSecure(cookie_data.get('secure', False)) qt_cookie.setHttpOnly(cookie_data.get('httpOnly', False)) # 注入Cookie store.setCookie(qt_cookie, QUrl(cookie_data.get('url', 'https://www.bilibili.com'))) self.status_bar.showMessage("Cookie注入成功,请刷新页面", 3000) def save_account(self): """保存当前账号配置""" account_name, ok = QInputDialog.getText( self, "保存账号", "输入账号名称:" ) if ok and account_name: cookies = self.get_current_cookies() if cookies: self.account_db[account_name] = { "cookies": cookies, "saved_date": datetime.now().isoformat() } self.account_list.addItem(account_name) # 持久化存储账号数据 try: with open("accounts.json", "w", encoding='utf-8') as f: json.dump(self.account_db, f, indent=2, ensure_ascii=False) QMessageBox.information(self, "成功", "账号保存成功") except Exception as e: QMessageBox.critical(self, "保存失败", f"账号保存失败: {str(e)}") else: QMessageBox.warning(self, "错误", "没有获取到有效Cookie") def switch_account(self, item): """切换账号""" account_name = item.text() if account_name in self.account_db: cookies = self.account_db[account_name].get("cookies", []) self.inject_cookies(cookies) self.status_bar.showMessage(f"已切换至账号: {account_name}", 3000) # 自动刷新当前页面 self.reload_page() else: QMessageBox.warning(self, "错误", "找不到该账号的Cookie信息") def load_accounts(self): """从文件加载保存的账号""" try: if os.path.exists("accounts.json"): with open("accounts.json", "r", encoding='utf-8') as f: self.account_db = json.load(f) self.account_list.addItems(self.account_db.keys()) except Exception as e: print(f"加载账号失败: {str(e)}") def closeEvent(self, event): """窗口关闭时释放全局资源[8](@ref)""" # 清理HTTP缓存和访问记录 self.profile.clearHttpCache() self.profile.clearAllVisitedLinks() # 保存账号数据 try: with open("accounts.json", "w", encoding='utf-8') as f: json.dump(self.account_db, f, indent=2, ensure_ascii=False) except Exception as e: print(f"保存账号失败: {str(e)}") super().closeEvent(event) if __name__ == "__main__": # 关键性能优化: 启用WebEngine调试日志[9](@ref) os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = "--enable-logging" app = QApplication(sys.argv) # 关键性能优化: 启用Qt内置的OpenGL渲染[7](@ref) app.setAttribute(Qt.AA_UseOpenGLES) window = MainWindow() window.show() sys.exit(app.exec_())
07-09
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值