Skip to main content
Glama
selectors.py10.5 kB
""" SEEK页面元素选择器 定义SEEK平台的CSS选择器和XPath表达式 """ # SEEK 登录页面 LOGIN_SELECTORS = { 'username_field': 'input[type="email"]', 'password_field': 'input[type="password"]', 'login_button': 'button[type="submit"]', 'remember_me': 'input[type="checkbox"][name="rememberMe"]', 'forgot_password': 'a[href*="forgot-password"]' } # SEEK 职位搜索页面 SEARCH_SELECTORS = { 'search_keywords': 'input[data-automation="searchKeywordsField"]', 'search_location': 'input[data-automation="searchLocationField"]', 'search_button': 'button[data-automation="searchSubmit"]', # 筛选器 'salary_filter': 'button[data-automation="salaryFilter"]', 'job_type_filter': 'button[data-automation="workTypeFilter"]', 'date_posted_filter': 'button[data-automation="dateRange"]', 'company_filter': 'button[data-automation="companyFilter"]', # 职位卡片 'job_cards': '[data-automation="normalJob"]', 'job_title': '[data-automation="jobTitle"]', 'company_name': '[data-automation="jobCompany"]', 'job_location': '[data-automation="jobLocation"]', 'job_salary': '[data-automation="jobSalary"]', 'job_classification': '[data-automation="jobClassification"]', # 分页 'next_page': 'a[aria-label="Next"]', 'pagination': '[data-automation="page-"]', 'results_count': '[data-automation="totalJobsCount"]' } # SEEK 职位详情页面 JOB_DETAIL_SELECTORS = { 'job_title': 'h1[data-automation="job-detail-title"]', 'company_name': '[data-automation="advertiser-name"]', 'job_location': '[data-automation="job-detail-location"]', 'job_classification': '[data-automation="job-detail-classification"]', 'job_sub_classification': '[data-automation="job-detail-subClassification"]', 'job_salary': '[data-automation="job-detail-salary"]', 'job_work_type': '[data-automation="job-detail-work-type"]', 'job_description': '[data-automation="jobAdDetails"]', # 申请按钮 'apply_button': '[data-automation="job-detail-apply"]', 'external_apply_button': '[data-automation="job-detail-apply-link-label"]', # 职位详情 'advertiser_description': '[data-automation="advertiser-description"]', 'job_reference': '[data-automation="job-reference-number"]', 'posted_date': '[data-automation="job-posted-date"]' } # SEEK 申请流程 APPLICATION_SELECTORS = { 'application_form': 'form[data-automation="application-form"]', # 个人信息 'first_name': 'input[name="firstName"]', 'last_name': 'input[name="lastName"]', 'email': 'input[name="email"]', 'phone': 'input[name="phone"]', 'mobile': 'input[name="mobile"]', # 地址信息 'address_line1': 'input[name="addressLine1"]', 'address_line2': 'input[name="addressLine2"]', 'suburb': 'input[name="suburb"]', 'state': 'select[name="state"]', 'postcode': 'input[name="postcode"]', 'country': 'select[name="country"]', # 签证状态 'work_rights': 'select[name="workRights"]', 'visa_status': 'select[name="visaStatus"]', # 文件上传 'resume_upload': 'input[type="file"][accept*=".pdf,.doc"]', 'cover_letter_upload': 'input[type="file"][accept*=".pdf,.doc,.txt"]', 'additional_documents': 'input[type="file"][name="additionalDocuments"]', # 问题回答 'text_questions': 'input[type="text"]', 'textarea_questions': 'textarea', 'dropdown_questions': 'select', 'radio_questions': 'input[type="radio"]', 'checkbox_questions': 'input[type="checkbox"]', # 提交按钮 'submit_application': 'button[data-automation="apply-form-continue-button"]', 'confirm_application': 'button[data-automation="confirm-application"]', # 验证信息 'error_messages': '.error-message', 'required_fields': '[required]', 'field_errors': '.field-error' } # SEEK 个人资料页面 PROFILE_SELECTORS = { 'profile_header': '.profile-header', 'profile_name': '.profile-name', 'profile_title': '.profile-title', 'profile_location': '.profile-location', # 个人资料部分 'personal_details': '.personal-details-section', 'work_experience': '.work-experience-section', 'education': '.education-section', 'skills': '.skills-section', 'certifications': '.certifications-section', # 编辑按钮 'edit_personal_details': 'button[data-automation="edit-personal-details"]', 'edit_experience': 'button[data-automation="edit-experience"]', 'edit_education': 'button[data-automation="edit-education"]', # 求职偏好 'job_preferences': '.job-preferences-section', 'salary_expectations': 'input[name="salaryExpectation"]', 'preferred_locations': 'input[name="preferredLocations"]', 'work_type_preferences': 'select[name="workTypePreferences"]' } # SEEK 申请历史页面 APPLICATION_HISTORY_SELECTORS = { 'applications_list': '.applications-list', 'application_items': '.application-item', 'application_status': '.application-status', 'application_date': '.application-date', 'job_title_link': '.job-title-link', 'company_name_text': '.company-name', 'view_application': 'a[data-automation="view-application"]', 'withdraw_application': 'button[data-automation="withdraw-application"]' } # SEEK 公司页面 COMPANY_SELECTORS = { 'company_header': '.company-header', 'company_name': '.company-name', 'company_logo': '.company-logo img', 'company_description': '.company-description', 'company_location': '.company-location', 'company_size': '.company-size', 'company_industry': '.company-industry', 'follow_company': 'button[data-automation="follow-company"]', 'company_jobs': '.company-jobs-section' } # SEEK 通知设置 NOTIFICATION_SELECTORS = { 'notification_settings': '.notification-settings', 'email_alerts': 'input[name="emailAlerts"]', 'sms_alerts': 'input[name="smsAlerts"]', 'job_alert_frequency': 'select[name="jobAlertFrequency"]', 'application_updates': 'input[name="applicationUpdates"]', 'save_settings': 'button[data-automation="save-notification-settings"]' } # 通用选择器 COMMON_SELECTORS = { 'loading_spinner': '.loading-spinner', 'error_banner': '.error-banner', 'success_banner': '.success-banner', 'modal_overlay': '.modal-overlay', 'modal_close': 'button[data-automation="modal-close"]', 'tooltip': '.tooltip', 'dropdown_menu': '.dropdown-menu', 'pagination_controls': '.pagination-controls', 'breadcrumbs': '.breadcrumbs' } # 表单验证选择器 VALIDATION_SELECTORS = { 'required_indicator': '.required-indicator', 'field_error': '.field-error', 'success_icon': '.success-icon', 'warning_icon': '.warning-icon', 'validation_message': '.validation-message' } # XPath 表达式 XPATH_SELECTORS = { 'button_by_text': "//button[contains(text(), '{text}')]", 'link_by_text': "//a[contains(text(), '{text}')]", 'label_by_text': "//label[contains(text(), '{text}')]", 'option_by_text': "//option[contains(text(), '{text}')]", 'input_by_label': "//label[contains(text(), '{label}')]/following-sibling::input", 'checkbox_by_label': "//label[contains(text(), '{label}')]/input[@type='checkbox']", 'radio_by_label': "//label[contains(text(), '{label}')]/input[@type='radio']" } # 动态选择器 DYNAMIC_SELECTORS = { 'job_by_title': lambda title: f'[data-automation="jobTitle"][title*="{title}"]', 'filter_by_value': lambda filter_type, value: f'[data-automation="{filter_type}Filter"] option[value="{value}"]', 'salary_range': lambda min_salary, max_salary: f'[data-automation="salaryFilter"] option[value*="{min_salary}-{max_salary}"]', 'work_type': lambda work_type: f'[data-automation="workTypeFilter"] option[value="{work_type}"]' } # 等待条件选择器 WAIT_SELECTORS = { 'page_loaded': 'main', 'search_results_loaded': '[data-automation="normalJob"]', 'job_detail_loaded': '[data-automation="job-detail-title"]', 'application_form_loaded': '[data-automation="application-form"]', 'profile_loaded': '.profile-header' } # 移动端选择器 MOBILE_SELECTORS = { 'mobile_menu': '.mobile-menu-toggle', 'mobile_search': '.mobile-search-form', 'mobile_job_card': '.mobile-job-card', 'mobile_filters': '.mobile-filters-toggle' } class SeekSelectors: """SEEK选择器管理类""" def __init__(self): self.login = LOGIN_SELECTORS self.search = SEARCH_SELECTORS self.job_detail = JOB_DETAIL_SELECTORS self.application = APPLICATION_SELECTORS self.profile = PROFILE_SELECTORS self.application_history = APPLICATION_HISTORY_SELECTORS self.company = COMPANY_SELECTORS self.notification = NOTIFICATION_SELECTORS self.common = COMMON_SELECTORS self.validation = VALIDATION_SELECTORS self.xpath = XPATH_SELECTORS self.dynamic = DYNAMIC_SELECTORS self.wait = WAIT_SELECTORS self.mobile = MOBILE_SELECTORS def get_field_selector(self, field_name: str) -> str: """根据字段名获取选择器""" field_mapping = { 'firstName': self.application['first_name'], 'lastName': self.application['last_name'], 'email': self.application['email'], 'phone': self.application['phone'], 'mobile': self.application['mobile'], 'workRights': self.application['work_rights'], 'visaStatus': self.application['visa_status'], 'resume': self.application['resume_upload'], 'coverLetter': self.application['cover_letter_upload'] } return field_mapping.get(field_name) def get_xpath_by_text(self, element_type: str, text: str) -> str: """根据文本内容生成XPath""" xpath_map = { 'button': self.xpath['button_by_text'], 'link': self.xpath['link_by_text'], 'label': self.xpath['label_by_text'], 'option': self.xpath['option_by_text'] } if element_type in xpath_map: return xpath_map[element_type].format(text=text) return None def get_dynamic_selector(self, selector_type: str, *args) -> str: """获取动态选择器""" if selector_type in self.dynamic: return self.dynamic[selector_type](*args) return None # 导出默认实例 selectors = SeekSelectors()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/guangliangyang/mcp4Interview'

If you have feedback or need assistance with the MCP directory API, please join our Discord server