@dyyz1993/agent-browser 0.11.5 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/agent-browser-darwin-arm64 +0 -0
- package/dist/__tests__/utils/parseCli.d.ts.map +1 -1
- package/dist/__tests__/utils/parseCli.js +97 -2
- package/dist/__tests__/utils/parseCli.js.map +1 -1
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +117 -81
- package/dist/actions.js.map +1 -1
- package/dist/browser.d.ts +1 -0
- package/dist/browser.d.ts.map +1 -1
- package/dist/browser.js +24 -29
- package/dist/browser.js.map +1 -1
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +102 -3
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/connection.d.ts.map +1 -1
- package/dist/cli/connection.js +12 -29
- package/dist/cli/connection.js.map +1 -1
- package/dist/cli/help.d.ts.map +1 -1
- package/dist/cli/help.js +35 -25
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/output.d.ts.map +1 -1
- package/dist/cli/output.js +3 -0
- package/dist/cli/output.js.map +1 -1
- package/dist/cli.js +117 -3
- package/dist/cli.js.map +1 -1
- package/dist/daemon.d.ts +18 -2
- package/dist/daemon.d.ts.map +1 -1
- package/dist/daemon.js +46 -32
- package/dist/daemon.js.map +1 -1
- package/dist/message-bridge.d.ts.map +1 -1
- package/dist/message-bridge.js +4 -1
- package/dist/message-bridge.js.map +1 -1
- package/dist/rc-config.d.ts +42 -0
- package/dist/rc-config.d.ts.map +1 -0
- package/dist/rc-config.js +170 -0
- package/dist/rc-config.js.map +1 -0
- package/dist/recorder/inject.js +30 -24
- package/package.json +1 -1
- package/scripts/check_goods_container.js +35 -0
- package/scripts/check_page_content.js +36 -0
- package/scripts/click_applause_rate.js +30 -0
- package/scripts/explore_jd_page.js +31 -0
- package/scripts/extract_all_jd_data.js +80 -0
- package/scripts/extract_jd_product_detail.js +62 -0
- package/scripts/extract_jd_products_correct_links.js +78 -0
- package/scripts/extract_jd_products_final.js +80 -0
- package/scripts/extract_jd_reviews.js +48 -0
- package/scripts/extract_jd_seafood_final.js +78 -0
- package/scripts/extract_multiple_products.js +77 -0
- package/scripts/extract_products_no_scroll.js +68 -0
- package/scripts/extract_products_simple.js +68 -0
- package/scripts/find_applause_rate.js +26 -0
- package/scripts/find_jd_links.js +28 -0
- package/scripts/find_main_content.js +20 -0
- package/scripts/find_product_cards.js +38 -0
- package/scripts/find_root_content.js +26 -0
- package/scripts/find_unique_products.js +55 -0
- package/scripts/generate-skill.cjs +303 -0
- package/scripts/get_jd_product_detail.js +16 -0
- package/scripts/get_jd_products.js +23 -0
- package/scripts/get_jd_seafood_products.js +44 -0
- package/scripts/get_product_details_from_images.js +54 -0
- package/scripts/verify-form.sh +67 -0
- package/scripts/verify-login.sh +65 -0
- package/scripts/verify-recording.sh +80 -0
- package/scripts/verify-upload.sh +41 -0
- package/skills/agent-browser/SKILL.md +135 -370
- package/bin/agent-browser-linux-x64 +0 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
export const CONFIG_DIR = path.join(os.homedir(), '.agent-browser');
|
|
5
|
+
export const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
6
|
+
export const CONFIG_KEY_MAP = {
|
|
7
|
+
'viewer.host': {
|
|
8
|
+
path: ['viewer', 'host'],
|
|
9
|
+
description: 'Viewer URL host (e.g., https://viewer.example.com:8443)',
|
|
10
|
+
},
|
|
11
|
+
'viewer.port': {
|
|
12
|
+
path: ['viewer', 'port'],
|
|
13
|
+
description: 'Stream Server port (default: 5005)',
|
|
14
|
+
},
|
|
15
|
+
'messageBridge.url': {
|
|
16
|
+
path: ['messageBridge', 'url'],
|
|
17
|
+
description: 'Message Bridge URL for ask command',
|
|
18
|
+
},
|
|
19
|
+
'browser.executablePath': {
|
|
20
|
+
path: ['browser', 'executablePath'],
|
|
21
|
+
description: 'Browser executable path (e.g., /Applications/Chromium.app/Contents/MacOS/Chromium)',
|
|
22
|
+
},
|
|
23
|
+
'proxy.url': {
|
|
24
|
+
path: ['proxy', 'url'],
|
|
25
|
+
description: 'Proxy server URL for browser',
|
|
26
|
+
},
|
|
27
|
+
'messageProxy.url': {
|
|
28
|
+
path: ['messageProxy', 'url'],
|
|
29
|
+
description: 'Proxy URL for Message Bridge requests (HTTP_PROXY/HTTPS_PROXY)',
|
|
30
|
+
},
|
|
31
|
+
};
|
|
32
|
+
export function loadConfig() {
|
|
33
|
+
try {
|
|
34
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
const raw = fs.readFileSync(CONFIG_FILE, 'utf-8');
|
|
38
|
+
return JSON.parse(raw);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function saveConfig(config) {
|
|
45
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
46
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
49
|
+
}
|
|
50
|
+
export function getConfigValue(key) {
|
|
51
|
+
const mapping = CONFIG_KEY_MAP[key];
|
|
52
|
+
if (!mapping)
|
|
53
|
+
return undefined;
|
|
54
|
+
const config = loadConfig();
|
|
55
|
+
let current = config;
|
|
56
|
+
for (const segment of mapping.path) {
|
|
57
|
+
if (current === null || current === undefined || typeof current !== 'object') {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
current = current[segment];
|
|
61
|
+
}
|
|
62
|
+
return current;
|
|
63
|
+
}
|
|
64
|
+
export function setConfigValue(key, value) {
|
|
65
|
+
const mapping = CONFIG_KEY_MAP[key];
|
|
66
|
+
if (!mapping)
|
|
67
|
+
return false;
|
|
68
|
+
const config = loadConfig();
|
|
69
|
+
let current = config;
|
|
70
|
+
for (let i = 0; i < mapping.path.length - 1; i++) {
|
|
71
|
+
const segment = mapping.path[i];
|
|
72
|
+
if (!current[segment] || typeof current[segment] !== 'object') {
|
|
73
|
+
current[segment] = {};
|
|
74
|
+
}
|
|
75
|
+
current = current[segment];
|
|
76
|
+
}
|
|
77
|
+
const lastKey = mapping.path[mapping.path.length - 1];
|
|
78
|
+
if (key === 'viewer.port') {
|
|
79
|
+
const num = parseInt(value, 10);
|
|
80
|
+
if (isNaN(num) || num <= 0 || num > 65535)
|
|
81
|
+
return false;
|
|
82
|
+
current[lastKey] = num;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
current[lastKey] = value;
|
|
86
|
+
}
|
|
87
|
+
saveConfig(config);
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
export function getEffectiveValue(key) {
|
|
91
|
+
const envMap = {
|
|
92
|
+
'viewer.host': 'AGENT_BROWSER_VIEWER_HOST',
|
|
93
|
+
'viewer.port': 'AGENT_BROWSER_STREAM_PORT',
|
|
94
|
+
'messageBridge.url': 'MESSAGE_BRIDGE_URL',
|
|
95
|
+
'browser.executablePath': 'AGENT_BROWSER_EXECUTABLE_PATH',
|
|
96
|
+
'proxy.url': 'AGENT_BROWSER_PROXY',
|
|
97
|
+
'messageProxy.url': 'HTTPS_PROXY',
|
|
98
|
+
};
|
|
99
|
+
const envKey = envMap[key];
|
|
100
|
+
if (envKey && process.env[envKey]) {
|
|
101
|
+
const val = process.env[envKey];
|
|
102
|
+
if (key === 'viewer.port') {
|
|
103
|
+
const num = parseInt(val, 10);
|
|
104
|
+
if (!isNaN(num))
|
|
105
|
+
return num;
|
|
106
|
+
}
|
|
107
|
+
return val;
|
|
108
|
+
}
|
|
109
|
+
return getConfigValue(key);
|
|
110
|
+
}
|
|
111
|
+
export function getViewerHost() {
|
|
112
|
+
return getEffectiveValue('viewer.host') || 'http://localhost';
|
|
113
|
+
}
|
|
114
|
+
export function getViewerPort() {
|
|
115
|
+
const val = getEffectiveValue('viewer.port');
|
|
116
|
+
if (typeof val === 'number')
|
|
117
|
+
return val;
|
|
118
|
+
const parsed = parseInt(String(val || '5005'), 10);
|
|
119
|
+
return isNaN(parsed) ? 5005 : parsed;
|
|
120
|
+
}
|
|
121
|
+
export function getViewerUrl(instanceId) {
|
|
122
|
+
const host = getViewerHost();
|
|
123
|
+
const port = getViewerPort();
|
|
124
|
+
if (host.includes('://')) {
|
|
125
|
+
return `${host}/view?instanceId=${instanceId}`;
|
|
126
|
+
}
|
|
127
|
+
return `http://${host}:${port}/view?instanceId=${instanceId}`;
|
|
128
|
+
}
|
|
129
|
+
export function getViewerWsUrl(instanceId) {
|
|
130
|
+
const host = getViewerHost();
|
|
131
|
+
const port = getViewerPort();
|
|
132
|
+
if (host.startsWith('https://')) {
|
|
133
|
+
return `wss://${host.replace('https://', '')}/?instanceId=${instanceId}`;
|
|
134
|
+
}
|
|
135
|
+
if (host.startsWith('http://')) {
|
|
136
|
+
return `ws://${host.replace('http://', '')}/?instanceId=${instanceId}`;
|
|
137
|
+
}
|
|
138
|
+
return `ws://${host}:${port}/?instanceId=${instanceId}`;
|
|
139
|
+
}
|
|
140
|
+
export function getMessageBridgeUrl() {
|
|
141
|
+
return getEffectiveValue('messageBridge.url') || 'https://message-bridge.docker.19930810.xyz:8443';
|
|
142
|
+
}
|
|
143
|
+
export function getExecutablePath() {
|
|
144
|
+
return getEffectiveValue('browser.executablePath');
|
|
145
|
+
}
|
|
146
|
+
export function isViewerConfigured() {
|
|
147
|
+
return getEffectiveValue('viewer.host') !== undefined;
|
|
148
|
+
}
|
|
149
|
+
export function isMessageBridgeConfigured() {
|
|
150
|
+
return getEffectiveValue('messageBridge.url') !== undefined;
|
|
151
|
+
}
|
|
152
|
+
export function formatTips(command) {
|
|
153
|
+
const tips = [];
|
|
154
|
+
if (command === 'viewer' && !isViewerConfigured()) {
|
|
155
|
+
tips.push('');
|
|
156
|
+
tips.push('[Tip] Viewer host not configured. Using default (localhost).');
|
|
157
|
+
tips.push(' To set a custom viewer host, run:');
|
|
158
|
+
tips.push(' agent-browser config set viewer.host https://viewer.example.com:8443');
|
|
159
|
+
tips.push(' Or set environment variable: AGENT_BROWSER_VIEWER_HOST');
|
|
160
|
+
}
|
|
161
|
+
if (command === 'ask' && !isMessageBridgeConfigured()) {
|
|
162
|
+
tips.push('');
|
|
163
|
+
tips.push('[Tip] Message Bridge URL not configured. Using default.');
|
|
164
|
+
tips.push(' To set a custom Message Bridge URL, run:');
|
|
165
|
+
tips.push(' agent-browser config set messageBridge.url https://your-bridge.example.com:8443');
|
|
166
|
+
tips.push(' Or set environment variable: MESSAGE_BRIDGE_URL');
|
|
167
|
+
}
|
|
168
|
+
return tips;
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=rc-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rc-config.js","sourceRoot":"","sources":["../src/rc-config.ts"],"names":[],"mappings":"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"}
|
package/dist/recorder/inject.js
CHANGED
|
@@ -925,6 +925,23 @@
|
|
|
925
925
|
return;
|
|
926
926
|
}
|
|
927
927
|
|
|
928
|
+
const tag = element.tagName;
|
|
929
|
+
const inputType = (element.type || '').toLowerCase();
|
|
930
|
+
|
|
931
|
+
if (tag === 'INPUT' && (inputType === 'checkbox' || inputType === 'radio')) {
|
|
932
|
+
const isChecked = element.checked;
|
|
933
|
+
const action = inputType === 'radio' ? 'check' : (isChecked ? 'check' : 'uncheck');
|
|
934
|
+
recordStep(action, {
|
|
935
|
+
selector: getSelector(element),
|
|
936
|
+
xpath: getXPath(element),
|
|
937
|
+
elementInfo: getElementInfo(element)
|
|
938
|
+
});
|
|
939
|
+
if (!HIDE_UI && !isInIframe && typeof addMarker === 'function') {
|
|
940
|
+
addMarker(element, action);
|
|
941
|
+
}
|
|
942
|
+
return;
|
|
943
|
+
}
|
|
944
|
+
|
|
928
945
|
recordStep('click', {
|
|
929
946
|
selector: getSelector(element),
|
|
930
947
|
xpath: getXPath(element),
|
|
@@ -1023,7 +1040,7 @@
|
|
|
1023
1040
|
|
|
1024
1041
|
document.addEventListener('keydown', (e) => {
|
|
1025
1042
|
const element = document.activeElement;
|
|
1026
|
-
|
|
1043
|
+
|
|
1027
1044
|
if (isInPanel(element)) return;
|
|
1028
1045
|
|
|
1029
1046
|
const specialKeys = ['Enter', 'Tab', 'Escape', 'Backspace', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];
|
|
@@ -1033,7 +1050,7 @@
|
|
|
1033
1050
|
return;
|
|
1034
1051
|
}
|
|
1035
1052
|
|
|
1036
|
-
|
|
1053
|
+
|
|
1037
1054
|
recordStep('keyboard', {
|
|
1038
1055
|
key: e.key,
|
|
1039
1056
|
code: e.code,
|
|
@@ -1123,18 +1140,18 @@
|
|
|
1123
1140
|
|
|
1124
1141
|
window.xyzInited = false;
|
|
1125
1142
|
window.xyzQueue = [];
|
|
1126
|
-
|
|
1143
|
+
|
|
1127
1144
|
};
|
|
1128
1145
|
|
|
1129
1146
|
// 检查录制会话是否已停止
|
|
1130
1147
|
if (window.xyzStopped) {
|
|
1131
|
-
|
|
1148
|
+
|
|
1132
1149
|
return;
|
|
1133
1150
|
}
|
|
1134
1151
|
|
|
1135
1152
|
// 检查录制会话是否激活
|
|
1136
1153
|
if (!window.xyzActive) {
|
|
1137
|
-
|
|
1154
|
+
|
|
1138
1155
|
return;
|
|
1139
1156
|
}
|
|
1140
1157
|
|
|
@@ -1196,6 +1213,8 @@
|
|
|
1196
1213
|
.xyzStp.fill { border-left-color: #2196F3; }
|
|
1197
1214
|
.xyzStp.select { border-left-color: #FF9800; }
|
|
1198
1215
|
.xyzStp.link_click { border-left-color: #9C27B0; }
|
|
1216
|
+
.xyzStp.check { border-left-color: #4CAF50; }
|
|
1217
|
+
.xyzStp.uncheck { border-left-color: #FF5722; }
|
|
1199
1218
|
.xyzStp.navigate { border-left-color: #607D8B; }
|
|
1200
1219
|
.xyzStp.annotate { border-left-color: #E91E63; }
|
|
1201
1220
|
.xyzStp .action { font-weight: 500; color: #333; }
|
|
@@ -1761,11 +1780,11 @@
|
|
|
1761
1780
|
// 延迟创建面板
|
|
1762
1781
|
setTimeout(() => {
|
|
1763
1782
|
if (window.xyzStopped) {
|
|
1764
|
-
|
|
1783
|
+
|
|
1765
1784
|
return;
|
|
1766
1785
|
}
|
|
1767
1786
|
if (!window.xyzActive) {
|
|
1768
|
-
|
|
1787
|
+
|
|
1769
1788
|
return;
|
|
1770
1789
|
}
|
|
1771
1790
|
createRecorderOverlay();
|
|
@@ -1786,7 +1805,7 @@
|
|
|
1786
1805
|
panelObserver = new MutationObserver((mutations) => {
|
|
1787
1806
|
// 检查录制会话是否已停止
|
|
1788
1807
|
if (window.xyzStopped) {
|
|
1789
|
-
|
|
1808
|
+
|
|
1790
1809
|
if (typeof window.xyzClose === 'function') {
|
|
1791
1810
|
window.xyzClose();
|
|
1792
1811
|
}
|
|
@@ -1799,7 +1818,7 @@
|
|
|
1799
1818
|
|
|
1800
1819
|
// 检查录制会话是否激活
|
|
1801
1820
|
if (!window.xyzActive) {
|
|
1802
|
-
|
|
1821
|
+
|
|
1803
1822
|
return;
|
|
1804
1823
|
}
|
|
1805
1824
|
|
|
@@ -1807,7 +1826,7 @@
|
|
|
1807
1826
|
const panel = document.getElementById('xyzPnl');
|
|
1808
1827
|
const style = document.getElementById('xyzSt');
|
|
1809
1828
|
if (document.body && (!panel || !style)) {
|
|
1810
|
-
|
|
1829
|
+
|
|
1811
1830
|
createRecorderOverlay();
|
|
1812
1831
|
}
|
|
1813
1832
|
});
|
|
@@ -1818,21 +1837,8 @@
|
|
|
1818
1837
|
subtree: false
|
|
1819
1838
|
});
|
|
1820
1839
|
|
|
1821
|
-
console.log('[Panel] MutationObserver started, watching for panel removal');
|
|
1822
|
-
}
|
|
1823
1840
|
|
|
1824
|
-
|
|
1825
|
-
setTimeout(() => {
|
|
1826
|
-
if (window.xyzStopped) {
|
|
1827
|
-
console.log('[Panel] Session was stopped during init, skipping panel creation');
|
|
1828
|
-
return;
|
|
1829
|
-
}
|
|
1830
|
-
if (!window.xyzActive) {
|
|
1831
|
-
console.log('[Panel] Session not active during init, skipping panel creation');
|
|
1832
|
-
return;
|
|
1833
|
-
}
|
|
1834
|
-
createRecorderOverlay();
|
|
1835
|
-
}, 0);
|
|
1841
|
+
}
|
|
1836
1842
|
|
|
1837
1843
|
// 启动 MutationObserver
|
|
1838
1844
|
startPanelObserver();
|
package/package.json
CHANGED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
const result = {
|
|
3
|
+
goodsContainerCount: 0,
|
|
4
|
+
sampleContainer: null,
|
|
5
|
+
allClasses: []
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const containers = document.querySelectorAll('._goodsContainer_1p2ae_1');
|
|
9
|
+
result.goodsContainerCount = containers.length;
|
|
10
|
+
|
|
11
|
+
if (containers.length > 0) {
|
|
12
|
+
result.sampleContainer = {
|
|
13
|
+
className: containers[0].className,
|
|
14
|
+
innerHTML: containers[0].innerHTML.substring(0, 500),
|
|
15
|
+
text: containers[0].textContent?.substring(0, 200)
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const allDivs = document.querySelectorAll('div');
|
|
20
|
+
const classSet = new Set();
|
|
21
|
+
allDivs.forEach(div => {
|
|
22
|
+
if (div.className) {
|
|
23
|
+
const classes = String(div.className).split(' ');
|
|
24
|
+
classes.forEach(c => {
|
|
25
|
+
if (c.includes('goods') || c.includes('item') || c.includes('product')) {
|
|
26
|
+
classSet.add(c);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
result.allClasses = Array.from(classSet).slice(0, 20);
|
|
33
|
+
|
|
34
|
+
return result;
|
|
35
|
+
})()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
const result = {
|
|
3
|
+
visibleElements: [],
|
|
4
|
+
linksWithProductInClass: []
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const allElements = document.querySelectorAll('*');
|
|
8
|
+
let count = 0;
|
|
9
|
+
|
|
10
|
+
for (const el of allElements) {
|
|
11
|
+
if (count >= 50) break;
|
|
12
|
+
|
|
13
|
+
const className = el.className ? String(el.className) : '';
|
|
14
|
+
const text = el.textContent?.trim() || '';
|
|
15
|
+
|
|
16
|
+
if ((className.includes('product') || className.includes('item') || className.includes('card')) && text.length > 10) {
|
|
17
|
+
result.visibleElements.push({
|
|
18
|
+
tagName: el.tagName,
|
|
19
|
+
className: className.substring(0, 100),
|
|
20
|
+
text: text.substring(0, 100)
|
|
21
|
+
});
|
|
22
|
+
count++;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const productLinks = document.querySelectorAll('a[href*="item.jd"], a[href*="product.jd"]');
|
|
27
|
+
productLinks.forEach((link, index) => {
|
|
28
|
+
if (index >= 10) return;
|
|
29
|
+
result.linksWithProductInClass.push({
|
|
30
|
+
href: link.href.substring(0, 100),
|
|
31
|
+
text: link.textContent?.substring(0, 50)
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return result;
|
|
36
|
+
})()
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
(async () => {
|
|
2
|
+
const applauseRateEl = document.querySelector('.applause-rate');
|
|
3
|
+
|
|
4
|
+
if (applauseRateEl) {
|
|
5
|
+
applauseRateEl.click();
|
|
6
|
+
|
|
7
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
8
|
+
|
|
9
|
+
const result = {
|
|
10
|
+
elementFound: true,
|
|
11
|
+
elementText: applauseRateEl.textContent,
|
|
12
|
+
popup: null
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const popup = document.querySelector('[class*="popup"], [class*="modal"], [class*="dialog"]');
|
|
16
|
+
if (popup) {
|
|
17
|
+
result.popup = {
|
|
18
|
+
className: popup.className,
|
|
19
|
+
text: popup.textContent?.substring(0, 500)
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return result;
|
|
24
|
+
} else {
|
|
25
|
+
return {
|
|
26
|
+
elementFound: false,
|
|
27
|
+
message: 'applause-rate element not found'
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
})()
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
const result = {
|
|
3
|
+
productContainers: [],
|
|
4
|
+
sampleProduct: null
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
const containers = document.querySelectorAll('[class*="product"], [class*="item"], [class*="card"]');
|
|
8
|
+
|
|
9
|
+
containers.forEach((el, index) => {
|
|
10
|
+
if (index < 5) {
|
|
11
|
+
result.productContainers.push({
|
|
12
|
+
className: el.className,
|
|
13
|
+
tagName: el.tagName,
|
|
14
|
+
innerHTML: el.innerHTML.substring(0, 200)
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const links = document.querySelectorAll('a[href*="item.jd"]');
|
|
20
|
+
if (links.length > 0) {
|
|
21
|
+
const parent = links[0].closest('[class*="product"], [class*="item"], [class*="card"]');
|
|
22
|
+
if (parent) {
|
|
23
|
+
result.sampleProduct = {
|
|
24
|
+
className: parent.className,
|
|
25
|
+
innerHTML: parent.innerHTML.substring(0, 500)
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return result;
|
|
31
|
+
})()
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
const result = {
|
|
3
|
+
url: window.location.href,
|
|
4
|
+
title: document.title,
|
|
5
|
+
basic: {
|
|
6
|
+
title: '',
|
|
7
|
+
shop: '',
|
|
8
|
+
price: '',
|
|
9
|
+
sales: ''
|
|
10
|
+
},
|
|
11
|
+
reviews: {
|
|
12
|
+
total: '',
|
|
13
|
+
good_percent: '',
|
|
14
|
+
good: '',
|
|
15
|
+
neutral: '',
|
|
16
|
+
bad: ''
|
|
17
|
+
},
|
|
18
|
+
region: ''
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const allText = document.body.textContent;
|
|
22
|
+
|
|
23
|
+
const titleMatch = allText.match(/首鲜道斑节虾[^¥]{0,200}/);
|
|
24
|
+
if (titleMatch) {
|
|
25
|
+
result.basic.title = titleMatch[0].trim().substring(0, 100);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const shopMatch = allText.match(/首鲜道旗舰店/);
|
|
29
|
+
if (shopMatch) {
|
|
30
|
+
result.basic.shop = shopMatch[0];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const priceMatch = allText.match(/[¥¥]\s*(\d+\.?\d*)/);
|
|
34
|
+
if (priceMatch) {
|
|
35
|
+
result.basic.price = priceMatch[1];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const totalReviewMatch = allText.match(/买家评价[((](\d+[+万])[))]/);
|
|
39
|
+
if (totalReviewMatch) {
|
|
40
|
+
result.reviews.total = totalReviewMatch[1];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const goodPercentMatch = allText.match(/超(\d+)%买家赞不绝口/);
|
|
44
|
+
if (goodPercentMatch) {
|
|
45
|
+
result.reviews.good_percent = goodPercentMatch[1];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const regionMatch = allText.match(/[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼]\s*[^\s]{0,10}(?:市|省|区|县|街道)/);
|
|
49
|
+
if (regionMatch) {
|
|
50
|
+
result.region = regionMatch[0].trim();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const filterItems = document.querySelectorAll('[class*="filter-item"], [class*="comment-item"]');
|
|
54
|
+
filterItems.forEach(item => {
|
|
55
|
+
const text = item.textContent || '';
|
|
56
|
+
|
|
57
|
+
if (text.includes('好评') || text.includes('positive')) {
|
|
58
|
+
const match = text.match(/(\d+)/);
|
|
59
|
+
if (match && !result.reviews.good) {
|
|
60
|
+
result.reviews.good = match[1];
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (text.includes('中评') || text.includes('neutral')) {
|
|
65
|
+
const match = text.match(/(\d+)/);
|
|
66
|
+
if (match && !result.reviews.neutral) {
|
|
67
|
+
result.reviews.neutral = match[1];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (text.includes('差评') || text.includes('negative')) {
|
|
72
|
+
const match = text.match(/(\d+)/);
|
|
73
|
+
if (match && !result.reviews.bad) {
|
|
74
|
+
result.reviews.bad = match[1];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return result;
|
|
80
|
+
})()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
const result = {
|
|
3
|
+
url: window.location.href,
|
|
4
|
+
title: document.title,
|
|
5
|
+
price: '',
|
|
6
|
+
shop: '',
|
|
7
|
+
sales: '',
|
|
8
|
+
reviews: {
|
|
9
|
+
good: '',
|
|
10
|
+
neutral: '',
|
|
11
|
+
bad: ''
|
|
12
|
+
},
|
|
13
|
+
region: ''
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const priceMatch = document.body.textContent.match(/[¥¥]\s*(\d+\.?\d*)/);
|
|
17
|
+
if (priceMatch) {
|
|
18
|
+
result.price = priceMatch[1];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const shopMatch = document.body.textContent.match(/([^\s]{2,10}(?:旗舰店|专营店|京东自营))/);
|
|
22
|
+
if (shopMatch) {
|
|
23
|
+
result.shop = shopMatch[1];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const reviewElements = document.querySelectorAll('[class*="comment"], [class*="review"], [class*="rating"]');
|
|
27
|
+
|
|
28
|
+
reviewElements.forEach(el => {
|
|
29
|
+
const text = el.textContent || '';
|
|
30
|
+
|
|
31
|
+
if (text.includes('好评') && !result.reviews.good) {
|
|
32
|
+
const match = text.match(/好评[::]\s*(\d+)/);
|
|
33
|
+
if (match) {
|
|
34
|
+
result.reviews.good = match[1];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (text.includes('中评') && !result.reviews.neutral) {
|
|
39
|
+
const match = text.match(/中评[::]\s*(\d+)/);
|
|
40
|
+
if (match) {
|
|
41
|
+
result.reviews.neutral = match[1];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (text.includes('差评') && !result.reviews.bad) {
|
|
46
|
+
const match = text.match(/差评[::]\s*(\d+)/);
|
|
47
|
+
if (match) {
|
|
48
|
+
result.reviews.bad = match[1];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const regionElements = document.querySelectorAll('[class*="address"], [class*="region"], [class*="location"]');
|
|
54
|
+
regionElements.forEach(el => {
|
|
55
|
+
const text = el.textContent || '';
|
|
56
|
+
if (!result.region && text.length > 0 && text.length < 100) {
|
|
57
|
+
result.region = text.trim();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return result;
|
|
62
|
+
})()
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
(async () => {
|
|
2
|
+
const result = {
|
|
3
|
+
keyword: "生鲜海鲜",
|
|
4
|
+
crawlTime: new Date().toISOString(),
|
|
5
|
+
products: []
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
window.scrollTo(0, 500);
|
|
9
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
10
|
+
|
|
11
|
+
window.scrollTo(0, 2000);
|
|
12
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
13
|
+
|
|
14
|
+
window.scrollTo(0, 4000);
|
|
15
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
16
|
+
|
|
17
|
+
const containers = document.querySelectorAll('._goodsContainer_1p2ae_1');
|
|
18
|
+
const seenLinks = new Set();
|
|
19
|
+
|
|
20
|
+
containers.forEach((container, index) => {
|
|
21
|
+
const link = container.querySelector('a[href]');
|
|
22
|
+
if (!link) return;
|
|
23
|
+
|
|
24
|
+
const href = link.href;
|
|
25
|
+
if (seenLinks.has(href)) return;
|
|
26
|
+
seenLinks.add(href);
|
|
27
|
+
|
|
28
|
+
const text = container.textContent || '';
|
|
29
|
+
const product = {
|
|
30
|
+
id: index + 1,
|
|
31
|
+
link: href
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const img = container.querySelector('img');
|
|
35
|
+
if (img) {
|
|
36
|
+
const src = img.getAttribute('data-src') || img.src;
|
|
37
|
+
if (src) product.image = src.substring(0, 150);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const priceMatch = text.match(/[¥¥]\s*(\d+\.?\d*)/);
|
|
41
|
+
if (priceMatch) {
|
|
42
|
+
product.price = priceMatch[1];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const salesMatch = text.match(/已售([\d\+万]+)/);
|
|
46
|
+
if (salesMatch) {
|
|
47
|
+
product.sales = salesMatch[1];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const shopMatch = text.match(/([^\s]{2,10}(?:旗舰店|专营店|京东自营))/);
|
|
51
|
+
if (shopMatch) {
|
|
52
|
+
product.shop = shopMatch[1];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const adIndex = text.indexOf('广告');
|
|
56
|
+
const titleStart = adIndex >= 0 ? adIndex + 2 : 0;
|
|
57
|
+
let title = text.substring(titleStart).trim();
|
|
58
|
+
|
|
59
|
+
title = title.replace(/已售[\d\+万]+/, '');
|
|
60
|
+
title = title.replace(/[\d\+万]+人加购/, '');
|
|
61
|
+
title = title.replace(/[\d\+万]+人种草/, '');
|
|
62
|
+
title = title.replace(/[\d\+万]+人浏览/, '');
|
|
63
|
+
title = title.replace(/[^\s]{2,10}(?:旗舰店|专营店|京东自营)/, '');
|
|
64
|
+
title = title.replace(/搜同款|关注|对比/g, '');
|
|
65
|
+
title = title.trim();
|
|
66
|
+
|
|
67
|
+
if (title.length > 5) {
|
|
68
|
+
product.title = title.substring(0, 100);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (product.title || product.price) {
|
|
72
|
+
result.products.push(product);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
result.total = result.products.length;
|
|
77
|
+
return result;
|
|
78
|
+
})()
|