Local 和 Web Contexts的学习

本文详细介绍了在Windows 8 Metro应用中,不同上下文(LocalContext与WebContext)下页面导航及iframe加载的规定。具体包括哪些URL格式可以在LocalContext和WebContext中合法使用,以及如何处理跨上下文导航的问题。

Programming Windows 8 Apps with HTML, CSS and JavaScript 2Ed 77页:

1. Metro 应用的主页(就是启动后的第一屏)总是运行在Local Context下的,而且通过<a href>标记或者document.location要转到的其他页面的必须也是在Local Context 下的。但是内嵌的iframe的页面,可以使Local的也可以是Web Context的。

2. 一个Web Context的页面,只能内嵌Web Context 的iFrame。

所以,作者有了如下总结:

假如源页面是Local Context的,那么

  • <iframe src="ms-appx:///" 这个是合法的,因为Local Context里面允许2种类型的iFrame。
  • <iframe src="ms-appx-web:///" 这个是合法的,因为Local Context里面允许2种类型的iFrame。
  • <iframe src="http[s]://" 这个是合法的,因为Local Context里面允许2种类型的iFrame。
  • <a href="[someURL]" target="myFrame"> <iframe name="myFrame"> 这个是合法的,因为Local Context里面允许2种类型的iFrame。
  • <a href="ms-appx:///"> 这个是合法的,因为Local Context里面允许Local Context的直接Navigation。
  • <a href="ms-appx-web:///"> 这个是不合法的,因为Local Context里面不允许Web Context的直接Navigation。
  • <a href="[remoteURL]"> 这个没有合不合法一说,因为目标页面是外部页面,即不是以ms-appx开头的,所以Metro应用会打开浏览器显示目标页面。

假如源页面是Web Context的,那么

  • <iframe src="ms-appx:///" 这个是不合法的,因为Web Context里面不允许Local Context的iFrame。
  • <iframe src="ms-appx-web:///" 这个是合法的,因为Web Context里面允许Web Context的iFrame。
  • <iframe src="http[s]://" 这个是合法的,因为Web Context里面允许Web Context的iFrame。
  • <a href="[someURL]" target="myFrame"> <iframe name="myFrame"> 这个是合法的只要URL是ms-appx-web开头。
  • <a href="ms-appx:///"> 这个是不合法的,因为Web Context里面不允许Local Context的直接Navigation,但是你可以通过调用MSApp.addPublicLocalApplicationUri("ms-appx:///frame-local.html");解决。
  • <a href="ms-appx-web:///"> 这个是合法的,因为Web Context里面允许Web Context的直接Navigation。
  • <a href="[remoteURL]"> 这个没有合不合法一说,因为目标页面是外部页面,即不是以ms-appx开头的,所以Metro应用会打开浏览器显示目标页面。

除此之外,Web Context的页面是无权使用Broker对象的(照相机啊,GeoLocation啊),微软这么设计的原因就是他认为Web Context是不安全的,因为他们可以随意导航到外部页面。你只有手动的把他们加到信任列表里面才行。



