Usando sed para filtrar itens RSS

Usando sed para filtrar itens RSS

Eu gostaria de escrever um script que:

  • Recebe algum URL do feed RSS como entrada
  • Baixa o feed
  • Exclui todas <item> ...</item>as ocorrências onde a titletag não corresponde a alguma expressão regular.

O exemplo a seguir deve ilustrar isso. Digamos que temos um RSS Feed com estes três itens:

  • Projeto Foo - Vamos começar!
  • Algo totalmente diferente
  • Outra atualização no Projeto Foo

Quero manter apenas os itens que possuem "Projeto Foo" no título.

Exemplo de arquivo de entrada:

<?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>

Exemplo de arquivo de saída:

<?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>

Se possível, gostaria de ficar longe de coisas assim pythone fazer isso com ferramentas de linha de comando. Mas sou um grande novato no uso de sedetc. e preciso de ajuda :)

Aqui está o que tenho até agora:

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

Primeiro, excluo todas as novas linhas. Em seguida, adiciono novas linhas para colocar cada uma <item>...</item>em sua própria linha. O comando final até agora exclui todas as linhas que começam com <item>. Para

O resultado é um feed rss válido sem nenhum item:

<?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>

Para fazer isso funcionar com URLs em vez de arquivos locais, eu apenas substituiria cat sample-feed.xmlpor curl -s <some url>.

O que ainda falta é uma modificação no comando sed '/^\<item\>/ d'que exclui apenas as linhas que começam com, <item>mas não contêm, "Project Foo".

Então, se você pudesse me ajudar a descobrir o que a última linha deveria dizer, eu ficaria muito feliz. Por outro lado, tenho certeza de que existe uma maneira mais elegante de fazer isso. Pelo que vi sedé bastante poderoso e deve ser possível fazer isso com um sedcomando.

Aguardo suas respostas :-)

Responder1

Conforme sugerido nos comentários, tentei usar xmlstarletpara resolver isso e funcionou bem. Aqui está meu roteiro

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

Vamos supor que o conteúdo do feed esteja no arquivo sample_rss.xml. Esse conteúdo é alimentado xml ed -d, o que exclui qualquer nota que corresponda à expressão XPath fornecida. A expressão XPath procura qualquer um <item> que não possua um nó <title>que contenha o texto "Project Foo".

Isso parece funcionar bem e também estou muito feliz com o tempo de execução:

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

Cuidado com os namespaces

Se você quiser fazer isso funcionar com feeds rss ou atom adequados, você pode notar que feedcontém um atributo XML Namespace ( xmlns), assim como neste exemplo do 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>

Então, o script acima não funcionará mais! Me causou muita dor de cabeça consertar isso, mas aqui está como fazer funcionar:

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

Mais sobre esse problema de namespace aqui:http://xmlstar.sourceforge.net/doc/UG/ch05.html

informação relacionada