ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

wget+PHP构建可审计静态资源镜像系统

wget+PHP构建可审计静态资源镜像系统 简介这是一套面向开发者与数据技术学习者的开源网页抓取系统专为解决网站静态/动态内容批量下载、本地化数据采集等实际需求而设计适用于数据挖掘、竞品分析、学术爬虫实践等场景。资源包共28个文件含8个PHP核心逻辑文件如api.php、wget_site.sh、smtp类等、3个CSS与2个JS前端交互文件、3张JPG演示图直观呈现操作界面另有README.md、LICENSE、.gitignore等工程规范文件整体仅318KB轻量易部署。已有346人学习下载体现其在中小型爬虫项目中的实用热度。用户可直接运行wget_site.sh调用本地wget实现免第三方API的稳定扒站深入理解任务调度、数据解析与反爬适配等模块设计配套演示图与清晰目录结构含work、assets、down等功能子目录大幅降低上手门槛是学习网络爬虫工程化落地的优质开源范例。1. 这不是爬虫工具而是一套可审计、可定制、可部署的网站静态资源采集系统“扒站”这个词在开发者社区里常被误读为黑灰产行为但实际在合法合规前提下它指代的是对公开网页结构、样式、脚本、图片等静态资源的完整镜像采集与本地重建——典型场景包括前端团队做竞品页面快照归档、UI 设计师提取设计规范素材库、内容运营人员备份活动页以防 CDN 失效、SEO 工程师分析页面渲染路径。2024 年这套系统之所以强调“至白”“非第三方 API”核心在于彻底剥离对外部服务的依赖不调用任何云解析服务、不走中间代理节点、不依赖商业爬虫平台所有逻辑由本地 PHP 脚本驱动所有网络请求由wget原生命令发起所有资源路径解析、HTML 重写、链接修正均由开源代码自主完成。它面向的是有 Linux 服务器运维能力、熟悉 PHP 环境配置、需要完全掌控数据流向的中高级开发者或 DevOps 工程师而非点选式小白工具用户。2. 用 wget PHP 构建最小可行采集链从命令行到可复用函数封装2.1 wget 是唯一网络层入口必须禁用递归但保留会话与重定向能力wget在此系统中不是辅助工具而是唯一的 HTTP 客户端。它不启用-r递归或-p页面所需全部文件因为那会导致不可控的深度抓取和域名越界相反我们采用“单页精准拉取 后置资源补全”策略。关键参数组合如下wget --no-cookies \ --no-check-certificate \ --user-agentMozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 \ --headerAccept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8 \ --timeout30 \ --tries2 \ --restrict-file-nameswindows \ --convert-links \ --page-requisites \ -O index.html \ https://example.com/提示--convert-links是核心开关——它让 wget 自动将远程绝对 URL 替换为相对路径使页面在本地打开时能正确加载 CSS/JS/图片--page-requisites仅下载 HTML 中link、script、img标签直接引用的资源不递归抓取这些资源内部再引用的资源避免无限嵌套。--restrict-file-nameswindows防止 Linux 下生成非法文件名如:/*确保 Windows 环境也能解压查看。2.2 PHP 负责资源调度与 HTML 二次修正解决 wget 的三大硬伤wget 虽可靠但在真实站点中存在三类无法自动处理的问题动态路径混淆img src/static/logo.png→ 实际应为https://example.com/static/logo.png但 wget 有时会错误解析为file:///static/logo.png内联 JS/CSS 中的 URLscriptfetch(/api/data)/script中的/api/data不会被 wget 捕获导致 JS 执行失败Base 标签干扰若页面含base hrefhttps://cdn.example.com/wget 会按此 base 下载资源但本地打开时却指向错误域名。PHP 脚本如rebuild.php需在 wget 完成后立即执行其主流程为?php // rebuild.php —— 输入wget 输出目录路径输出修正后的 index.html 及资源树 $rootDir realpath($argv[1] ?? ./site); $htmlPath $rootDir . /index.html; if (!file_exists($htmlPath)) { die(ERROR: index.html not found in {$rootDir}\n); } $html file_get_contents($htmlPath); $dom new DOMDocument(); libxml_use_internal_errors(true); $dom-loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); // 1. 移除 base 标签防止后续 relative link 解析错乱 $baseNodes $dom-getElementsByTagName(base); for ($i $baseNodes-length - 1; $i 0; $i--) { $baseNodes-item($i)-parentNode-removeChild($baseNodes-item($i)); } // 2. 重写所有 src/href 属性统一转为相对路径以 index.html 为基准 $attrs [src, href, data-src, data-href]; foreach ($attrs as $attr) { $nodes $dom-getElementsByTagName(*); foreach ($nodes as $node) { if ($node-hasAttribute($attr)) { $url $node-getAttribute($attr); if (filter_var($url, FILTER_VALIDATE_URL)) { // 提取路径部分忽略协议域名 $parsed parse_url($url); $relPath ltrim($parsed[path], /); $node-setAttribute($attr, $relPath); } } } } // 3. 保存修正后 HTML file_put_contents($htmlPath, $dom-saveHTML()); echo ✓ HTML rebuilt: {$htmlPath}\n; ?参数说明LIBXML_HTML_NOIMPLIED防止 DOMDocument 自动补全htmlbody结构避免污染原始结构parse_url()是安全提取路径的唯一方式比正则更鲁棒ltrim($parsed[path], /)确保路径以static/logo.png形式存在而非/static/logo.png后者在本地文件系统中会被解释为根目录。3. 源码级可配置项详解5 个必调参数决定采集精度与兼容性3.1config.php中的 5 个核心开关及其真实影响系统通过config.php统一管理行为策略以下 5 项参数在 2024 年主流站点含 Vue/React SSR 页面、CDN 资源分离架构、HTTP/2 优先站点中必须显式设置参数名默认值作用说明修改建议ALLOWED_DOMAINS[*]控制 wget 只允许访问哪些域名防跨域采集生产环境必须设为[example.com, cdn.example.com]禁止通配符RESOURCE_TIMEOUT_MS5000PHP 解析 HTML 时对每个外部资源 URL 的 DNS连接超时阈值毫秒若目标站使用 Cloudflare建议调高至8000MAX_RESOURCE_DEPTH2允许 PHP 主动补抓的资源层级如 HTML → CSS → CSS 中 import 的另一个 CSS静态站设1含复杂 CSS 架构的设2设0则仅处理 HTML 直接引用资源REWRITE_CSS_URLStrue是否解析并重写 CSS 文件内的url()函数路径必须开启否则 CSS 中背景图、字体等仍指向线上地址SKIP_JS_EXECUTIONtrue是否跳过执行内联 JS因 PHP 无法运行 JS设 false 会导致解析失败始终保持trueJS 渲染逻辑不在本系统职责范围内3.2REWRITE_CSS_URLS true的实现细节与边界条件当开启 CSS 重写时PHP 不会真正加载 CSS 文件而是用正则提取url(...)内容并替换。关键代码段如下// css_rewriter.php function rewriteCssUrls($cssContent, $baseUrl) { return preg_replace_callback( /url\(\s*[\]?([^\\)])[\]?\s*\)/i, function ($matches) use ($baseUrl) { $rawUrl $matches[1]; if (filter_var($rawUrl, FILTER_VALIDATE_URL)) { // 远程 URL → 提取 path 并转为相对路径 $parsed parse_url($rawUrl); return url( . ltrim($parsed[path], /) . ); } elseif (strpos($rawUrl, //) 0) { // 协议相对 URL → 视为同协议按 $baseUrl 解析 $absUrl parse_url($baseUrl)[scheme] . : . $rawUrl; $parsed parse_url($absUrl); return url( . ltrim($parsed[path], /) . ); } else { // 已是相对路径直接返回 return $matches[0]; } }, $cssContent ); }注意该正则不匹配url(data:image/png;base64,...)或url(#some-id)SVG 引用这两类无需重写strpos($rawUrl, //) 0专门处理//cdn.example.com/img.png这类协议相对 URL避免误判为绝对路径。4. 非第三方 API 接口的设计哲学为什么拒绝封装 curl 或 Guzzle4.1 所谓“非第三方 API”是指零外部依赖连 Composer 都不引入本系统所有网络交互严格限定在两个原生能力内Shell 层仅调用wgetLinux/macOS 原生命令CentOS/RHEL/Ubuntu 均预装PHP 层仅使用file_get_contents()、DOMDocument、parse_url()、preg_replace_callback()等内置函数不 require 任何第三方包。这意味着无需composer install无vendor/目录无需配置php.ini开启allow_url_fopenwget 已承担网络请求无需处理 Guzzle 的 PSR-7、PSR-18 兼容性问题无需担心curl版本差异导致的 TLS 1.3 支持问题wget 由系统维护更稳定。验证方式极其简单# 进入源码目录检查是否含 vendor 或 composer.json ls -la | grep -E (vendor|composer\.json|package\.json) # 应无任何输出4.2 “API 接口”在此处指 CLI 命令行接口而非 Web API标题中“API 接口”并非指/api/v1/xxx这类 HTTP 接口而是指系统对外暴露的标准化命令行调用契约。例如# 标准采集命令带参数校验 php run.php --urlhttps://example.com --output./mirror --depth1 # 批量采集接受 JSON 配置文件 php run.php --configconfig/batch.json # 仅重写 HTML跳过 wget用于二次加工 php rebuild.php --dir./mirror/example.comrun.php的参数解析逻辑如下// run.php $options getopt(u:o:d:c:, [url:, output:, depth:, config:]); if (isset($options[u]) || isset($options[url])) { $targetUrl $options[u] ?? $options[url]; } else { die(ERROR: --url is required\n); } if (!filter_var($targetUrl, FILTER_VALIDATE_URL)) { die(ERROR: invalid URL format: {$targetUrl}\n); }逻辑说明getopt()是 PHP 内置函数无需扩展FILTER_VALIDATE_URL比正则更准确识别合法 URL错误信息直接输出到 stderr符合 Unix 工具规范方便 Shell 脚本捕获if [ $? -ne 0 ]; then ...。5. 实战排错3 类高频失败场景与对应日志定位法5.1 wget 报错 “ERROR 403: Forbidden” 的 3 种真实原因及对策这不是简单的反爬而是现代站点常见的主动防御机制需分层排查日志线索真实原因解决方案Resolving example.com... failed: Name or service not known.DNS 解析失败但ping example.com成功 → 说明系统 DNS 缓存污染或/etc/resolv.conf配置错误执行nslookup example.com 8.8.8.8测试公共 DNS若成功则修改/etc/resolv.conf为nameserver 8.8.8.8Connecting to example.com192.0.2.1:443... failed: Connection refused.ERROR 403: Forbidden且响应头含cf-ray: xxxxxCloudflare 人机验证拦截非 User-Agent 问题添加--headerCookie: __cf_bmxxx需先手动访问获取有效 cookie或改用--user-agentGooglebot/2.1部分站对搜索引擎 UA 放行5.2 PHP 重写后页面空白的 2 个隐蔽根源若rebuild.php执行成功但浏览器打开index.html为空白90% 情况源于DOMDocument 加载失败未报错libxml_use_internal_errors(true)屏蔽了 HTML 解析错误需主动检查$errors libxml_get_errors(); if (!empty($errors)) { foreach ($errors as $error) { echo XML Error: {$error-message} at line {$error-line}\n; } libxml_clear_errors(); }CSS/JS 路径重写后 404wget未下载某些资源如.woff2字体但 PHP 仍尝试重写其路径。验证方法# 查看 wget 日志中缺失资源 grep -i 404 wget-log.txt # 检查对应文件是否存在于 output 目录 find ./mirror -name font.woff2若不存在需在config.php中将MAX_RESOURCE_DEPTH提高并确认wget命令中--accept参数包含.woff2,.woff,.ttf。5.3 使用wget -o /etc/yum.repos.d/centos-base.repo https://mirrors.aliyun.com/repo/ce类命令的警示该命令常见于 CentOS 系统初始化但它与本系统完全无关且危险wget -o是覆盖写入文件若误将index.html路径写成/etc/...将直接破坏系统配置https://mirrors.aliyun.com/repo/ce是 YUM 源地址返回的是.repo文本不是 HTML本系统所有 wget 输出均强制指定-O filename单文件或-P dir目录绝不用-o日志文件替代输出目标。务必养成习惯执行任何wget前先echo命令确认路径echo wget -O ./mirror/index.html https://example.com/ # 确认无误后再删掉 echo 执行本文还有配套的精品资源点击获取
返回列表