from playwright.sync_api import sync_playwright import subprocess import time import os debugging_ports = [9222, 9223, 9224, 9225] chrome_path = "C:\\Users\\Windows10\\Desktop\\chromedriver-win64\\chrome-win64\\chrome.exe" subprocess.Popen([ chrome_path, f"--remote-debugging-port={debugging_ports[0]}", "https://www.dianxiaomi.com/web/popTemu/add" ]) all_files = [] # 存储所有要上传的文件路径 root_folder = "C:\\Users\\Windows10\\Desktop\\素材" # 替换为需要上传文件夹的路径 dz_numbered_folders = [ folder for folder in os.listdir(root_folder) if folder.startswith("DZ") and folder[2:].isdigit() and os.path.isdir(os.path.join(root_folder, folder)) ] print(f"找到 {len(dz_numbered_folders)} 个 DZ+数字 的文件夹:") dz_numbered_folders.sort()# 排序确保按 DZ001 -> DZ002 顺序上传 def upload_folder(folder_name, port): folder_path = os.path.join(root_folder, folder_name) process = subprocess.Popen([ chrome_path, f"--remote-debugging-port={port}", "https://www.dianxiaomi.com/web/popTemu/add" ])# 启动 Chrome 并打开目标网页 time.sleep(2) # 等待浏览器启动 with sync_playwright() as p: try: browser = p.chromium.connect_over_cdp(f"http://localhost:{port}") page = browser.contexts[0].pages[0] # 获取第一个上下文的第一个页面 # 定位按钮并悬停 button = page.locator("button:has-text('选择图片')") button.hover() print("鼠标已悬停在按钮上") # 定位并点击“本地图片”选项 page.wait_for_selector("li[data-menu-id='local']", timeout=1000) page.locator("li[data-menu-id='local']").click() print("已点击“本地图片”选项") files_in_folder = [ os.path.join(folder_path, f) for f in os.listdir(folder_path) if os.path.isfile(os.path.join(folder_path, f)) and f.lower().endswith(('.png', '.jpg', '.jpeg')) ] if files_in_folder: print(f"准备上传 {len(files_in_folder)} 个文件...") page.set_input_files("input[type=file]", files_in_folder)# 批量上传文件(页面中有一个隐藏的 <input type="file"> 元素) print("文件上传完成!") else: print(f"文件夹 {folder_name} 中没有图片可上传。") print("请关闭浏览器窗口以继续下一个文件夹...") while True: if not browser.contexts or not browser.contexts[0].pages: print("页面已关闭,继续下一个任务。") break time.sleep(1) browser.close()# 关闭浏览器连接 # # 遍历每个 DZ 文件夹 # for folder in dz_numbered_folders: # full_folder_path = os.path.join(root_folder, folder) # 修复:加上 root_folder # print(f"正在处理文件夹:{folder}") # # files_in_folder = [ # # os.path.join(folder, f) for f in os.listdir(folder) # # if os.path.isfile(os.path.join(folder, f)) and f.lower().endswith(('.png', '.jpg', '.jpeg')) # # ] # file_paths = [ # os.path.join(root_folder, f) for f in os.listdir(root_folder) # if os.path.isfile(os.path.join(root_folder, f)) and f.lower().endswith(('.png', '.jpg', '.jpeg')) # ] # all_files.extend(file_paths) # # 批量上传文件(页面中有一个隐藏的 <input type="file"> 元素) # if all_files: # print(f"准备上传 {len(all_files)} 个文件...") # page.set_input_files("input[type=file]", all_files) # print("所有文件上传完成!") # else: # print("没有找到任何可上传的文件。") except Exception as e: print(f"操作失败:{e}") finally: process.terminate() # 关闭浏览器进程 for i, folder in enumerate(dz_numbered_folders): upload_folder(folder, debugging_ports[i % len(debugging_ports)]) 代码如上,打开是多个标签页,我需要打开多个窗口,如何修改
09-11
你当前的代码使用的是: ```python subprocess.Popen([...]) ``` 来启动浏览器,并且是**每个任务都复用同一个浏览器实例**,但实际上你发现它会**打开多个标签页**而不是**多个独立窗口**。 --- ## ✅ 问题分析 你希望的是: > 每个上传任务都在**独立的浏览器窗口**中执行,而不是在同一个浏览器实例中打开多个标签页。 但目前: - 你使用 `connect_over_cdp` 连接到的是一个浏览器实例; - 每次打开新页面时,Playwright 会复用该实例,导致打开多个标签页; - 要实现“多个浏览器窗口”,你需要**启动多个独立的浏览器实例**。 --- ## ✅ 解决方案 ### ✅ 方法:每次调用 `upload_folder()` 时启动一个**全新浏览器实例** 我们不再复用浏览器,而是为每个任务启动一个独立的浏览器实例,并指定不同的调试端口用户数据目录(可选),以确保互不干扰。 --- ## ✅ 修改后的代码如下: ```python from playwright.sync_api import sync_playwright import subprocess import time import os # 调试端口列表 debugging_ports = [9222, 9223, 9224, 9225] # Chrome 浏览器路径(请确保是真正的 Chrome 可执行文件) chrome_path = "C:\\Users\\Windows10\\Desktop\\chromedriver-win64\\chrome-win64\\chrome.exe" # 根目录 root_folder = "C:\\Users\\Windows10\\Desktop\\素材" # 获取 DZ 文件夹 dz_numbered_folders = sorted([ folder for folder in os.listdir(root_folder) if folder.startswith("DZ") and folder[2:].isdigit() and os.path.isdir(os.path.join(root_folder, folder)) ]) print(f"找到 {len(dz_numbered_folders)} 个 DZ+数字 的文件夹:") def upload_folder(folder_name, port): folder_path = os.path.join(root_folder, folder_name) # 每个任务都启动一个独立浏览器实例 process = subprocess.Popen([ chrome_path, f"--remote-debugging-port={port}", "--no-first-run", "--no-default-browser-check", "--new-window", # 强制以新窗口形式打开 "https://www.dianxiaomi.com/web/popTemu/add" ]) time.sleep(3) # 等待浏览器启动完成 try: with sync_playwright() as p: browser = p.chromium.connect_over_cdp(f"http://localhost:{port}") page = browser.contexts[0].pages[0] # 获取第一个页面 print(f"正在上传文件夹:{folder_name}") # 定位按钮并悬停 page.locator("button:has-text('选择图片')").hover() print("鼠标已悬停在按钮上") # 点击“本地图片”选项 page.locator("li[data-menu-id='local']").click() print("已点击“本地图片”选项") # 获取当前文件夹下的所有图片 files_in_folder = [ os.path.join(folder_path, f) for f in os.listdir(folder_path) if os.path.isfile(os.path.join(folder_path, f)) and f.lower().endswith(('.png', '.jpg', '.jpeg')) ] if files_in_folder: print(f"准备上传 {len(files_in_folder)} 个文件...") page.set_input_files("input[type=file]", files_in_folder) print("文件上传完成!") else: print(f"文件夹 {folder_name} 中没有图片可上传。") # 等待页面关闭 print("请关闭浏览器窗口以继续下一个文件夹...") while True: if not browser.contexts or not browser.contexts[0].pages: print("页面已关闭,继续下一个任务。") break time.sleep(1) browser.close() except Exception as e: print(f"操作失败:{e}") finally: process.terminate() # 关闭浏览器进程 # 执行上传任务 for i, folder in enumerate(dz_numbered_folders): upload_folder(folder, debugging_ports[i % len(debugging_ports)]) ``` --- ## ✅ 关键修改点说明 | 修改点 | 说明 | |--------|------| | `--new-window` | Chrome 参数,强制以新窗口形式打开页面,避免复用标签页 | | 每次 `upload_folder()` 都启动一个新浏览器实例 | 确保任务之间互不干扰 | | `process.terminate()` | 任务完成后关闭浏览器进程 | | 独立的调试端口 | 每个浏览器使用不同端口,避免冲突 | --- ## ✅ 如果你希望每个浏览器有独立的配置目录(更彻底隔离) 你还可以添加: ```python "--user-data-dir=C:\\temp\\chrome_profile_{port}", # 每个浏览器使用独立的配置目录 ``` 例如: ```python profile_dir = f"C:\\temp\\chrome_profile_{port}" os.makedirs(profile_dir, exist_ok=True) process = subprocess.Popen([ chrome_path, f"--remote-debugging-port={port}", f"--user-data-dir={profile_dir}", "--new-window", "https://www.dianxiaomi.com/web/popTemu/add" ]) ``` --- ## ✅ 总结 | 需求 | 实现方式 | |------|----------| | 打开多个浏览器窗口 | 每次启动一个新浏览器实例 | | 避免标签页复用 | 使用 `--new-window` 参数 | | 避免端口冲突 | 每个浏览器使用不同调试端口 | | 更彻底隔离 | 每个浏览器使用独立的 `--user-data-dir` | --- ##
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值