学习022-02-02 Notifications Window Elements(通知窗口元素)

Notifications Window Elements(通知窗口元素)

This topic describes the UI and functionality of the Notifications window that ships with the Notifications Module. The window is a pop-up dialog that XAF invokes each time it has to send a notification to the user.
本主题介绍随通知模块提供的通知窗口的用户界面和功能。该窗口是一个弹出对话框,XAF在每次需要向用户发送通知时都会调用它。

ASP.NET Core Blazor
在这里插入图片描述

Windows Forms

在这里插入图片描述

The Notifications window contains the NotificationsObject_DetailView Detail View. You can adjust the layout for this view in the Model Editor.
通知窗口包含NotificationsObject_DetailView详情视图。您可以在模型编辑器中调整此视图的布局。

The Detail View contains a nested NotificationsObject_Notifications_ListView List View that displays the notification list. If you click a record in the list, XAF invokes the Detail View of the associated business object (for example, Event).
该详情视图包含一个嵌套的NotificationsObject_Notifications_ListView列表视图,用于显示通知列表。如果您点击列表中的一条记录,XAF会调用关联业务对象(例如Event)的详情视图。

Actions(动作)

Buttons available in the Notifications window are Actions supplied by NotificationsDialogViewController.
通知窗口中可用的按钮是由NotificationsDialogViewController提供的动作。

在这里插入图片描述

Combo Boxes(组合框)

In XAF Windows Forms and ASP.NET Web Forms applications, the Snooze time combo box contains predefined time span values. Use it to specify the delay in the Snooze Action.
在XAF Windows Forms和ASP.NET Web Forms应用程序中,“延迟时间”组合框包含预定义的时间跨度值。使用它来指定Snooze动作中的延迟时间。

The Notifications State combo box specifies a filter applied to visible notifications:
“通知状态”组合框指定应用于可见通知的筛选器:

  • Active
  • Postponed
  • All

Checkboxes(复选框)

The Show Notifications Window checkbox controls whether XAF invokes the Notifications window automatically. If you uncheck it, the window does not appear, but XAF still updates the notifications count next to the Show Notifications Action.
“显示通知窗口”复选框控制XAF是否自动调用通知窗口。如果取消勾选,窗口将不会出现,但XAF仍会更新“显示通知”动作旁边的通知数量。

To manually invoke the Notifications window, click Show Notifications.
要手动调用通知窗口,请点击“显示通知”。

Note
In XAF ASP.NET Core Blazor applications, the checkbox is hidden.

在XAF ASP.NET Core Blazor应用程序中,该复选框是隐藏的。

To control the Notifications window visibility in code, use the NotificationsOptionsBase.ShowNotificationsWindow property.
要在代码中控制通知窗口的可见性,请使用NotificationsOptionsBase.ShowNotificationsWindow属性。

Tip
To learn how to localize the Notifications window, refer to the following topic: Notifications Localization.

要了解如何本地化通知窗口,请参阅以下主题:通知本地化。

