xstarlet을 사용하여 XHTML에서 특정 클래스가 있는 div를 제거하는 방법은 무엇입니까?

xstarlet을 사용하여 XHTML에서 특정 클래스가 있는 div를 제거하는 방법은 무엇입니까?

하위 디렉터리(*)에 수백 개의 .xhtml 파일이 있는데, 여기에서 특정 클래스가 포함된 모든 DIV(및 해당 DIV의 전체 콘텐츠(다른 div, 범위, 이미지 및 단락 요소 포함))를 제거하려고 합니다. DIV는 각 .xhtml 파일 내의 모든 깊이에서 0회, 1회 또는 여러 번 발생할 수 있습니다.

삭제하려는 특정 DIV는 다음과 같습니다.

<div class="portlet solid author-note-portlet">.....</div>

xml_grepPerl에서 유틸리티 사용XML::나뭇가지모듈을 실행하면 xml_grep -v 'div[@class="portlet solid author-note-portlet"]' file*.xhtml.xhtml 파일에서 해당 div의 모든 인스턴스가 제거되고 결과가 stdout에 표시됩니다. "표준 출력에 표시"를 제외하고는 정확히 내가 원하는 것입니다.

xml_grep일종의 내부 편집 옵션이 있으면 좋을 텐데... 그냥 사용하고 싶지만 그렇지 않기 때문에 임시 파일을 사용하는 래퍼 스크립트를 작성하거나 각 sponge. xhtml 파일을 개별적으로 작성하면 느리고 지루할 것입니다. 또는 입력 파일을 편집할 수 있도록 xml_grep의 복사본을 해킹할 수도 있습니다.

하지만 저는 그런 일을 하고 싶지 않습니다. 이미 이 작업을 수행할 수 있는 기존 도구를 사용하고 싶습니다. xmlstarlet더 빠르고, 내부 편집 기능이 있으며, 그럴 필요가 없습니다. 파일 이름당 한 번씩 실행합니다.

문제는 내가 무엇을 시도하든(그리고 수십 가지 변형을 시도했지만) 이와 같은 div를 제거하기 위한 올바른 xpath 사양을 알아낼 수 없다는 것입니다. 예를 들어 다음을 시도했습니다.

xmlstarlet ed -d "div[@class='portlet solid author-note-portlet']" file.xhtml

그리고 (다른 참조로)

xmlstarlet ed -d 'div[@class="portlet solid author-note-portlet"]' file.xhtml

그리고

xmlstarlet ed -d '//html/body/div/div/div[@class="portlet solid author-note-portlet"]'

그리고 수십 가지 다른 변형. 그 중 어느 것도 xhtml 출력에 어떤 변화도 일으키지 않았습니다. 이때 보통 xmlstarlet을 포기하고 perl 스크립트를 작성하는데 이번에는 xmlstarlet을 사용하기로 결정했습니다.

그렇다면 xmlstarlet에 대해 이 div 클래스를 지정하는 올바른 방법은 무엇입니까?

그런데 샘플 .xhtml 파일(이 div에는 두 개의 인스턴스가 있으며 동일한 깊이에 있습니다... 이는 매우 일반적이지만 보편적이지 않음)에 대해 다음과 같이 xmlstarlet el -v말합니다.

$ xmlstarlet el -v OEBPS/file0007.xhtml | grep author-note-portlet
html/body/div/div[@class='portlet solid author-note-portlet']
html/body/div/div[@class='portlet solid author-note-portlet']

(*) 중요하지는 않지만 이러한 .xhtml 파일은 다음 위치에 있습니다.팬픽션 요금플러그인구경- 다양한 소설 웹사이트에 있는 책의 모든 장을 다운로드하여 epub 파일(기본적으로 XHTML 및 CSS 파일, jpeg 또는 gif 파일 및 여러 메타데이터 파일이 포함된 zip 아카이브)로 변환합니다.

<div class="portlet solid author-note-portlet">작성자가 장에 메모를 추가하기 위해 웹사이트(Royal Road)에서 사용됩니다. 일부 저자는 이를 아껴서 장이나 책에 대한 간략한 메모를 삽입하거나 구독자 페이지 링크와 함께 임의의 콘텐츠에 대한 간략한 발표를 삽입합니다. 음, 별 문제는 아닙니다.

다른 사람들은 이를 사용하여 반 페이지 분량의 메모를 추가하고 처음에 다른 10권의 책에 대한 링크를 추가합니다.그리고 다시 각 장의 끝에 이 책에 대한 링크(표지 이미지 포함)를 3페이지 반씩 추가합니다. 웹사이트에서 연재 형식으로 한 장씩 읽으면 괜찮지만, 책으로 읽으면 그렇지 않습니다. - 스토리의 6~10명당 자기 홍보 내용이 4페이지 페이지 수가 너무 많아 방해가 됩니다. . 그건 그렇고, 내 10인치 Android 태블릿에는 4개의 "페이지"가 ​​있는데, 이는 내 휴대폰에 있는 것보다 두 배 이상 많은 것입니다.

