油猴脚本作为浏览器自动化的重要工具,能够极大提升网页使用体验和工作效率。本文将全面介绍油猴脚本的开发技巧,从基础语法到高级应用,帮助读者掌握浏览器自动化的核心技能。
## 油猴脚本基础入门
### 脚本元数据配置
每个油猴脚本都以特定的元数据块开始,定义脚本的基本信息和行为。
```javascript
// ==UserScript==
// @name 网页自动化助手
// @namespace http://tampermonkey.net/
// @version 1.2.0
// @description 自动完成网页操作,提升浏览效率
// @author 开发者名称
// @match https://*.example.com/*
// @match https://www.sample-site.com/path/*
// @exclude https://admin.example.com/*
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_notification
// @grant GM_xmlhttpRequest
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @connect api.example.com
// @run-at document-end
// ==/UserScript==
```
### 基础脚本结构
理解油猴脚本的基本结构和执行流程。
```javascript
(function() {
'use strict';
// 配置常量
const CONFIG = {
autoRefresh: true,
refreshInterval: 30000, // 30秒
enableFeatures: ['autoFill', 'removeAds', 'enhanceUI']
};
// 主函数
function init() {
console.log('油猴脚本开始执行');
// 等待页面加载完成
if (document.readyState <"gold.kandy58.cn">=== 'loading') {
document.addEventListener('DOMContentLoaded', main);
} else {
main();
}
}
// 主要逻辑
function main() {
try {
// 执行各个功能模块
if (CONFIG.autoRefresh) {
setupAutoRefresh();
}
if (CONFIG.enableFeatures.includes('autoFill')) {
setupAutoFill();
}
if (CONFIG.enableFeatures.includes('removeAds')) {
removeAdvertisements();
}
if (CONFIG.enableFeatures.includes('enhanceUI')) {
enhanceUserInterface();
}
// 监听页面变化(单页应用)
observePageChanges();
} catch (error) {
console.error('脚本执行错误:', error);
showNotification('脚本执行出现错误', 'error');
}
}
// 启动脚本
init();
})();
```
## 实用功能开发
### 表单自动填充
自动化填写常见表单字段,节省重复输入时间。
```javascript
// 表单自动填充功能
function setupAutoFill() {
const formData =<"EPL.kandy58.cn"> {
username: 'my_username',
email: 'user@example.com',
phone: '13800138000',
address: '示例地址'
};
// 自动填充函数
function autoFillForm() {
const forms = document.querySelectorAll('form');
forms.forEach(form => {
// 根据输入框的name、id或placeholder自动填充
Object.keys(formData).forEach(key => {
const selectors = [
`input[name="${key}"]`,
`input[id="${key}"]`,
`input[placeholder*="${key}"]`,
`textarea[name="${key}"]`
];
selectors.forEach(selector => {
const element = form.querySelector(selector);
if (element && !element.value) {
element.value = formData[key];
// 触发输入事件,确保验证通过
element.dispatchEvent(new Event('input', { bubbles: true }));
element.dispatchEvent(new Event('change', { bubbles: true }));
}
});
});
});
}
// 立即执行一次
autoFillForm();
// 监听动态加载的表单
const observer = new MutationObserver(() => {
autoFillForm();
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
// 智能表单填充(根据上下文)
function smartFormFiller() {
// 检测页面类型并智能填充
const pageType = detectPageType();
switch (pageType) {
case 'login':
autoFillLoginForm();
break<"CFL.kandy58.cn">;
case 'registration':
autoFillRegistrationForm();
break;
case 'shipping':
autoFillShippingForm();
break;
case 'payment':
autoFillPaymentForm();
break;
}
}
function detectPageType() {
const url = window.location.href;
const title = document.title.toLowerCase();
if (url.includes('login') || title.includes('登录')) return 'login';
if (url.includes('register') || title.includes('注册')) return 'registration';
if (url.includes('shipping') || title.includes('配送')) return 'shipping';
if (url.includes('payment') || title.includes('支付')) return 'payment';
return 'unknown';
}
function autoFillLoginForm() {
const savedUsername = GM_getValue('username', '');
const savedPassword = GM_getValue('password', '');
if (savedUsername) {
const usernameField = document.querySelector('input[type="text"], input[type="email"], input[name="username"]');
if (usernameField) usernameField.value = savedUsername;
}
}
function autoFillRegistrationForm() {
// 注册表单的智能填充逻辑
const userInfo = {
nickname: '用户昵称',
birthday: '1990-01-01',
gender: 'male'
};
// 填充昵称
const nicknameFields <"SerieA.kandy58.cn">= [
'input[name="nickname"]',
'input[name="display_name"]',
'input[placeholder*="昵称"]'
];
nicknameFields.forEach(selector => {
const field = document.querySelector(selector);
if (field) field.value = userInfo.nickname;
});
}
```
### 广告屏蔽与内容清理
移除网页中的广告和干扰元素。
```javascript
// 广告屏蔽功能
function removeAdvertisements() {
// 广告选择器列表
const adSelectors = [
'.ad-container',
'.advertisement',
'[class*="ad"]',
'[id*="ad"]',
'iframe[src*="ads"]',
'ins.adsbygoogle',
'.ad-banner',
'.popup-ad',
'.ad-sidebar',
'.sponsored-content'
];
// 移除广告元素
function removeAds() {
adSelectors.forEach(selector => {
const elements = document.querySelectorAll(selector);
elements.forEach(element => {
// 检查元素是否可见
const style = window.getComputedStyle(element);
if (style.display !== 'none') {
element.remove();
console.log('移除广告元素:', selector);
}
});
});
}
// 屏蔽广告请求
function blockAdRequests() {
const originalFetch = window.fetch;
window.fetch = function(...args) {
const url = args[0];
if (typeof url === 'string' && isAdUrl(url)) {
console.log('屏蔽广告请求:', url);
return Promise.reject(new Error('广告请求被屏蔽'));
}
return originalFetch.apply(this, args);
};
// 重写XMLHttpRequest
const originalOpen <"LFP.kandy58.cn">= XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(method, url) {
if (isAdUrl(url)) {
console.log('屏蔽XHR广告请求:', url);
this._isAdRequest = true;
return;
}
originalOpen.apply(this, arguments);
};
}
function isAdUrl(url) {
const adKeywords = ['adsystem', 'doubleclick', 'googleads', 'adservice', 'adsafeprotected'];
return adKeywords.some(keyword => url.includes(keyword));
}
// 执行广告移除
removeAds();
blockAdRequests();
// 持续监控新出现的广告
const adObserver = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1) { // Element node
adSelectors.forEach(selector => {
if (node.matches && node.matches(selector)) {
node.remove();
} else if (node.querySelector) {
const ads = node.querySelectorAll(selector);
ads.forEach(ad => ad.remove());
}
});
}
});
});
});
adObserver.observe(document.body, {
childList: true,
subtree: true
});
}
// 页面内容清理
function cleanPageContent<"LaLiga.kandy58.cn">() {
// 移除干扰元素
const elementsToRemove = [
'.social-share', // 社交分享按钮
'.popup', // 弹窗
'.newsletter', // 邮件订阅
'.recommendation', // 推荐内容
'.floating-bar' // 浮动栏
];
elementsToRemove.forEach(selector => {
document.querySelectorAll(selector).forEach(element => {
element.remove();
});
});
// 清理样式
const style = document.createElement('style');
style.textContent = `
/* 隐藏不需要的元素 */
.social-buttons,
.share-widget,
.floating-ad {
display: none !important;
}
/* 改善阅读体验 */
.article-content {
max-width: 800px !important;
margin: 0 auto !important;
line-height: 1.6 !important;
font-size: 16px !important;
}
/* 移除背景广告 */
body {
background-image: none !important;
}
`;
document.head.appendChild(style);
}
```
## 高级自动化技巧
### 页面内容监控与自动操作
监控页面变化并自动执行操作。
```javascript
// 页面监控与自动操作
function observePageChanges() {
let lastUrl = location.href;
// 监听URL变化(单页应用)
new MutationObserver(() => {
const url = location.href;
if (url !== lastUrl) {
lastUrl =<"Bundesliga.kandy58.cn"> url;
onPageChange(url);
}
}).observe(document, { subtree: true, childList: true });
// 监听元素出现
function waitForElement(selector, timeout = 10000) {
return new Promise((resolve, reject) => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}
const observer = new MutationObserver(() => {
if (document.querySelector(selector)) {
observer.disconnect();
resolve(document.querySelector(selector));
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
setTimeout(() => {
observer.disconnect();
reject(new Error(`等待元素超时: ${selector}`));
}, timeout);
});
}
// 自动点击"加载更多"
function autoLoadMore() {
const loadMoreSelectors = [
'.load-more',
'.show-more',
'[class*="load"]',
'button:contains("加载更多")'
];
setInterval(() => {
loadMoreSelectors.forEach(selector => {
const button = document.querySelector(selector);
if (button && isElementInViewport(button)) {
button.click();
console.log('自动点击加载更多按钮');
}
});
}, 3000);
}
function isElementInViewport(el) {
const rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
}
// 自动分页
function autoPagination() {
let currentPage = 1;
function navigateToNextPage() {
const nextPageSelectors = [
'.next-page',
'.pagination-next',
'a[rel="next"]',
'a:contains("下一页")'
];
for (const selector of nextPageSelectors) {
const nextButton = document.querySelector(selector);
if (nextButton && !nextButton.disabled) {
currentPage++;
console.log(`自动翻页到第 ${currentPage} 页`);
nextButton.click();
return true;
}
}
return false;
}
// 每30秒尝试翻页
setInterval(()<"UEFA.kandy58.cn"> => {
// 检查是否到达页面底部
const isAtBottom = (window.innerHeight + window.scrollY) >= document.body.offsetHeight - 100;
if (isAtBottom) {
navigateToNextPage();
}
}, 30000);
}
```
### 数据抓取与导出
自动抓取网页数据并导出。
```javascript
// 数据抓取功能
function setupDataScraping() {
// 配置抓取规则
const scrapeRules = {
'product-list': {
container: '.product-list, .items-grid',
items: '.product-item, .item',
fields: {
name: '.product-name, .title',
price: '.price, .cost',
image: '.product-image img',
link: 'a'
}
},
'article-list': {
container: '.article-list, .posts',
items: '.article, .post',
fields: {
title: 'h2, h3, .title',
summary: '.summary, .excerpt',
date: '.date, time',
author: '.author, .byline'
}
}
};
// 抓取数据
function scrapeData(ruleName) {
const rule = scrapeRules[ruleName];
if (!rule) return [];
const container = document.querySelector(rule.container);
if (!container) return [];
const items = container.querySelectorAll(rule.items);
const results = [];
items.forEach(item => {
const data = {};
Object.keys(rule.fields).forEach(field => {
const selector = rule.fields[field];
const element = item.querySelector(selector);
if (element) {
if (field === 'image') {
data[field] = element.src;
} else if (field === 'link') {
data[field] = element.href;
} else {
data[field] = <"blog.maicaixia.cn">element.textContent.trim();
}
}
});
if (Object.keys(data).length > 0) {
results.push(data);
}
});
return results;
}
// 导出数据
function exportData(data, format = 'json') {
let content, mimeType, filename;
switch (format) {
case 'json':
content = JSON.stringify(data, null, 2);
mimeType = 'application/json';
filename = 'data.json';
break;
case 'csv':
content = convertToCSV(data);
mimeType = 'text/csv';
filename = 'data.csv';
break;
case 'excel':
content = convertToExcel(data);
mimeType = 'application/vnd.ms-excel';
filename = 'data.xls';
break;
}
const blob = new Blob([content], { type: mimeType });
const url = URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = filename;
link.click();
URL.revokeObjectURL(url);
}
function convertToCSV(data) {
if (data.length === 0) return '';
const headers = Object.keys(data[0]);
const csvRows = [headers.join(',')];
data.forEach(row => {
const values = headers.map(header => {
const value = row[header] || '';
return `"${value.toString().replace(/"/g, '""')}"`;
});
csvRows.push(values.join(','));
});
return csvRows.join('\n');
}
// 添加快捷键
document.addEventListener('keydown', (e) => {
// Ctrl+Shift+S 抓取数据
if (e.ctrlKey && e.shiftKey && e.key === 'S') {
e.preventDefault();
const data = scrapeData('product-list');
exportData(data, 'json');
showNotification(`成功导出 ${data.length} 条数据`, 'success');
}
});
}
```
## 用户界面增强
### 自定义控制面板
为脚本添加可视化控制界面。
```javascript
// 创建控制面板
function createControlPanel() {
// 检查是否已存在面板
if (document.getElementById('tm-control-panel')) {
return;
}
const panel = document.createElement('div');
panel.id = 'tm-control-panel';
panel.innerHTML = `
<div class=<"key.maicaixia.cn">"tm-panel-header">
<h3>脚本控制面板</h3>
<button class="tm-close-btn">×</button>
</div>
<div class="tm-panel-body">
<div class="tm-control-group">
<label>
<input type="checkbox" id="tm-auto-refresh"> 自动刷新
</label>
<label>
<input type="checkbox" id="tm-auto-fill" checked> 自动填充
</label>
<label>
<input type="checkbox" id="tm-remove-ads" checked> 移除广告
</label>
</div>
<div class="tm-control-group">
<button id="tm-export-data" class="tm-btn">导出数据</button>
<button id="tm-clear-data" class="tm-btn">清除数据</button>
</div>
<div class="tm-control-group">
<label>刷新间隔:</label>
<select id="tm-refresh-interval">
<option value="30000">30秒</option>
<option value="60000">1分钟</option>
<option value="300000">5分钟</option>
</select>
</div>
</div>
`;
// 添加样式
const style = document.createElement('style');
style.textContent = `
#tm-control-panel {
position: fixed;
top: 20px;
right: 20px;
width: 300px;
background: white;
border: 1px solid #ccc;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
z-index: 10000;
font-family: Arial, sans-serif;
}
.tm-panel-header {
background: #f5f5f5;
padding: 10px;
border-bottom: 1px solid #ddd;
display: flex;
justify-content: space-between;
align-items: center;
}
.tm-panel-header h3 {
margin: 0;
font-size: 14px;
}
.tm-close-btn {
background: none;
border: none;
font-size: 18px;
cursor: pointer;
}
.tm-panel-body {
padding: 15px;
}
.tm-control-group {
margin-bottom: 15px;
}
.tm-control-group label {
display: block;
margin-bottom: 8px;
cursor: pointer;
}
.tm-btn {
background: #007cba;
color: white;
border: none;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
margin-right: <"lmd.maicaixia.cn">10px;
}
.tm-btn:hover {
background: #005a87;
}
`;
document.head.appendChild(style);
document.body.appendChild(panel);
// 事件处理
setupPanelEvents(panel);
}
function setupPanelEvents(panel) {
// 关闭按钮
panel.querySelector('.tm-close-btn').addEventListener('click', () => {
panel.style.display = 'none';
});
// 复选框事件
panel.querySelector('#tm-auto-refresh').addEventListener('change', (e) => {
GM_setValue('autoRefresh', e.target.checked);
});
panel.querySelector('#tm-auto-fill').addEventListener('change', (e) => {
GM_setValue('autoFill', e.target.checked);
});
// 按钮事件
panel.querySelector('#tm-export-data').addEventListener('click', () => {
// 导出数据逻辑
showNotification('数据导出功能', 'info');
});
// 恢复设置
restorePanelSettings(panel);
}
function restorePanelSettings(panel) {
panel.querySelector('#tm-auto-refresh').checked = GM_getValue('autoRefresh', false);
panel.querySelector('#tm-auto-fill').checked = GM_getValue('autoFill', true);
panel.querySelector('#tm-remove-ads').checked = GM_getValue('removeAds', true);
}
```
## 实用工具函数
### 通用辅助函数
```javascript
// 工具函数集合
const TMUtils = {
// 显示通知
showNotification(message, type = 'info', duration = 3000) {
GM_notification({
text: message,
title: '油猴脚本提示',
image: type === 'success' ? 'https://example.com/success.png' :
type === 'error' ? 'https://example.com/error.png' :
'https://example.com/info.png',
timeout: duration
});
},
// 防抖函数
debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
},
// 节流函数
throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
},
// 等待函数
wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
},
// 安全查询选择器
safeQuery(selector, context = document) {
try {
return context.querySelector(selector);
} catch (error) {
console.error('选择器错误:', selector, error);
return null;
}
},
// 生成随机ID
generateId() {
return Math.random().toString(36).substr(2, 9);
}
};
// 键盘快捷键管理
class ShortcutManager {
constructor() {
this.shortcuts = new Map();
this.setupGlobalListener();
}
register(keys, callback, description = '') {
this.shortcuts.set(keys, { callback, description });
}
setupGlobalListener() {
document.addEventListener('keydown', (e) => {
const key = this.getKeyString(e);
for (const [keys, { callback }] of this.shortcuts) {
if (keys === key) {
e.preventDefault();
callback();
break;
}
}
});
}
getKeyString(e) {
const parts = [];
if (e.ctrlKey) parts.push('ctrl');
if (e.shiftKey) parts.push('shift');
if (e.altKey) parts.push('alt');
parts.push(e.key.toLowerCase());
return parts.join('+');
}
}
// 初始化快捷键管理器
const shortcutManager = new ShortcutManager();
// 注册常用快捷键
shortcutManager.register('ctrl+shift+p', () => {
createControlPanel();
}, '打开控制面板');
shortcutManager.register('ctrl+shift+e', () => {
const data = scrapeData('product-list');
exportData(data, 'json');
}, '导出数据');
```
## 脚本部署与维护
### 开发与调试技巧
```javascript
// 调试工具
function setupDebugTools() {
// 只在开发环境启用
if (!GM_getValue('debugMode', false)) return;
// 添加调试面板
const debugPanel = document.createElement('div');
debugPanel.style.cssText = `
position: fixed;
bottom: 10px;
right: 10px;
background: rgba(0,0,0,0.8);
color: white;
padding: 10px;
border-radius: 5px;
font-family: monospace;
font-size: 12px;
z-index: 9999;
`;
function updateDebugInfo() {
debugPanel.innerHTML = `
URL: ${location.href}<br>
脚本状态: 运行中<br>
内存使用: ${Math.round(performance.memory.usedJSHeapSize / 1024 / 1024)}MB<br>
时间: ${new Date().toLocaleTimeString()}
`;
}
setInterval(updateDebugInfo, 1000);
document.body.appendChild(debugPanel);
}
// 错误监控
window.addEventListener('error', (e) => {
console.error('油猴脚本错误:', e.error);
GM_setValue('lastError', {
message: e.error.message,
stack: e.error.stack,
timestamp: new Date().toISOString()
});
});
// 性能监控
const scriptStartTime = Date.now();
window.addEventListener('beforeunload', () => {
const duration = Date.now() - scriptStartTime;
console.log(`脚本运行时长: ${duration}ms`);
});
```
## 结语
油猴脚本为浏览器自动化提供了强大的能力,从简单的表单填充到复杂的数据抓取,都能通过脚本实现。掌握油猴脚本开发不仅能够提升个人浏览体验,还能在工作和学习中创造巨大的效率提升。
随着对脚本开发的深入理解,读者可以进一步探索更复杂的应用场景,如API集成、数据处理、用户界面定制等,让浏览器真正成为个性化的生产力工具。