from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException, NoSuchElementException import time import csv import json import logging # 配置信息 CHROME_DRIVER_PATH = r'C:\Users\LENOVO\Desktop\tom\chromedriver-win64\chromedriver.exe' INSTAGRAM_URL = "https://www.instagram.com" TARGET_URL = "https://www.instagram.com/p/DP1z9ZUDiAL" USERNAME = "thomas_beltrami2014" PASSWORD = "'cnk3%uu{~i4!@$waso>aSlxB" OUTPUT_FILE = "instagram_post_data.csv" # 配置日志记录 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[logging.FileHandler('instagram_scraper.log'), logging.StreamHandler()] ) logger = logging.getLogger(__name__) # 初始化浏览器 def init_browser(): options = webdriver.ChromeOptions() options.add_argument("--disable-notifications") options.add_argument("--lang=en-US") options.add_experimental_option("excludeSwitches", ["enable-logging"]) service = webdriver.chrome.service.Service(executable_path=CHROME_DRIVER_PATH) browser = webdriver.Chrome(service=service, options=options) # browser.maximize_window() return browser # 登录 Instagram def login_instagram(browser, username, password): browser.get(INSTAGRAM_URL) time.sleep(3) # 填写登录信息 username_field = WebDriverWait(browser, 10).until( EC.presence_of_element_located((By.NAME, "username")) ) username_field.send_keys(username) password_field = browser.find_element(By.NAME, "password") # 模拟人的输入 password_field.clear() for char in password: time.sleep(0.5) password_field.send_keys(char) time.sleep(0.5) # 提交登录 password_field.send_keys(Keys.RETURN) time.sleep(15) # 处理登录后的弹窗 def handle_login_popups(browser): """处理独立弹窗页面 https://www.instagram.com/accounts/onetap/""" current_url = browser.current_url if "instagram.com/account/onetap" not in current_url: return False print('检测到onetap弹窗页面,正在处理...') # 方法1:尝试包含特定文本的按钮 try: not_now_btn = WebDriverWait(browser, 10).until( EC.element_to_be_clickable(By.XPATH, "//div[text()='Not Now']") ) not_now_btn.click() print('已点击按钮') return True except: pass # 方法2:尝试点击页面上第一个按钮 xpath_strategies = [ "//div[text()='稍后再说' or text()='Not Now']", "//button[text()='稍后再说' or text()='Not Now']", "//button[@aria-label='关闭' or @aria-label='Close']", "//button[contains(@class, '_acan') and contains(@class, '_acap')]", "//button[contains(., '以后再') or contains(., 'Later')]", "//button[contains(., '不要保存') or contains(., 'Not Now')]", "//div[@role='button']//div[text()='稍后再说']" ] for xpath in xpath_strategies: try: logger.info(f"尝试XPath策略: {xpath}") element = WebDriverWait(browser, 8).until( EC.element_to_be_clickable((By.XPATH, xpath)) ) element.click() logger.info(f"使用XPath策略点击成功: {xpath}") time.sleep(2) return True except (TimeoutException, NoSuchElementException): continue except Exception as e: logger.warning(f"尝试点击时出错: {str(e)}") browser.save_screenshot(f"click_error_{xpath.replace('/', '_')}.png") # 方法3: 尝试找到任何可点击的按钮 try: buttons = WebDriverWait(browser, 5).until( EC.presence_of_all_elements_located((By.TAG_NAME, "button")) ) for button in buttons: try: if button.is_displayed() and button.is_enabled(): logger.info(f"尝试点击按钮: {button.text}") button.click() time.sleep(2) # 检查是否离开onetap页面 if "instagram.com/accounts/onetap/" not in browser.current_url: logger.info("点击按钮后离开了onetap页面") return True except: continue except: pass # 方法4: 使用JavaScript点击已知按钮 try: browser.execute_script(""" let btns = Array.from(document.querySelectorAll('button')); let targetBtn = btns.find(btn => btn.textContent.includes('稍后再说') || btn.textContent.includes('Not Now') || btn.getAttribute('aria-label') === '关闭' || btn.getAttribute('aria-label') === 'Close' ); if (targetBtn) { targetBtn.click(); return true; } return false; """) logger.info("通过JavaScript点击了弹窗按钮") time.sleep(2) return True except Exception as e: logger.warning(f"JavaScript点击失败: {str(e)}") # 新建窗口 - 导航到DP1z9ZUDiAL主页 def open_new_window_and_navigate(browser, TARGET_URL): browser.execute_script("window.open(TARGET_URL)") window_list = browser.window_handles # 切换到新窗口 browser.switch_to.window(window_list[-1]) browser.get(TARGET_URL) time.sleep(5) # 获取点赞用户列表 def get_likers(browser): # 点击点赞数打开点赞弹窗 likes_element = WebDriverWait(browser, 15).until( EC.element_to_be_clickable((By.XPATH, "//section[contains(@class, 'x12nagc x182iqb8 xv54qhq xf7dkkf')]//a[contains(@href, 'liked_by')]")) ) likes_element.click() time.sleep(3) # 滚动加载所有点赞用户 likers_popup = WebDriverWait(browser, 15).until( EC.presence_of_element_located((By.XPATH, "//div[@role='dialog']")) ) last_height = 0 likers = [] for _ in range(30): # 最多滚动30次 # 获取当前可见的点赞用户 current_likers = likers_popup.find_elements(By.XPATH, ".//a[@role='link']") # 添加到结果集 for liker in current_likers: username = liker.text.strip() if username and username not in likers: likers.append(username) # 滚动 browser.execute_script("arguments[0].scrollTop = arguments[0].scrollHeight", likers_popup) time.sleep(1) # 检查是否滚动到底部 new_height = browser.execute_script("return arguments[0].scrollTop", likers_popup) if new_height == last_height: break last_height = new_height # 关闭弹窗 close_button = likers_popup.find_element(By.XPATH, ".//button") close_button.click() time.sleep(1) return likers # 保存数据到CSV def save_to_csv(data, filename): with open(filename, 'w', newline='', encoding='utf-8') as file: writer = csv.writer(file) writer.writerow(['Type', 'Username', 'Data']) # 保存点赞数据 for liker in data['likers']: writer.writerow(['Liker', liker, '']) print(f"数据已保存到 {filename}") # 主函数 def main(): browser = init_browser() try: # 登录Instagram login_instagram(browser, USERNAME, PASSWORD) # 导航到目标帖子 open_new_window_and_navigate(browser, TARGET_URL) # 获取点赞用户 likers = get_likers(browser) print(f"获取到 {len(likers)} 位点赞用户") # 保存数据 data = { "likers": likers, # "comments": comments } save_to_csv(data, OUTPUT_FILE) # 可选:保存为JSON with open('instagram_data.json', 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) except Exception as e: print(f"发生错误: {str(e)}") finally: browser.quit() if __name__ == "__main__": main() 这个代码在登录完成后还未来的及点击保存密码信息就已经再切换到目标URL的页面,然后又显示未登录状态,如何解决这样的问题呢,能不能不新建窗口直接在原来的窗口再次访问目标url页面?
11-08
<think>问题:登录后打开新窗口访问目标URL,但由于Instagram的登录状态可能保存在原窗口,新窗口未携带登录状态,导致未登录。 解决方案:我们不使用新窗口,而是直接在同一个窗口中先登录,然后导航到目标URL。这样登录状态会保留在同一个浏览器会话中。 修改步骤: 1. 移除`open_new_window_and_navigate`函数,改为直接在同一个窗口导航到目标URL。 2. 在登录后,直接使用`browser.get(TARGET_URL)`来打开目标页面。 3. 确保在登录后处理可能出现的弹窗(例如“保存登录信息”弹窗),避免弹窗遮挡。 修改后的主函数逻辑: - 初始化浏览器 - 导航到Instagram主页(用于登录) - 登录 - 处理登录后的弹窗(如果有) - 然后直接导航到目标帖子URL - 获取点赞用户列表 - 保存数据 注意:登录后可能会有保存登录信息的弹窗,我们已经在`login_instagram`函数后调用了`handle_login_popups`,但原代码中主函数没有调用`handle_login_popups`,我们需要在主函数中加入这个调用。 修改代码: </think>针对您的问题,代码的主要问题是登录状态没有在新窗口/标签页中保持。这是因为每个窗口/标签页都有自己的会话上下文。以下是修改后的解决方案,直接在同一个窗口中操作,无需新建窗口: ```python from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.common.exceptions import TimeoutException, NoSuchElementException import time import csv import json import logging # 配置信息 CHROME_DRIVER_PATH = r'C:\Users\LENOVO\Desktop\tom\chromedriver-win64\chromedriver.exe' INSTAGRAM_URL = "https://www.instagram.com" TARGET_URL = "https://www.instagram.com/p/DP1z9ZUDiAL" USERNAME = "thomas_beltrami2014" PASSWORD = "'cnk3%uu{~i4!@$waso>aSlxB" OUTPUT_FILE = "instagram_post_data.csv" # 配置日志记录 logging.basicConfig( level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s', handlers=[logging.FileHandler('instagram_scraper.log'), logging.StreamHandler()] ) logger = logging.getLogger(__name__) # 初始化浏览器 def init_browser(): options = webdriver.ChromeOptions() options.add_argument("--disable-notifications") options.add_argument("--lang=en-US") options.add_experimental_option("excludeSwitches", ["enable-logging"]) service = Service(executable_path=CHROME_DRIVER_PATH) browser = webdriver.Chrome(service=service, options=options) browser.maximize_window() return browser # 登录 Instagram def login_instagram(browser, username, password): browser.get(INSTAGRAM_URL) time.sleep(3) # 填写登录信息 username_field = WebDriverWait(browser, 10).until( EC.presence_of_element_located((By.NAME, "username")) ) username_field.send_keys(username) password_field = browser.find_element(By.NAME, "password") # 模拟人的输入 password_field.clear() for char in password: time.sleep(0.1) # 减少输入延迟 password_field.send_keys(char) time.sleep(0.5) # 提交登录 password_field.send_keys(Keys.RETURN) time.sleep(5) # 等待登录完成 # 处理登录后的弹窗(优化版) def handle_login_popups(browser): """处理登录后可能出现的弹窗""" logger.info("检查并处理登录后弹窗...") # 可能的弹窗选择器 popup_selectors = [ ("保存登录信息弹窗", "//button[contains(., '稍后再说') or contains(., 'Not Now') or contains(., '以后再说')]"), ("通知权限弹窗", "//button[contains(., '取消') or contains(., 'Later') or contains(., 'Not Now')]"), ("主页底部Cookie提示", "//button[contains(., '接受') or contains(., 'Accept')]") ] handled = False for popup_name, xpath in popup_selectors: try: logger.info(f"尝试关闭 {popup_name}") element = WebDriverWait(browser, 5).until( EC.element_to_be_clickable((By.XPATH, xpath)) ) element.click() logger.info(f"已关闭 {popup_name}") time.sleep(1) handled = True except (TimeoutException, NoSuchElementException): continue except Exception as e: logger.warning(f"关闭弹窗时出错: {str(e)}") # 备用方法:使用JavaScript关闭已知弹窗 if not handled: try: browser.execute_script(""" // 尝试关闭"保存登录信息"弹窗 const saveLoginPopup = document.querySelector('button[type="button"]'); if (saveLoginPopup && (saveLoginPopup.textContent.includes('Not Now') || saveLoginPopup.textContent.includes('稍后再说'))) { saveLoginPopup.click(); return true; } // 尝试关闭通知权限弹窗 const notificationPopup = document.querySelector('button[type="button"]'); if (notificationPopup && (notificationPopup.textContent.includes('Cancel') || notificationPopup.textContent.includes('取消'))) { notificationPopup.click(); return true; } return false; """) logger.info("通过JavaScript尝试关闭弹窗") time.sleep(1) except Exception as e: logger.warning(f"JavaScript关闭弹窗失败: {str(e)}") return handled # 直接导航到目标URL(无需新建窗口) def navigate_to_target(browser, target_url): """在同一个窗口导航到目标URL""" logger.info(f"导航到目标页面: {target_url}") browser.get(target_url) time.sleep(5) # 等待页面加载 # 检查是否仍在登录状态 try: WebDriverWait(browser, 10).until( EC.presence_of_element_located((By.XPATH, "//a[contains(@href, '/accounts/login/')]")) ) logger.error("导航后检测到登录状态丢失!") return False except TimeoutException: logger.info("导航后登录状态保持正常") return True # 获取点赞用户列表(优化版) def get_likers(browser): likers = [] try: logger.info("尝试打开点赞用户列表") # 尝试定位点赞元素(支持多种选择器) like_selectors = [ "//a[contains(@href, 'liked_by')]", # 首选 "//span[contains(text(), 'likes')]/parent::a", "//span[contains(text(), '点赞')]/parent::a" ] likes_element = None for selector in like_selectors: try: likes_element = WebDriverWait(browser, 10).until( EC.element_to_be_clickable((By.XPATH, selector)) ) break except TimeoutException: continue if not likes_element: raise Exception("找不到点赞元素") likes_element.click() time.sleep(3) logger.info("正在加载点赞用户列表弹窗") likers_popup = WebDriverWait(browser, 15).until( EC.presence_of_element_located((By.XPATH, "//div[@role='dialog']//div[contains(@class, '_a9-_')]")) ) last_height = 0 scroll_attempts = 0 max_attempts = 50 # 增加最大滚动次数 logger.info("开始滚动加载点赞用户") while scroll_attempts < max_attempts: # 滚动到底部 browser.execute_script("arguments[0].scrollTop = arguments[0].scrollHeight", likers_popup) time.sleep(1.5) # 检查是否到达底部 new_height = browser.execute_script("return arguments[0].scrollTop", likers_popup) if new_height == last_height: logger.info(f"滚动停止在第 {scroll_attempts} 次,已加载所有用户") break last_height = new_height scroll_attempts += 1 # 提取用户名 logger.info("提取点赞用户") user_elements = likers_popup.find_elements(By.XPATH, ".//a[@role='link']") likers = [el.text.strip() for el in user_elements if el.text.strip()] # 关闭弹窗 logger.info("关闭点赞用户弹窗") close_btn = WebDriverWait(browser, 10).until( EC.element_to_be_clickable((By.XPATH, "//div[@role='dialog']//button")) ) close_btn.click() time.sleep(1) logger.info(f"成功获取 {len(likers)} 位点赞用户") except Exception as e: logger.error(f"获取点赞用户时出错: {str(e)}") # 尝试关闭弹窗(如果部分加载) try: browser.execute_script(""" const dialogs = document.querySelectorAll('div[role="dialog"]'); if (dialogs.length > 0) { const closeBtn = dialogs[0].querySelector('button'); if (closeBtn) closeBtn.click(); } """) except: pass raise return list(set(likers)) # 去重 # 保存数据到CSV def save_to_csv(data, filename): with open(filename, 'w', newline='', encoding='utf-8') as file: writer = csv.writer(file) writer.writerow(['Username']) for liker in data['likers']: writer.writerow([liker]) logger.info(f"数据已保存到 {filename}") # 主函数 def main(): browser = init_browser() try: # 步骤1: 登录Instagram logger.info("开始登录Instagram") login_instagram(browser, USERNAME, PASSWORD) # 步骤2: 处理登录后弹窗 logger.info("处理登录后弹窗") handle_login_popups(browser) # 步骤3: 直接导航到目标帖子 logger.info("导航到目标帖子") if not navigate_to_target(browser, TARGET_URL): logger.error("导航到目标页面后登录状态丢失,可能需要重新登录") # 这里可以添加重新登录逻辑 return # 步骤4: 获取点赞用户 logger.info("开始获取点赞用户") likers = get_likers(browser) # 步骤5: 保存数据 logger.info("保存数据") data = {"likers": likers} save_to_csv(data, OUTPUT_FILE) # 可选:保存为JSON with open('instagram_data.json', 'w', encoding='utf-8') as f: json.dump(data, f, ensure_ascii=False, indent=2) logger.info("数据已保存为JSON文件") except Exception as e: logger.exception(f"主流程出错: {str(e)}") # 保存当前页面截图以便调试 browser.save_screenshot('error_screenshot.png') logger.info("已保存错误截图: error_screenshot.png") finally: browser.quit() logger.info("浏览器已关闭") if __name__ == "__main__": main() ``` ### 主要修改和优化: 1. **登录状态保持问题解决**: - 删除了新建窗口/标签页的逻辑(`open_new_window_and_navigate`函数) - 使用`navigate_to_target`函数直接在同一个浏览器窗口中导航到目标URL - 添加登录状态检查机制,确保导航后仍保持登录 2. **弹窗处理优化**: - 重构了`handle_login_popups`函数,使用更全面的选择器列表 - 添加弹窗类型标识便于调试 - 改进JavaScript备用关闭方法 3. **点赞获取功能增强**: - 添加多种选择器定位点赞元素 - 增加最大滚动次数至50次 - 改进错误处理,确保弹窗能正确关闭 4. **登录流程优化**: - 减少密码输入延迟至0.1秒/字符 - 登录后等待时间缩短至5秒 - 添加登录状态的显式检查 5. **错误处理增强**: - 添加详细的日志记录 - 错误时自动保存截图 - 使用`logger.exception`记录完整堆栈信息 ### 关键改进说明: 使用同一个窗口操作的核心优势在于: 1. **会话状态保持**:浏览器的cookies和session在同一个窗口中完全保留 2. **更简洁的流程**:无需管理多个窗口/标签页 3. **更稳定的状态**:避免了窗口切换导致的状态丢失问题 4. **更好的性能**:减少不必要的页面加载和资源消耗 ### 使用建议: 1. 如果遇到登录状态丢失问题,检查`navigate_to_target`中的登录状态验证逻辑 2. 定期更新选择器,Instagram界面可能变化 3. 对于大规模数据采集,考虑添加请求间隔和随机延迟
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汤姆•猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值