Использование sed для фильтрации элементов RSS

Использование sed для фильтрации элементов RSS

Я хотел бы написать сценарий, который:

  • Принимает в качестве входных данных URL-адрес RSS-канала
  • Загружает ленту
  • Удаляет все <item> ...</item>вхождения, где titleтег не соответствует какому-либо регулярному выражению.

Следующий пример должен это проиллюстрировать. Допустим, у нас есть RSS-канал с этими тремя элементами:

  • Проект Foo — Начнем!
  • Что-то совсем другое
  • Еще одно обновление Project Foo

Я хочу оставить только те элементы, в названии которых есть «Project Foo».

Пример входного файла:

<?xml version="1.0" encoding="iso-8859-1"?>
<rss version="2.0">
<channel>
<title>My glorious newsfeed</title>
<description>...</description>
<link>...</link>
<language>...</language>
<pubDate>...</pubDate>

<item>
<title>Project Foo - Let's get started!</title>
<link>...</link>
<description>...</description>
<pubDate>...</pubDate>
</item>

<item>
<title>Something else entirely</title>
<link>...</link>
<description>...</description>
<pubDate>...</pubDate>
</item>

<item>
<title>Another update on Project Foo</title>
<link>...</link>
<description>...</description>
<pubDate>...</pubDate>
</item>

</channel>
</rss>

Пример выходного файла:

<?xml version="1.0" encoding="iso-8859-1"?>
<rss version="2.0">
<channel>
<title>My glorious newsfeed</title>
<description>...</description>
<link>...</link>
<language>...</language>
<pubDate>...</pubDate>

<item>
<title>Project Foo - Let's get started!</title>
<link>...</link>
<description>...</description>
<pubDate>...</pubDate>
</item>

<item>
<title>Another update on Project Foo</title>
<link>...</link>
<description>...</description>
<pubDate>...</pubDate>
</item>

</channel>
</rss>

Если возможно, я бы хотел держаться подальше от подобных pythonи делать это с помощью инструментов командной строки. Но я большой новичок в использовании sedи т. д. и мне нужна помощь :)

Вот что у меня есть на данный момент:

cat sample-feed.xml \
  | tr -d '\n' \
  | sed $'s/\<item\>/\\\n\<item\>/g;s/\<\/channel\><\/rss\>/\\\n\<\/channel\><\/rss\>/g' \
  | sed '/^\<item\>/ d'

Сначала я удаляю все новые строки. Затем я добавляю новые строки, чтобы каждая строка была <item>...</item>на своей собственной строке. Последняя команда на данный момент удаляет все строки, которые начинаются с <item>. Для

Результат — корректный RSS-канал без каких-либо элементов:

<?xml version="1.0" encoding="iso-8859-1"?><rss version="2.0"><channel><title>My glorious newsfeed</title><description>...</description><link>...</link><language>...</language><pubDate>...</pubDate>    
</channel></rss>

Чтобы это работало с URL-адресами вместо локальных файлов, я бы просто заменил cat sample-feed.xmlна curl -s <some url>.

Однако по-прежнему не хватает модификации команды, sed '/^\<item\>/ d'которая удаляет только строки, начинающиеся с <item>«Project Foo», но не содержащие его.

Итак, если бы вы могли помочь мне разобраться, что должна быть в последней строке, я был бы очень рад. С другой стороны, я уверен, что есть более элегантный способ сделать это. Из того, что я видел, sedэто довольно мощно, и это должно быть возможно сделать одной sedкомандой.

С нетерпением жду ваших ответов :-)

решение1

Как и предлагалось в комментариях, я попробовал использовать xmlstarletдля решения этой проблемы, и это работает хорошо. Вот мой скрипт

xml ed -d '//item[not(contains(title,"Project Foo"))]' < sample_rss.xml

Предположим, что содержимое фида находится в файле sample_rss.xml. Это содержимое передается в xml ed -d, что удаляет любую заметку, соответствующую заданному выражению XPath. Выражение XPath ищет любую, <item> которая не имеет узла <title>, содержащего текст "Project Foo".

Кажется, это работает хорошо, и я также очень доволен временем выполнения:

real    0m0.003s
user    0m0.001s
sys     0m0.002s

Остерегайтесь пространств имен

Если вы хотите, чтобы это работало с правильными каналами RSS или Atom, вы можете заметить, что feedсодержит атрибут XML Namespace ( xmlns), как в этом примере с YouTube:

<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns:yt="http://www.youtube.com/xml/schemas/2015" xmlns:media="http://search.yahoo.com/mrss/" xmlns="http://www.w3.org/2005/Atom">
   ...
</feed>

Тогда скрипт выше больше не будет работать! Мне пришлось изрядно помучиться, чтобы его исправить, но вот как заставить его работать:

xml ed -d '//_:entry[not(contains(_:title,"Project Foo"))]' < youtube_rss.xml

Подробнее об этой проблеме пространства имен можно узнать здесь:http://xmlstar.sourceforge.net/doc/UG/ch05.html

Связанный контент