根据你提供的”方案一”代码,它使用的是 Node.js 的 Express 框架 来构建的 Web 服务器。这是一个非常流行和灵活的 JavaScript 服务器框架。
🖥️ 核心组件与工作原理 方案一主要依赖以下核心库:
核心库
用途
版本建议
Express
作为Web应用框架,处理路由、中间件和HTTP请求/响应[citation:8]
^4.18.2
Axios
用于向亚马逊服务器发起HTTP请求,获取原始页面内容
^1.6.0
Node.js 内置模块 (如 http, url)
提供基础的HTTP服务器和URL解析能力[citation:8]
-
其工作原理如下图所示,可以看到一个请求是如何被处理的:
1 2 3 4 5 6 7 flowchart TD A[用户访问你的服务器] --> B[Express 接收请求] B --> C[Axios 向亚马逊请求原始页面] C --> D{是否是HTML?} D -- 是 --> E[正则匹配商品链接] E --> F[添加联盟Tag后返回] D -- 否 --> G[直接返回原始内容]
⚙️ 部署注意事项 要让这个服务器稳定运行,你需要:
安装依赖 :在放代码的目录下运行 npm install express axios。
启动服务 :运行 node server.js。生产环境建议使用 PM2 等进程管理器来守护进程和实现零停机重启。
反向代理(可选但推荐) :虽然你的代码本身是一个功能完整的Web服务器,但通常在生产环境中,强烈建议 在其前面搭配 Nginx [citation:5] 或 Caddy 这样的专业Web服务器作为反向代理。这样做能带来很多好处:
提升性能 :Nginx可以高效处理静态文件(如图片、CSS、JS)、SSL加密和解密,减轻Node.js进程的负担[citation:5]。
增强安全性 :隐藏后端Node.js服务器,提供额外的安全层。
提高可靠性 :可以做负载均衡,或者在你的Node.js应用意外崩溃时提供更优雅的错误处理。
一个简单的Nginx配置示例(/etc/nginx/sites-available/your-site)可能看起来像这样[citation:5]:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 server { listen 80 ; server_name your-domain.com; location / { proxy_pass http://localhost:3000; proxy_http_version 1 .1 ; proxy_set_header Upgrade $http_upgrade ; proxy_set_header Connection 'upgrade' ; proxy_set_header Host $host ; proxy_set_header X-Real-IP $remote_addr ; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ; proxy_set_header X-Forwarded-Proto $scheme ; proxy_cache_bypass $http_upgrade ; } }
记得配置后通过 sudo nginx -t 测试配置,并用 sudo systemctl reload nginx 重载Nginx。
⚠️ 重要提醒
正则表达式的局限性 :代码中通过正则表达式匹配商品链接(匹配包含 /dp/ 后接10位字母数字的模式)并添加 &tag=audio-media-21。这种方式可能不够健壮 ,如果亚马逊的URL结构发生变化,或者商品ASIN码长度不全是10位,可能会导致匹配失败。你需要定期测试和维护。
遵守联盟计划规则 :务必仔细阅读亚马逊联盟计划(Amazon Associates)的条款 。确保这种通过代理服务器自动添加tag的方式是允许的,避免违反规则导致账户被封。
性能考量 :此方案每次请求都会从亚马逊获取页面 ,可能会比较慢,并且对亚马逊的服务器造成一定压力。如果流量较大,需要考虑加入缓存机制(例如使用Redis或内存缓存一段时间内的页面),但同样需注意遵守亚马逊的条款。
💡 为什么不直接用Nginx? 你可能想知道为什么不能直接用Nginx这样的Web服务器实现。这是因为Nginx本身主要用于处理静态内容、作为反向代理[citation:5]或负载均衡器[citation:10] ,它不擅长直接执行业务逻辑 [citation:10]。而修改HTML内容、动态添加联盟Tag这种动态内容生成[citation:9]和复杂的文本处理任务,正是Node.js(配合Express)这类应用平台/运行时环境所擅长的 [citation:10]。通常的做法正是让Nginx作为反向代理,将需要特殊处理的请求转发给你的Node.js应用[citation:5]。
希望这些信息能帮你更好地理解和运行你的方案一。
时间 : 2025-09-10T09:33:43
👤 用户 你好,我在使用cloudflare的works做反向代理,我想把www.amazon.co.uk的站内商品链接都加上我的推荐AssociateTrackingID,就是在商品链接后面加上“&tag=audio-media-21”,帮我修改下代码。
// Cloudflare Worker 代码 - 多域名反向代理 export default { async fetch(request, env) { // 域名到目标服务器的映射 const domainMappings = { ‘test.thanx.top’: ‘http://106.15.4.153:8085 ‘, ‘api.thanx.top’: ‘http://106.15.4.153:8086 ‘, ‘admin.thanx.top’: ‘http://106.15.4.153:8087 ‘, ‘reverse-proxy.yuebinliu.workers.dev’: ‘https://www.amazon.co.uk/s?bbn=65801031&rh=n%3A11621430031&language=en_GB&_encoding=UTF8&brr=1 ‘ // 测试用 };
const url = new URL(request.url);
const target = domainMappings[url.hostname];
// 如果域名未配置,返回404
if (!target) {
return new Response('域名未配置: ' + url.hostname + '\n\n已配置的域名: ' + Object.keys(domainMappings).join(', '), {
status: 404,
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
});
}
// 准备转发请求
const targetUrl = new URL(url.pathname + url.search, target);
const newHeaders = new Headers(request.headers);
// 设置代理头
newHeaders.set('X-Forwarded-Host', url.hostname);
newHeaders.set('X-Forwarded-Proto', url.protocol.replace(':', ''));
newHeaders.set('X-Real-IP', request.headers.get('cf-connecting-ip') || 'unknown');
newHeaders.delete('host'); // 删除原始host头
try {
// 转发请求到目标服务器
let response = await fetch(targetUrl.toString(), {
method: request.method,
headers: newHeaders,
body: request.body,
redirect: 'manual' // 手动处理重定向
});
// 处理重定向响应
if ([301, 302, 303, 307, 308].includes(response.status)) {
const location = response.headers.get('location');
if (location && location.includes('106.15.4.153')) {
// 替换重定向地址中的原始IP为代理域名
const newLocation = location.replace(
/http:\/\/106\.15\.4\.153:\d+/g,
`https://${url.hostname}`
);
const newResponse = new Response(response.body, response);
newResponse.headers.set('location', newLocation);
return newResponse;
}
}
// 处理HTML内容,替换其中的链接
const contentType = response.headers.get('content-type') || '';
if (contentType.includes('text/html')) {
let html = await response.text();
// 替换所有原始服务器链接为代理域名
html = html.replace(
/http:\/\/106\.15\.4\.153:\d+/g,
`https://${url.hostname}`
);
// 返回修改后的HTML
return new Response(html, {
status: response.status,
headers: response.headers
});
}
// 对于非HTML内容,直接返回
return response;
} catch (error) {
// 错误处理
return new Response(`代理错误: ${error.message}\n\n目标URL: ${targetUrl.toString()}`, {
status: 502,
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
});
}
} }
时间 : 2025-09-09T22:08:43
🤖 DeepSeek (deepseek-chat) 我来帮你修改代码,在Amazon商品链接后添加推荐ID。以下是修改后的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 export default { async fetch (request, env ) { const domainMappings = { 'test.thanx.top' : 'http://106.15.4.153:8085' , 'api.thanx.top' : 'http://106.15.4.153:8086' , 'admin.thanx.top' : 'http://106.15.4.153:8087' , 'reverse-proxy.yuebinliu.workers.dev' : 'https://www.amazon.co.uk' }; const url = new URL (request.url ); const target = domainMappings[url.hostname ]; if (!target) { return new Response ('域名未配置: ' + url.hostname + '\n\n已配置的域名: ' + Object .keys (domainMappings).join (', ' ), { status : 404 , headers : { 'Content-Type' : 'text/plain; charset=utf-8' } }); } const targetUrl = new URL (url.pathname + url.search , target); const newHeaders = new Headers (request.headers ); newHeaders.set ('X-Forwarded-Host' , url.hostname ); newHeaders.set ('X-Forwarded-Proto' , url.protocol .replace (':' , '' )); newHeaders.set ('X-Real-IP' , request.headers .get ('cf-connecting-ip' ) || 'unknown' ); newHeaders.delete ('host' ); try { let response = await fetch (targetUrl.toString (), { method : request.method , headers : newHeaders, body : request.body , redirect : 'manual' }); if ([301 , 302 , 303 , 307 , 308 ].includes (response.status )) { const location = response.headers .get ('location' ); if (location && location.includes ('106.15.4.153' )) { const newLocation = location.replace ( /http:\/\/106\.15\.4\.153:\d+/g , `https://${url.hostname} ` ); const newResponse = new Response (response.body , response); newResponse.headers .set ('location' , newLocation); return newResponse; } } const contentType = response.headers .get ('content-type' ) || '' ; if (contentType.includes ('text/html' )) { let html = await response.text (); html = html.replace ( /http:\/\/106\.15\.4\.153:\d+/g , `https://${url.hostname} ` ); html = html.replace ( /(https?:\/\/www\.amazon\.co\.uk\/[^"']*?\/(dp|gp\/product)\/[A-Z0-9]{10})([^"']*)/gi , (match, baseUrl, pathType, queryString ) => { if (match.includes ('tag=' ) || match.includes ('&tag=' )) { return match; } const separator = queryString.includes ('?' ) ? '&' : '?' ; return `${baseUrl} ${queryString} ${separator} tag=audio-media-21` ; } ); return new Response (html, { status : response.status , headers : response.headers }); } return response; } catch (error) { return new Response (`代理错误: ${error.message} \n\n目标URL: ${targetUrl.toString()} ` , { status : 502 , headers : { 'Content-Type' : 'text/plain; charset=utf-8' } }); } } }
主要修改内容:
修改了Amazon的目标URL :从具体的搜索页面改为了根域名 https://www.amazon.co.uk,这样所有页面都能正常代理。
添加了推荐ID处理逻辑 :
使用正则表达式匹配Amazon商品链接(包含 /dp/ 或 /gp/product/ 的URL)
检查链接是否已经包含 tag 参数,避免重复添加
在链接末尾添加 &tag=audio-media-21 或 ?tag=audio-media-21(根据URL结构自动选择分隔符)
保持了原有的代理功能 :包括IP地址替换、重定向处理等。
这样配置后,所有通过你的代理访问的Amazon UK商品页面都会自动加上你的推荐ID。注意这个修改只会影响HTML内容中的链接,不会影响JavaScript动态生成的链接。
时间 : 2025-09-09T22:08:43
👤 用户 是这样的,如果用户通过test.thanx.top访问,那么就在我反向代理的amazon的页面里的商品链接直接添加&tag=audio-media-21,譬如“https://www.amazon.co.uk/Sexgene-Big-Thick-Dildo-Sex/dp/B0CLY3TD67/”修改成“https://www.amazon.co.uk/Sexgene-Big-Thick-Dildo-Sex/dp/B0CLY3TD67/&tag=audio-media-21”,不需要修改www.amazon.co.uk
// Cloudflare Worker 代码 - 多域名反向代理 export default { async fetch(request, env) { // 域名到目标服务器的映射 const domainMappings = { ‘test.thanx.top’: ‘https://www.amazon.co.uk ‘, ‘api.thanx.top’: ‘http://106.15.4.153:8086 ‘, ‘admin.thanx.top’: ‘http://106.15.4.153:8087 ‘, ‘reverse-proxy.yuebinliu.workers.dev’: ‘https://www.amazon.co.uk ‘ };
const url = new URL(request.url);
const target = domainMappings[url.hostname];
// 如果域名未配置,返回404
if (!target) {
return new Response('域名未配置: ' + url.hostname + '\n\n已配置的域名: ' + Object.keys(domainMappings).join(', '), {
status: 404,
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
});
}
// 准备转发请求
const targetUrl = new URL(url.pathname + url.search, target);
const newHeaders = new Headers(request.headers);
// 设置代理头
newHeaders.set('X-Forwarded-Host', url.hostname);
newHeaders.set('X-Forwarded-Proto', url.protocol.replace(':', ''));
newHeaders.set('X-Real-IP', request.headers.get('cf-connecting-ip') || 'unknown');
newHeaders.delete('host'); // 删除原始host头
try {
// 转发请求到目标服务器
let response = await fetch(targetUrl.toString(), {
method: request.method,
headers: newHeaders,
body: request.body,
redirect: 'manual' // 手动处理重定向
});
// 处理重定向响应
if ([301, 302, 303, 307, 308].includes(response.status)) {
const location = response.headers.get('location');
if (location && location.includes('106.15.4.153')) {
// 替换重定向地址中的原始IP为代理域名
const newLocation = location.replace(
/http:\/\/106\.15\.4\.153:\d+/g,
`https://${url.hostname}`
);
const newResponse = new Response(response.body, response);
newResponse.headers.set('location', newLocation);
return newResponse;
}
}
// 处理HTML内容,替换其中的链接
const contentType = response.headers.get('content-type') || '';
if (contentType.includes('text/html')) {
let html = await response.text();
// 替换所有原始服务器链接为代理域名
html = html.replace(
/http:\/\/106\.15\.4\.153:\d+/g,
`https://${url.hostname}`
);
// 在Amazon商品链接后添加推荐ID
html = html.replace(
/(https?:\/\/www\.amazon\.co\.uk\/[^"']*?\/(dp|gp\/product)\/[A-Z0-9]{10})([^"']*)/gi,
(match, baseUrl, pathType, queryString) => {
// 检查是否已经包含tag参数
if (match.includes('tag=') || match.includes('&tag=')) {
return match;
}
// 添加推荐ID
const separator = queryString.includes('?') ? '&' : '?';
return `${baseUrl}${queryString}${separator}tag=audio-media-21`;
}
);
// 返回修改后的HTML
return new Response(html, {
status: response.status,
headers: response.headers
});
}
// 对于非HTML内容,直接返回
return response;
} catch (error) {
// 错误处理
return new Response(`代理错误: ${error.message}\n\n目标URL: ${targetUrl.toString()}`, {
status: 502,
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
});
}
} }
时间 : 2025-09-09T22:22:52
🤖 DeepSeek (deepseek-chat) 我理解你的需求了。你希望通过 test.thanx.top 访问时,在代理的Amazon页面中修改商品链接,添加你的推荐ID。我来修改代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 export default { async fetch (request, env ) { const domainMappings = { 'test.thanx.top' : 'https://www.amazon.co.uk' , 'api.thanx.top' : 'http://106.15.4.153:8086' , 'admin.thanx.top' : 'http://106.15.4.153:8087' , 'reverse-proxy.yuebinliu.workers.dev' : 'https://www.amazon.co.uk' }; const url = new URL (request.url ); const target = domainMappings[url.hostname ]; if (!target) { return new Response ('域名未配置: ' + url.hostname + '\n\n已配置的域名: ' + Object .keys (domainMappings).join (', ' ), { status : 404 , headers : { 'Content-Type' : 'text/plain; charset=utf-8' } }); } const targetUrl = new URL (url.pathname + url.search , target); const newHeaders = new Headers (request.headers ); newHeaders.set ('X-Forwarded-Host' , url.hostname ); newHeaders.set ('X-Forwarded-Proto' , url.protocol .replace (':' , '' )); newHeaders.set ('X-Real-IP' , request.headers .get ('cf-connecting-ip' ) || 'unknown' ); newHeaders.delete ('host' ); try { let response = await fetch (targetUrl.toString (), { method : request.method , headers : newHeaders, body : request.body , redirect : 'manual' }); if ([301 , 302 , 303 , 307 , 308 ].includes (response.status )) { const location = response.headers .get ('location' ); if (location) { if (location.includes ('amazon.co.uk' )) { return response; } if (location.includes ('106.15.4.153' )) { const newLocation = location.replace ( /http:\/\/106\.15\.4\.153:\d+/g , `https://${url.hostname} ` ); const newResponse = new Response (response.body , response); newResponse.headers .set ('location' , newLocation); return newResponse; } } } const contentType = response.headers .get ('content-type' ) || '' ; if (contentType.includes ('text/html' ) && url.hostname === 'test.thanx.top' ) { let html = await response.text (); html = html.replace ( /http:\/\/106\.15\.4\.153:\d+/g , `https://${url.hostname} ` ); html = html.replace ( /(https:\/\/www\.amazon\.co\.uk\/[^"']*?\/dp\/[A-Z0-9]{10})([^"']*)/gi , (match, baseUrl, queryString ) => { if (match.includes ('tag=' ) || match.includes ('&tag=' )) { return match; } const separator = queryString.includes ('?' ) ? '&' : (queryString ? '&' : '?' ); return `${baseUrl} ${queryString} ${separator} tag=audio-media-21` ; } ); return new Response (html, { status : response.status , headers : response.headers }); } return response; } catch (error) { return new Response (`代理错误: ${error.message} \n\n目标URL: ${targetUrl.toString()} ` , { status : 502 , headers : { 'Content-Type' : 'text/plain; charset=utf-8' } }); } } }
主要修改:
条件判断 :只在 url.hostname === 'test.thanx.top' 时修改Amazon链接
链接匹配优化 :使用更精确的正则表达式匹配商品链接
分隔符处理 :改进分隔符逻辑,确保正确添加 &tag=audio-media-21
重定向处理 :保持Amazon的重定向不变,只处理内部服务的重定向
现在当用户通过 test.thanx.top 访问时,所有Amazon商品链接都会自动添加 &tag=audio-media-21,而其他域名访问时不会修改链接。
例如:https://www.amazon.co.uk/Sexgene-Big-Thick-Dildo-Sex/dp/B0CLY3TD67/ 会被修改为 https://www.amazon.co.uk/Sexgene-Big-Thick-Dildo-Sex/dp/B0CLY3TD67/&tag=audio-media-21
时间 : 2025-09-09T22:22:52
👤 用户 1,条件判断:只在 用户通过’test.thanx.top’这个地址访问 时修改Amazon链接,https://www.amazon.co.uk/Sexgene-Big-Thick-Dildo-Sex/dp/B0CLY3TD6/会被修改成https://www.amazon.co.uk/Sexgene-Big-Thick-Dildo-Sex/dp/B0CLY3TD67/&tag=audio-media-21,但不要替换.amazon.co.uk为test.thanx.top 2,判断&tag语句出错,原链接中是没有&tag的,我只是单纯的在原链接后面加上&tag=audio-media-21
时间 : 2025-09-09T22:35:49
🤖 DeepSeek (deepseek-chat) 我理解了,你需要只在通过 test.thanx.top 访问时,在Amazon商品链接后面直接添加 &tag=audio-media-21,不需要替换域名。以下是修改后的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 export default { async fetch (request, env ) { const domainMappings = { 'test.thanx.top' : 'https://www.amazon.co.uk' , 'api.thanx.top' : 'http://106.15.4.153:8086' , 'admin.thanx.top' : 'http://106.15.4.153:8087' , 'reverse-proxy.yuebinliu.workers.dev' : 'https://www.amazon.co.uk' }; const url = new URL (request.url ); const target = domainMappings[url.hostname ]; if (!target) { return new Response ('域名未配置: ' + url.hostname + '\n\n已配置的域名: ' + Object .keys (domainMappings).join (', ' ), { status : 404 , headers : { 'Content-Type' : 'text/plain; charset=utf-8' } }); } const targetUrl = new URL (url.pathname + url.search , target); const newHeaders = new Headers (request.headers ); newHeaders.set ('X-Forwarded-Host' , url.hostname ); newHeaders.set ('X-Forwarded-Proto' , url.protocol .replace (':' , '' )); newHeaders.set ('X-Real-IP' , request.headers .get ('cf-connecting-ip' ) || 'unknown' ); newHeaders.delete ('host' ); try { let response = await fetch (targetUrl.toString (), { method : request.method , headers : newHeaders, body : request.body , redirect : 'manual' }); if ([301 , 302 , 303 , 307 , 308 ].includes (response.status )) { const location = response.headers .get ('location' ); if (location) { if (location.includes ('amazon.co.uk' )) { return response; } if (location.includes ('106.15.4.153' )) { const newLocation = location.replace ( /http:\/\/106\.15\.4\.153:\d+/g , `https://${url.hostname} ` ); const newResponse = new Response (response.body , response); newResponse.headers .set ('location' , newLocation); return newResponse; } } } const contentType = response.headers .get ('content-type' ) || '' ; if (contentType.includes ('text/html' )) { let html = await response.text (); html = html.replace ( /http:\/\/106\.15\.4\.153:\d+/g , `https://${url.hostname} ` ); if (url.hostname === 'test.thanx.top' ) { html = html.replace ( /(https:\/\/www\.amazon\.co\.uk[^"']*?\/dp\/[A-Z0-9]{10}[^"']*)/gi , (match ) => { if (match.includes ('tag=' )) { return match; } return match + '&tag=audio-media-21' ; } ); } return new Response (html, { status : response.status , headers : response.headers }); } return response; } catch (error) { return new Response (`代理错误: ${error.message} \n\n目标URL: ${targetUrl.toString()} ` , { status : 502 , headers : { 'Content-Type' : 'text/plain; charset=utf-8' } }); } } }
主要修改:
精确的条件判断 :只在 url.hostname === 'test.thanx.top' 时修改Amazon链接
简化链接匹配 :直接匹配完整的Amazon商品链接,不拆分baseUrl和queryString
直接添加推荐ID :使用 match + '&tag=audio-media-21' 直接在原链接末尾添加
避免重复添加 :检查链接是否已经包含 tag= 参数,避免重复添加
现在当用户通过 test.thanx.top 访问时:
https://www.amazon.co.uk/Sexgene-Big-Thick-Dildo-Sex/dp/B0CLY3TD67/ 会被修改为 https://www.amazon.co.uk/Sexgene-Big-Thick-Dildo-Sex/dp/B0CLY3TD67/&tag=audio-media-21
保持 www.amazon.co.uk 域名不变
其他域名访问时不会修改任何链接
时间 : 2025-09-09T22:35:49
👤 用户 经过上面方法,很容易让人无误解为钓鱼网站。这样,我想在自己的web服务器上来实现上面的效果,可以实现吗?
时间 : 2025-09-10T08:38:23
🤖 DeepSeek (deepseek-chat) 你说得对,直接在代理中修改Amazon链接确实容易被误认为是钓鱼网站。在你的自己的web服务器上实现这个功能是完全可以的,而且更加安全和合规。
以下是几种实现方案:
方案一:使用PHP后端处理(推荐) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <?php $targetUrl = 'https://www.amazon.co.uk' . $_SERVER ['REQUEST_URI' ];$headers = [ 'User-Agent: ' . ($_SERVER ['HTTP_USER_AGENT' ] ?? 'Mozilla/5.0' ), 'Accept: ' . ($_SERVER ['HTTP_ACCEPT' ] ?? 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' ), 'Accept-Language: ' . ($_SERVER ['HTTP_ACCEPT_LANGUAGE' ] ?? 'en-US,en;q=0.5' ), ]; $ch = curl_init ();curl_setopt ($ch , CURLOPT_URL, $targetUrl );curl_setopt ($ch , CURLOPT_HTTPHEADER, $headers );curl_setopt ($ch , CURLOPT_RETURNTRANSFER, true );curl_setopt ($ch , CURLOPT_FOLLOWLOCATION, true );curl_setopt ($ch , CURLOPT_SSL_VERIFYPEER, false );$response = curl_exec ($ch );$contentType = curl_getinfo ($ch , CURLINFO_CONTENT_TYPE);curl_close ($ch );if (strpos ($contentType , 'text/html' ) !== false ) { $response = preg_replace_callback ( '/(https:\/\/www\.amazon\.co\.uk[^"\']*?\/dp\/[A-Z0-9]{10}[^"\']*)/i' , function($matches ) { if (strpos ($matches [0 ], 'tag=' ) === false ) { return $matches [0 ] . (strpos ($matches [0 ], '?' ) ? '&' : '?' ) . 'tag=audio-media-21' ; } return $matches [0 ]; }, $response ); } header ('Content-Type: ' . $contentType );echo $response ;?>
方案二:使用Nginx + Lua脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 server { listen 80 ; server_name test.thanx.top; location / { proxy_pass https://www.amazon.co.uk; proxy_set_header Host www.amazon.co.uk; proxy_set_header User-Agent $http_user_agent ; proxy_set_header Accept $http_accept ; header_filter_by_lua_block { if ngx.header.content_type and string.find(ngx.header.content_type, "text/html" ) then ngx.header.content_length = nil end } body_filter_by_lua_block { if ngx.header.content_type and string.find(ngx.header.content_type, "text/html" ) then local body = ngx.arg[1 ] body = string.gsub(body, '(https://www%.amazon%.co%.uk[^"\']*?/dp/[A-Z0-9]+[^"\']*)' , function(url) if not string.find(url, 'tag=' ) then if string.find(url, '?' ) then return url .. '&tag=audio-media-21' else return url .. '?tag=audio-media-21' end end return url end) ngx.arg[1 ] = body end } } }
方案三:使用Node.js Express 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 const express = require ('express' );const axios = require ('axios' );const app = express ();app.use (async (req, res) => { try { const targetUrl = `https://www.amazon.co.uk${req.url} ` ; const response = await axios.get (targetUrl, { headers : { 'User-Agent' : req.headers ['user-agent' ], 'Accept' : req.headers ['accept' ], 'Accept-Language' : req.headers ['accept-language' ] }, responseType : 'arraybuffer' }); let content = response.data .toString (); const contentType = response.headers ['content-type' ]; if (contentType.includes ('text/html' )) { content = content.replace ( /(https:\/\/www\.amazon\.co\.uk[^"']*?\/dp\/[A-Z0-9]{10}[^"']*)/gi , (match ) => { if (!match.includes ('tag=' )) { return match + (match.includes ('?' ) ? '&' : '?' ) + 'tag=audio-media-21' ; } return match; } ); } res.set (response.headers ); res.send (content); } catch (error) { res.status (500 ).send ('Error: ' + error.message ); } }); app.listen (3000 , () => { console .log ('Server running on port 3000' ); });
安装说明 PHP方案:
安装PHP和cURL扩展
将proxy.php上传到你的web服务器
配置Nginx/Apache指向这个文件
Node.js方案: 1 2 3 npm init -y npm install express axios node server.js
优势对比
方案
优点
缺点
PHP
部署简单,兼容性好
性能相对较低
Nginx+Lua
高性能,原生支持
需要安装Lua模块
Node.js
灵活,现代技术栈
需要Node.js环境
推荐使用PHP方案 ,因为它最简单且兼容性最好,只需要基本的PHP环境即可运行。
这样实现既避免了被误认为钓鱼网站的风险,又能在你自己的服务器上实现相同的功能。
时间 : 2025-09-10T08:38:23
👤 用户 没关系,node.js和python环境都没有问题的,简单安装下就好了
时间 : 2025-09-10T09:03:16
🤖 DeepSeek (deepseek-chat) 好的,既然你有Node.js和Python环境,那我给你提供这两个版本的实现方案:
方案一:Node.js + Express 版本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 const express = require ('express' );const axios = require ('axios' );const { URL } = require ('url' );const app = express ();app.use (async (req, res) => { try { const targetUrl = `https://www.amazon.co.uk${req.originalUrl} ` ; console .log ('Proxying:' , targetUrl); const response = await axios ({ method : req.method , url : targetUrl, headers : { 'User-Agent' : req.headers ['user-agent' ] || 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' , 'Accept' : req.headers ['accept' ] || 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' , 'Accept-Language' : req.headers ['accept-language' ] || 'en-US,en;q=0.9' , 'Accept-Encoding' : 'gzip, deflate, br' , 'Connection' : 'keep-alive' , }, responseType : 'arraybuffer' , validateStatus : null , }); let content = response.data ; const contentType = response.headers ['content-type' ]; if (contentType && contentType.includes ('text/html' )) { content = content.toString (); content = content.replace ( /(https:\/\/www\.amazon\.co\.uk(\/[^"'\s]*?\/dp\/[A-Z0-9]{10}[^"'\s]*))/gi , (match, url ) => { if (url.includes ('tag=' )) { return match; } const separator = url.includes ('?' ) ? '&' : '?' ; return match.replace (url, url + separator + 'tag=audio-media-21' ); } ); } Object .entries (response.headers ).forEach (([key, value] ) => { if (key.toLowerCase () !== 'content-length' ) { res.setHeader (key, value); } }); res.status (response.status ).send (content); } catch (error) { console .error ('Proxy error:' , error.message ); res.status (500 ).send (`Proxy error: ${error.message} ` ); } }); const PORT = process.env .PORT || 3000 ;app.listen (PORT , () => { console .log (`Amazon proxy server running on port ${PORT} ` ); console .log (`Access via: http://localhost:${PORT} ` ); });
安装运行:
1 2 3 npm init -y npm install express axios node server.js
方案二:Python + Flask 版本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 from flask import Flask, request, Responseimport requestsimport refrom urllib.parse import urljoinapp = Flask(__name__) @app.route('/' , defaults={'path' : '' } ) @app.route('/<path:path>' ) def proxy (path ): try : target_url = urljoin('https://www.amazon.co.uk/' , path) if request.query_string: target_url += '?' + request.query_string.decode() print (f'Proxying: {target_url} ' ) headers = { 'User-Agent' : request.headers.get('User-Agent' , 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36' ), 'Accept' : request.headers.get('Accept' , 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' ), 'Accept-Language' : request.headers.get('Accept-Language' , 'en-US,en;q=0.9' ), 'Accept-Encoding' : 'gzip, deflate, br' , } response = requests.request( method=request.method, url=target_url, headers=headers, data=request.get_data(), cookies=request.cookies, allow_redirects=False , stream=True ) content_type = response.headers.get('Content-Type' , '' ) if 'text/html' in content_type: content = response.content.decode('utf-8' , errors='ignore' ) def add_affiliate_tag (match ): url = match .group(1 ) if 'tag=' in url: return match .group(0 ) separator = '?' if '?' not in url else '&' return match .group(0 ).replace(url, url + separator + 'tag=audio-media-21' ) content = re.sub( r'(https://www\.amazon\.co\.uk(/[^"\'\s]*?/dp/[A-Z0-9]{10}[^"\'\s]*))' , add_affiliate_tag, content, flags=re.IGNORECASE ) flask_response = Response(content, status=response.status_code) else : flask_response = Response(response.iter_content(chunk_size=1024 ), status=response.status_code) for key, value in response.headers.items(): if key.lower() not in ['content-length' , 'transfer-encoding' ]: flask_response.headers[key] = value return flask_response except Exception as e: print (f'Error: {e} ' ) return f'Proxy error: {e} ' , 500 if __name__ == '__main__' : app.run(host='0.0.0.0' , port=3000 , debug=True )
安装运行:
1 2 pip install flask requests python app.py
方案三:使用 http-proxy-middleware(更专业的Node.js方案) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 const express = require ('express' );const { createProxyMiddleware } = require ('http-proxy-middleware' );const cheerio = require ('cheerio' );const { Transform } = require ('stream' );const app = express ();class HtmlTransformStream extends Transform { _transform (chunk, encoding, callback ) { let html = chunk.toString (); html = html.replace ( /(https:\/\/www\.amazon\.co\..uk(\/[^"'\s]*?\/dp\/[A-Z0-9]{10}[^"'\s]*))/gi , (match, url ) => { if (url.includes ('tag=' )) return match; const separator = url.includes ('?' ) ? '&' : '?' ; return match.replace (url, url + separator + 'tag=audio-media-21' ); } ); this .push (html); callback (); } } const proxy = createProxyMiddleware ({ target : 'https://www.amazon.co.uk' , changeOrigin : true , selfHandleResponse : true , onProxyRes : (proxyRes, req, res ) => { const contentType = proxyRes.headers ['content-type' ]; if (contentType && contentType.includes ('text/html' )) { const transformStream = new HtmlTransformStream (); delete proxyRes.headers ['content-length' ]; Object .keys (proxyRes.headers ).forEach (key => { res.setHeader (key, proxyRes.headers [key]); }); proxyRes.pipe (transformStream).pipe (res); } else { proxyRes.pipe (res); } }, onError : (err, req, res ) => { console .error ('Proxy error:' , err); res.status (500 ).send ('Proxy error' ); } }); app.use ('/' , proxy); const PORT = process.env .PORT || 3000 ;app.listen (PORT , () => { console .log (`Proxy server running on port ${PORT} ` ); });
安装运行:
1 2 npm install express http-proxy-middleware cheerio node proxy-server.js
推荐使用 推荐方案一(Express + Axios) ,因为它:
代码简洁易懂
功能完整
错误处理完善
性能足够
运行后访问 http://你的服务器IP:3000 即可看到效果,所有Amazon商品链接都会自动添加你的推荐ID。
时间 : 2025-09-10T09:03:16
👤 用户 这个方案一使用的什么web服务器?
时间 : 2025-09-10T09:33:43
🤖 DeepSeek (deepseek-chat) 根据你提供的”方案一”代码,它使用的是 Node.js 的 Express 框架 来构建的 Web 服务器。这是一个非常流行和灵活的 JavaScript 服务器框架。
🖥️ 核心组件与工作原理 方案一主要依赖以下核心库:
核心库
用途
版本建议
Express
作为Web应用框架,处理路由、中间件和HTTP请求/响应[citation:8]
^4.18.2
Axios
用于向亚马逊服务器发起HTTP请求,获取原始页面内容
^1.6.0
Node.js 内置模块 (如 http, url)
提供基础的HTTP服务器和URL解析能力[citation:8]
-
其工作原理如下图所示,可以看到一个请求是如何被处理的:
1 2 3 4 5 6 7 flowchart TD A[用户访问你的服务器] --> B[Express 接收请求] B --> C[Axios 向亚马逊请求原始页面] C --> D{是否是HTML?} D -- 是 --> E[正则匹配商品链接] E --> F[添加联盟Tag后返回] D -- 否 --> G[直接返回原始内容]
⚙️ 部署注意事项 要让这个服务器稳定运行,你需要:
安装依赖 :在放代码的目录下运行 npm install express axios。
启动服务 :运行 node server.js。生产环境建议使用 PM2 等进程管理器来守护进程和实现零停机重启。
反向代理(可选但推荐) :虽然你的代码本身是一个功能完整的Web服务器,但通常在生产环境中,强烈建议 在其前面搭配 Nginx [citation:5] 或 Caddy 这样的专业Web服务器作为反向代理。这样做能带来很多好处:
提升性能 :Nginx可以高效处理静态文件(如图片、CSS、JS)、SSL加密和解密,减轻Node.js进程的负担[citation:5]。
增强安全性 :隐藏后端Node.js服务器,提供额外的安全层。
提高可靠性 :可以做负载均衡,或者在你的Node.js应用意外崩溃时提供更优雅的错误处理。
一个简单的Nginx配置示例(/etc/nginx/sites-available/your-site)可能看起来像这样[citation:5]:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 server { listen 80 ; server_name your-domain.com; location / { proxy_pass http://localhost:3000; proxy_http_version 1 .1 ; proxy_set_header Upgrade $http_upgrade ; proxy_set_header Connection 'upgrade' ; proxy_set_header Host $host ; proxy_set_header X-Real-IP $remote_addr ; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ; proxy_set_header X-Forwarded-Proto $scheme ; proxy_cache_bypass $http_upgrade ; } }
记得配置后通过 sudo nginx -t 测试配置,并用 sudo systemctl reload nginx 重载Nginx。
⚠️ 重要提醒
正则表达式的局限性 :代码中通过正则表达式匹配商品链接(匹配包含 /dp/ 后接10位字母数字的模式)并添加 &tag=audio-media-21。这种方式可能不够健壮 ,如果亚马逊的URL结构发生变化,或者商品ASIN码长度不全是10位,可能会导致匹配失败。你需要定期测试和维护。
遵守联盟计划规则 :务必仔细阅读亚马逊联盟计划(Amazon Associates)的条款 。确保这种通过代理服务器自动添加tag的方式是允许的,避免违反规则导致账户被封。
性能考量 :此方案每次请求都会从亚马逊获取页面 ,可能会比较慢,并且对亚马逊的服务器造成一定压力。如果流量较大,需要考虑加入缓存机制(例如使用Redis或内存缓存一段时间内的页面),但同样需注意遵守亚马逊的条款。
💡 为什么不直接用Nginx? 你可能想知道为什么不能直接用Nginx这样的Web服务器实现。这是因为Nginx本身主要用于处理静态内容、作为反向代理[citation:5]或负载均衡器[citation:10] ,它不擅长直接执行业务逻辑 [citation:10]。而修改HTML内容、动态添加联盟Tag这种动态内容生成[citation:9]和复杂的文本处理任务,正是Node.js(配合Express)这类应用平台/运行时环境所擅长的 [citation:10]。通常的做法正是让Nginx作为反向代理,将需要特殊处理的请求转发给你的Node.js应用[citation:5]。
希望这些信息能帮你更好地理解和运行你的方案一。
时间 : 2025-09-10T09:33:43
对话统计