XML 파일에서 텍스트 추출

XML 파일에서 텍스트 추출

다음과 같은 항목이 포함된 XML 파일이 있는 경우

<root>
  <d:entry d:title="OYSTER">
    <span class="foot">
      <span role="text">
      foo</span>
    </span>
    <span class="sg">
      <span id="004">
    <span role="text">
      <span class="pos">
        <span class="baz">tart</span>
        <d:pos></d:pos>
      </span>
    </span>
    <span id="005" class="star">
      <span class="NAME">GUYBRUSH THREEPWOOD
      <d:def></d:def></span>
      <span role="text" class="bar">:</span>
      <span role="text" class="grog">
        <span class="ex">pirate
        </span>
        <span class="parrot">.</span>
      </span>
    </span>
      </span>
    </span>
  </d:entry>
</root>

(d:) 제목 "OYSTER"와 클래스 "NAME"을 제공하여 "GUYBRUSH THREEPWOOD" 텍스트를 추출하려면 어떻게 해야 합니까?

답변1

xq(YAML, XML 및 TOML을 사용하는 유사한 파서 모음 yq의 일부)jqhttps://kislyuk.github.io/yq/), xmlstarlet누락된 네임스페이스의 선언이 너무 엄격하기 때문입니다( xmlstarlet어쨌든 질문 끝에 있는 해결 방법을 참조하세요).

xq -r --arg title "OYSTER" --arg class "NAME" '
    (.. | select(."@d:title"? == $title)) |
    (.. | select(."@class"?   == $class))."#text"' file.xml

d:title이는 값이 있는 속성(표현식에 사용된 첫 번째 문자는 노드 이름이 아닌 노드 속성을 나타냄)을 가진 모든 문서 노드를 재귀적으로 선택합니다 .@OYSTER

이러한 노드(예제에서는 하나만)가 주어지면 class값 속성이 있는 모든 노드 에 대해 반복적으로 검색됩니다 NAME.

각 노드에 대해 해당 노드의 값을 추출합니다.

문자열 OYSTERNAMEvia 옵션은 모두 명령줄의 내부 변수에 바인딩됩니다 --arg.

문제의 문서의 출력을 제공합니다.

GUYBRUSH THREEPWOOD

다른 노드가 속성을 d:entry가질 수 d:title있고/또는 다른 노드가 속성을 span가질 수 class있고 잘못된 유형의 노드에서 해당 속성을 일치시키지 않으려면 적절한 노드만 보아야 합니다.

xq -r --arg title "OYSTER" --arg class "NAME" '
    (.. | ."d:entry"? | select(."@d:title"? == $title)) |
    (.. | .span?[]?   | select(."@class"?   == $class))."#text"' file.xml

참고로 호출은 xq실제로 jq배후에서 JSON 문서를 사용하므로 XML 문서가 변환되는 JSON 문서는 다음과 같습니다.

{
  "root": {
    "d:entry": {
      "@d:title": "OYSTER",
      "span": [
        {
          "@class": "foot",
          "span": {
            "@role": "text",
            "#text": "foo"
          }
        },
        {
          "@class": "sg",
          "span": {
            "@id": "004",
            "span": [
              {
                "@role": "text",
                "span": {
                  "@class": "pos",
                  "span": {
                    "@class": "baz",
                    "#text": "tart"
                  },
                  "d:pos": null
                }
              },
              {
                "@id": "005",
                "@class": "star",
                "span": [
                  {
                    "@class": "NAME",
                    "d:def": null,
                    "#text": "GUYBRUSH THREEPWOOD"
                  },
                  {
                    "@role": "text",
                    "@class": "bar",
                    "#text": ":"
                  },
                  {
                    "@role": "text",
                    "@class": "grog",
                    "span": [
                      {
                        "@class": "ex",
                        "#text": "pirate"
                      },
                      {
                        "@class": "parrot",
                        "#text": "."
                      }
                    ]
                  }
                ]
              }
            ]
          }
        }
      ]
    }
  }
}

문서에 올바른 d네임스페이스 선언이 있다고 가정하면 xmlstarlet다음과 같이 필요한 텍스트를 추출하는 데 사용할 수 있습니다.

xmlstarlet sel -t \
    -m '//d:entry[@d:title = "OYSTER"]' \
    -v '//span[@class = "NAME"]' -nl file.xml

또는 명령줄에서 내부 변수를 설정합니다 --var(값의 따옴표 참고).

xmlstarlet sel -t --var title='"OYSTER"' --var class='"NAME"' \
    -m '//d:entry[@d:title = $title]' \
    -v '//span[@class = $class]' -nl file

둘 다 d:entry속성이 일치하는 노드로 시작합니다. 일치하는 각 노드에 대해 값 속성이 있는 노드를 반복적으로 찾습니다. 각 노드의 값을 인쇄합니다.d:titleOYSTERspanclassNAME

관련 정보