Shell脚本如何提取网页中所有链接

wen 实用脚本 1

Shell脚本高效提取网页中所有链接:实战指南与SEO优化策略

📖 目录导读

  1. 为什么用Shell脚本提取网页链接?
  2. 核心工具:grep、sed、awk与curl的搭配使用
  3. 实战脚本:从单页面提取所有链接
  4. 进阶技巧:处理动态页面与相对路径
  5. 批量提取:遍历网站所有页面链接
  6. 常见问题与解决方案(FAQ)
  7. 性能优化与SEO友好性建议

为什么用Shell脚本提取网页链接?

在网络爬虫、SEO分析或数据采集场景中,提取网页中的超链接是最基础的需求,相比于Python的BeautifulSoup或Node.js的Cheerio,Shell脚本具有零依赖、轻量级、可管道化的优势——尤其适合服务器环境,无需安装额外语言运行时,你可以在Linux VPS上直接用一行命令获取某个网页的外链列表,然后配合sort | uniq去重统计。

Shell脚本如何提取网页中所有链接

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动态页面时有局限性,但结合xidelpup等工具,内核依然是轻量级Shell管道——这正是它在DevOps和自动化场景中不可替代的理由。

抱歉,评论功能暂时关闭!