You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用FSharp.Data解析HTML:提取带href属性的a标签PDF链接

使用FSharp.Data提取HTML中的PDF链接

问题背景

我正在用F#和FSharp.Data库解析HTML,目前能提取<head>里的meta标签数据,但换成查找带href属性的<a>标签(获取所有PDF链接,比如file1.pdf)时却得不到结果。现有代码能获取meta标签的name属性值,但把Descendants参数改成"a"就失效了。

当前代码:

#r @"C:\...\.nuget\packages\fsharp.data\5.0.2\lib\netstandard2.0\FSharp.Data.dll"
open Fsharp.Data
let html = HtmlDocument.Load("test.html")
let d =
    html.Descendants [ "meta" ]
    |> Seq.choose (fun x ->
        x.TryGetAttribute("name")
        |> Option.map (fun a -> a.Value()))
    |> Seq.toList

测试HTML示例:

<!DOCTYPE html  PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="pragma" content="no-cache">
    ...
    <meta name="description" content="....">
    <meta name="robots" content="all">
        <meta name="keywords" content="keywords...">
        <link rel='stylesheet' href='*****.css' type='text/css'/>
        <link rel='stylesheet' href='*****.css' type='text/css' title='***'/>
        <link rel='alternate stylesheet' href='../styles.css' type='text/css' title='***'/>
        <script src='../Menu.js' type='text/javascript'></script>

    <script type='text/javascript'>
        if (isIE() || isChrome())
        {
            setActiveStyleSheet(SET_SS);
        }
        setRequestedStyleSheet();
    </script>
</head>
<body onload='init()'>
<div id='Menu'><input id='checkBox' type='checkbox' onclick='toggleStyleSheet()' title='Show'></div>
<iframe class="A" >
</iframe>
    
<table class="CenteredTable" border="0" space="preserve">
  <colgroup space="preserve" span="1">
    <col class="col1" space="preserve" span="1"></col>
  </colgroup>
  <tbody space="preserve">
    <tr id="1_title" space="preserve">
      <td space="preserve" rowspan="1" colspan="1">
        <a id="fsdf" space="preserve" shape="rect"></a>
        <div class="sect2" space="preserve">
          <div class="titlepage" space="preserve">
            <div space="preserve">
              <div space="preserve">
                <h3 class="title" space="preserve">
                  <a id="" space="preserve" shape="rect"></a>
                  <span class="_default" space="preserve">TEXT</span>
                  <a class="link" href="../file1.pdf" target="_blank" space="preserve" shape="rect">
                    <img src="../images/app_pdf.png" space="preserve"></img> 
                  </a>
                </h3>
              </div>
            </div>
            <div space="preserve"></div>
          </div>
        </div>
      </td>
    </tr>
    <tr id="2_title" space="preserve">
      ....
    </tr>
  </tbody>
</table>
<iframe class="B" >
</iframe>
</body>
</html>

解决方案

核心问题是命名空间大小写错误,以及缺少对PDF链接的筛选逻辑,修正后的代码如下:

#r @"C:\...\.nuget\packages\fsharp.data\5.0.2\lib\netstandard2.0\FSharp.Data.dll"
open FSharp.Data // 修正命名空间大小写:FSharp.Data而非Fsharp.Data

let html = HtmlDocument.Load("test.html")
let pdfLinks =
    html.Descendants ["a"]
    |> Seq.choose (fun aTag ->
        // 1. 尝试获取href属性,忽略无href的a标签
        aTag.TryGetAttribute("href")
        // 2. 筛选出链接以.pdf结尾的项(大小写不敏感)
        |> Option.filter (fun attr -> attr.Value().EndsWith(".pdf", System.StringComparison.OrdinalIgnoreCase))
        // 3. 提取href属性的值
        |> Option.map (fun attr -> attr.Value()))
    |> Seq.toList

代码说明

  • 命名空间修正:FSharp.Data的命名空间是大写的F和S,之前的Fsharp.Data会导致API调用异常,这是无法获取<a>标签的关键原因。
  • 筛选逻辑:
    • TryGetAttribute安全获取属性,避免无href的<a>标签引发错误;
    • Option.filter只保留PDF链接,用OrdinalIgnoreCase确保.PDF这类大写后缀也能被识别;
    • 最终提取所有符合条件的href值并转为List。

可选:转换相对路径为绝对路径

如果需要把示例中的相对路径(如../file1.pdf)转为绝对路径,可以借助System.Uri:

open System

let baseUrl = Uri("https://your-website-base-url.com/")
let absolutePdfLinks =
    pdfLinks
    |> List.map (fun relativePath -> Uri(baseUrl, relativePath).AbsoluteUri)

内容的提问来源于stack exchange,提问作者RAh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 07:17:09