Shell脚本高效提取网页中所有链接:实战指南与SEO优化策略
📖 目录导读
- 为什么用Shell脚本提取网页链接?
- 核心工具:grep、sed、awk与curl的搭配使用
- 实战脚本:从单页面提取所有链接
- 进阶技巧:处理动态页面与相对路径
- 批量提取:遍历网站所有页面链接
- 常见问题与解决方案(FAQ)
- 性能优化与SEO友好性建议
为什么用Shell脚本提取网页链接?
在网络爬虫、SEO分析或数据采集场景中,提取网页中的超链接是最基础的需求,相比于Python的BeautifulSoup或Node.js的Cheerio,Shell脚本具有零依赖、轻量级、可管道化的优势——尤其适合服务器环境,无需安装额外语言运行时,你可以在Linux VPS上直接用一行命令获取某个网页的外链列表,然后配合sort | uniq去重统计。

Shell脚本提取链接的核心逻辑是:获取网页HTML源码 → 匹配<a href="...">模式 → 过滤有效URL → 输出为列表,虽然正则表达式不如DOM解析精确,但结合过滤规则后足以应对80%的常见场景。
核心工具:grep、sed、awk与curl的搭配使用
1 工具角色分工
| 工具 | 作用 | 关键参数 |
|---|---|---|
curl |
下载网页HTML | -s (静默)、-L (跟随重定向) |
grep |
提取包含href的行 |
-o (仅输出匹配)、-E (扩展正则) |
sed |
清理和格式化URL | -n (安静)、-e (执行脚本) |
awk |
分割字段、去重统计 | -F (分隔符)、!a[$0]++ (去重) |
2 正则表达式模式
最通用的链接匹配模式:
# 匹配 <a href="..."> 中的URL grep -oE 'href="([^"]+)"' | sed 's/href="//;s/"//'
但需要考虑特殊情况:单引号、无引号、href=后带空格等,改进后的模式:
grep -oE 'href=[\'"]?([^\'" >]+)' | sed "s/href=['\"]//"
实战脚本:从单页面提取所有链接
1 基础版本:提取绝对路径链接
#!/bin/bash
# 功能:从指定URL提取所有链接,输出绝对路径
# 用法:./extract_links.sh https://example.com/page
URL=$1
DOMAIN=$(echo "$URL" | awk -F/ '{print $3}') # 提取域名
curl -s -L "$URL" | \
grep -oE 'href="([^"]+)"' | \
sed 's/href="//;s/"//' | \
while read LINK; do
if [[ "$LINK" =~ ^http ]]; then
# 已经是绝对路径
echo "$LINK"
elif [[ "$LINK" =~ ^// ]]; then
# 协议相对路径(如 //example.com/page)
echo "https:$LINK"
elif [[ "$LINK" =~ ^/ ]]; then
# 根相对路径(如 /page)
echo "https://$DOMAIN$LINK"
else
# 相对路径(如 page.html)
echo "https://$DOMAIN/$(dirname "$URL" | sed "s|^.*$DOMAIN||")/$LINK"
fi
done | sort -u > links.txt
2 实战输出示例
假设URL为https://example.com/blog/article1,输出:
https://example.com/
https://example.com/about
https://example.com/blog/article2
https://external-site.com/resource
进阶技巧:处理动态页面与相对路径
1 处理JavaScript渲染的链接
静态HTML提取法无法获取由JS动态生成的链接(如SPA应用),解决方案:
-
搭配
xidel(支持XPath和CSS选择器):# 需安装xidel,但仍属于Shell生态 xidel -s "$URL" --extract "//a/@href" 2>/dev/null
-
使用
pup(Go写的HTML解析器):# 提取所有链接并去重 curl -s "$URL" | pup 'a attr{href}' | sort -u
2 规范化相对路径的实用函数
# 将相对路径转为绝对路径(基于当前URL的目录)
resolve_url() {
local base_url=$1
local relative_path=$2
# 省略实现细节(需处理../和./)
# 可使用Python临时辅助:python3 -c "from urllib.parse import urljoin; print(urljoin('$base_url', '$relative_path'))"
}
批量提取:遍历网站所有页面链接
1 简易爬虫框架
#!/bin/bash
# 限定域名,递归提取(最多2层深度)
SEED_URL="https://example.com"
DOMAIN="example.com"
MAX_DEPTH=2
extract_all_links() {
local url=$1
local depth=$2
[ $depth -gt $MAX_DEPTH ] && return
links=$(curl -s "$url" | grep -oE 'href="/[^"]+"' | sed 's/href="//;s/"//')
for link in $links; do
absolute="https://$DOMAIN$link"
echo "$absolute"
# 递归:只抓取同域名下的html页面
if [[ "$absolute" != *".pdf"* ]] && [[ "$absolute" != *".jpg"* ]]; then
extract_all_links "$absolute" $((depth+1))
fi
done
}
extract_all_links "$SEED_URL" 0 | sort -u > all_links.txt
⚠️ 注意:递归爬取要控制深度,避免无限循环和服务器负载,建议添加curl的--connect-timeout和--max-time参数。
2 过滤无意义链接
# 排除 mailto:、javascript:、# 锚点 grep -vE '^(mailto:|javascript:|#)' | grep -v '^$'
常见问题与解决方案(FAQ)
Q1:为什么脚本提取的链接比浏览器看到的少?
A:主要原因是:
- JavaScript动态加载:浏览器会执行JS生成链接,而curl只获取HTML源码,可尝试使用
headless浏览器工具(如Puppeteer),但会增加复杂度,如果需要全部链接,建议切换到Python+Playwright方案。 - CSS隐藏链接:某些链接通过
<a>标签但CSS隐藏了,脚本仍会提取(因为HTML源码存在),这通常是正常行为。 - 注释中的链接:
<!-- <a href="..."> -->会被误提取,可先用sed '/<!--/,/-->/d'删除注释。
Q2:如何处理相对路径如../page?
A:相对路径解析需要知道当前页面目录,建议使用urljoin工具或临时调用Python:
python3 -c "from urllib.parse import urljoin; import sys; print(urljoin(sys.argv[1], sys.argv[2]))" "$base_url" "$relative_url"
Q3:提取的链接中有重复,如何去重?
A:使用sort -u即可,如果链接结构一样但结尾斜杠不同(如/page vs /page/),可先用sed 's|/*$||'统一处理。
Q4:脚本运行缓慢怎么办?
A:优化点:
- 使用
parallel工具并行请求(需安装GNU parallel) - 限制每个域名并发数(
curl的--parallel参数) - 添加
--retry 0避免重试延迟
性能优化与SEO友好性建议
1 Shell脚本提取链接的SEO价值
- 外链分析:快速获取某个页面的所有出站链接,检查死链(配合
curl -I判断状态码)。 - 内部链接审计:统计站内链接分布,确保核心页面被充分连接。
- Sitemap生成:提取所有页面URL后,可转换为XML格式提交给搜索引擎。
2 提升脚本效率的技巧
# 1. 使用进程池并行获取
cat urls.txt | parallel -j8 "curl -s -o /dev/null -w '%{http_code} %{url_effective}' {}"
# 2. 只提取200状态码的页面链接
curl -sI "$URL" | head -1 | grep -q "200" && curl -s "$URL" | ...
# 3. 缓存HTML避免重复请求(适用于多次提取)
if [ ! -f "cache/$(echo $URL | md5sum | cut -d' ' -f1).html" ]; then
curl -s "$URL" > "cache/$(echo $URL | md5sum | cut -d' ' -f1).html"
fi
3 输出格式与搜索引擎友好
- 导出为CSV格式:每行包含
源URL,目标URL,链接文本,便于导入Google Search Console或Ahrefs。 - 生成纯文本Sitemap:符合搜索引擎接受的
txt格式(一行一个URL)。 - 排除
nofollow链接(若需统计实际传递权重的链接):grep -v 'rel="nofollow"'
4 完整生产级脚本示例
#!/bin/bash
# extract_links_pro.sh - 提取并验证链接状态
URL=$1
curl -s -L "$URL" | \
grep -oE 'href="([^"]+)"' | \
sed 's/href="//;s/"//' | \
while read link; do
# 补全绝对路径(简化版,仅处理/开头的)
if [[ "$link" == /* ]]; then
full="https://$(echo $URL | awk -F/ '{print $3}')$link"
elif [[ "$link" == http* ]]; then
full=$link
else
continue
fi
# 检查是否可访问(只输出200的)
http_code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "$full")
echo "$http_code $full"
done | sort -u | column -t
通过以上方法,你可以用Shell脚本灵活、高效地提取网页中的链接,适用于SEO审计、死链检测、内容抓取等场景,尽管在处理JS动态页面时有局限性,但结合xidel、pup等工具,内核依然是轻量级Shell管道——这正是它在DevOps和自动化场景中不可替代的理由。