이 클래스를 epub 스타일시트에 쉽게 추가할 수 있지만 display: none실제로는 .xhtml 파일에서 div를 제거하고 싶습니다. .epub 파일의 크기가 크게 늘어납니다.

(**) unzip을 사용하여 .epub의 콘텐츠를 추출한 후 다시 빌드하는 것은 이 질문의 범위를 벗어나므로 관련 없는 세부 사항으로 인해 주의가 산만해지지 않도록 하십시오. 이미 처리되었습니다.


최소한으로 편집된 .xhtml 파일 예("유죄:-)"를 보호하기 위해 익명으로 스토리/챕터/저자 이름:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Chapter Five - Chapter Name</title>
<link href="stylesheet.css" type="text/css" rel="stylesheet"/>
<meta name="chapterurl" content="https://www.royalroad.com/fiction/URL"/>
<meta name="chapterorigtitle" content="Chapter Five - Chapter Name"/>
<meta name="chaptertoctitle" content="Chapter Five - Chapter Name"/>
<meta name="chaptertitle" content="Chapter Five - Chapter Name"/>
</head>
<body class="fff_chapter">
<h3 class="fff_chapter_title">Chapter Five - Chapter Name</h3>
<div class="chapter-inner chapter-content"><div class="portlet solid author-note-portlet">
                    <div class="portlet-title">
                        <div class="caption">
                            <i class="fa fa-sticky-note"></i>
                            <span class="caption-subject bold uppercase">A note from Author Name</span>
                        </div>
                    </div>
                    <div class="portlet-body author-note"><p><span>About a dozen or so p, span, img, and br tags here</span></p>
</div>
                </div>
<p> story text here.  a few hundreds p, br, etc tags
</p>
            <div class="portlet solid author-note-portlet">
                    <div class="portlet-title">
                        <div class="caption">
                            <i class="fa fa-sticky-note"></i>
                            <span class="caption-subject bold uppercase">A note from Author Name</span>
                        </div>
                    </div>
                    <div class="portlet-body author-note"><p>several dozen more p, span, br, img, etc tags here</p>
</div>
                </div>
</div>
</body>
</html>

답변1

올바른 방법 xmlstarlet

xmlstarlet ed --inplace -N xmlns="http://www.w3.org/1999/xhtml" \
    --delete '//xmlns:div[@class="portlet solid author-note-portlet"]' file

또는 짧은 옵션을 사용하세요.

xmlstarlet ed -L -N xmlns="http://www.w3.org/1999/xhtml" \
    -d '//xmlns:div[@class="portlet solid author-note-portlet"]' file

문서는 기본 네임스페이스를 사용하므로 xmlstarlet모든 노드를 해당 네임스페이스에 속하게 한 다음 네임스페이스 자리 표시자를 XPath 표현식의 노드 이름에 대한 접두사로 사용해야 합니다.

문서에 따르면 마지막 "전역 옵션"이어야 합니다. 즉, (다른 전역 옵션) -N뒤에 와야 합니다 . -L-d"작업 삭제" xmlstarlet ed이므로 전역 옵션 중 하나가 아닙니다.

//xmlns:divXPath는 네임스페이스에서 호출된 노드를 반복적으로 찾습니다 .divxmlns

이 질문에서는 네임스페이스를 처리하지 않는 것 외에도 네임스페이스를 과소 또는 과도하게 지정하고 있습니다. div와 동일하게 를 사용하면 /div루트 노드와 일치하며 어디에서나 직계 하위 노드 //html/body/div/div/div와 일치합니다 .html/body/div/div


포장지 yq(안드레이 키슬리크)JSON 프로세서를 기반으로 jq구축XML 파서 래퍼가 호출됩니다.xq. 다음과 같이 사용할 수도 있습니다.

xq -x 'del(.. | .div? | select(."@class"? == "portlet solid author-note-portlet"))' file

-x( ) 옵션은 --xml-outputJSON 출력 대신 XML 출력을 제공합니다. ( ) xq와 함께 -i사용하면 --in-place그 자리에서 편집할 수 있습니다.

이 XML 파서는 네임스페이스에 관심이 없습니다.

답변2

xml_grep별도의 참고 사항은 다음을 사용하여 필요한 필터링을 구현할 수 있다는 점입니다.

    mkdir temp
    for file in <subdir>/*.xhtml; do
        # Your magic xml_grep command
        xml_grep -v 'div[@class="portlet solid author-note-portlet"]' "$file" > "temp/$file"
    done
    rm -r subdir
    mv temp subdir

반면에 다른 도구를 사용하는 방법을 배우면 이점과 만족감도 있습니다.

관련 정보