サブディレクトリ(*)には何百もの.xhtmlファイルがあり、そこから特定のクラスを含むすべてのDIV(およびそのDIVの全コンテンツ(他のdiv、範囲、画像、および段落要素を含む))を削除しようとしています。 DIVは、各.xhtmlファイル内の任意の深さで0回、1回、または複数回発生する可能性があります。
削除する特定のDIVは次のとおりです。
<div class="portlet solid author-note-portlet">.....</div>
xml_grep
Perlでユーティリティを使用するXML::木の枝モジュールを実行すると、xml_grep -v 'div[@class="portlet solid author-note-portlet"]' file*.xhtml
.xhtmlファイルからそのdivのすべてのインスタンスが削除され、結果がstdoutに表示されます。 「標準出力に表示」を除いて、まさに私が望むものです。
xml_grep
一種の内部編集オプションがあれば良いでしょう... ただ使用したいが、そうではないので、一時ファイルを使用するラッパースクリプトを書いたり、各sponge
。 xhtmlファイルを個別に作成すると、遅くて退屈になります。あるいは、入力ファイルを編集できるようにxml_grepのコピーをハックすることもできます。
しかし、私はそのようなことをしたくありません。すでにこれを行うことができる既存のツールを使用したいと思います。xmlstarlet
より速く、内部編集機能があり、そうする必要はありません。ファイル名ごとに1回実行します。
問題は、私が何を試しても(そして何十ものバリエーションを試しましたが)、このような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には2つのインスタンスがあり、同じ深さにあります...これは非常に一般的ですが普遍的ではありません)について次のように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">
作成者は、章にメモを追加するためにWebサイト(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
なので、グローバルオプションの1つではありません。
//xmlns:div
XPathは、名前空間で呼び出されたノードを繰り返し探します。div
xmlns
この質問では、名前空間を処理しないことに加えて、名前空間を過小または過度に指定しています。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-output
JSON出力の代わりに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
一方、他のツールの使い方を学ぶと、利点と満足感もあります。