Navigace

Hlavnφ menu

 

RSS p°φmo z webu pomocφ .NET t°φdy SgmlReader

RSS kanßl je standardnφm dopl≥kem dneÜnφch webzin∙. P°esto mezi nimi existujφ ΦernΘ ovce, kterΘ tuto technologii zarputile ignorujφ, nebo dokonce odmφtajφ. Vytvo°me pro n∞ tedy parazitnφ RSS kanßl v prost°edφ Visual Studio .NET.

V tomto Φlßnku budou popsßny techniky, kterΘ lze vyu₧φt nejen na vytvß°enφ RSS kanßl∙, ale obecn∞ pro jakΘkoli sbφrßnφ dat z www strßnek. Myslφm si, ₧e Φtenß°i Intervalu jsou na ·rovni a nebudou tyto dovednosti zneu₧φvat proti v∙li autor∙ web∙. Jß jsem si jako testovacφ web vybral magazφn Interval.cz, a to i p°esto, ₧e ji₧ mß sv∙j vlastnφ RSS kanßl.

Princip

V²sledkem prßce bude webovß aplikace, kterou m∙₧ete umφstit nap°φklad na svΘm poΦφtaΦi, nebo kdekoli na svΘm serveru. Jejφm prvnφm ·kolem je sta₧enφ obsahu vybranΘ (nejspφÜe titulnφ) strßnky webzinu, kterß (pokud mo₧no) obsahuje seznam Φlßnk∙ Φi spot∙ vΦetn∞ n∞jak²ch p°φpadn²ch dopl≥kov²ch informacφ (perex, jmΘno autora Φi datum vydßnφ). Z obsahu strßnky je pomocφ knihovny SgmlReader zφskßn XmlDocument, z n∞ho₧ ji₧ XML transformacφ vytßhneme po₧adovanΘ data a zpracujeme do RSS kanßlu. To je vÜe. Tak₧e jdeme na to!

Sta₧enφ dat a SgmlReader

Zalo₧φme nov² projekt typu ASP.NET Web Application a v n∞m vytvo°φme strßnku rss.aspx. Jejφ HTML k≤d bude velmi jednoduch², bude obsahovat pouze komponentu Xml, do kterΘ ho se budou naΦφtat vstupnφ data pro kanßl, a kterß bude nad t∞mito daty provßd∞t transformaci. Proto vytvo°φme transformaΦnφ stylesheet data.xslt, kter² prozatφm ponechßme prßzdn², a na°φdφme komponent∞, aby jej pou₧φvala pomocφ atributu TransformSource. Musφme takΘ nastavit v²stupnφ MIME typ strßnky na text/xml, abychom sv∞tu sd∞lili, ₧e naÜe strßnka generuje data ve formßtu XML.

<%@ Page language="c#" Codebehind="rss.aspx.cs"
  ContentType="text/xml" Inherits="interval.rss" %>
<asp:Xml id="data" TransformSource="data.xslt"
  runat="server"></asp:Xml>

N∞kolikrßt zde byla zmφn∞na knihovna SgmlReader. O co krßΦφ? Jak vφme, .NET Framework poskytuje sadu t°φd pro zpracovßnφ XML dokument∙. Celß °ada web∙ je vÜak psßna ve starΘm (a Φasto nevalidnφm) HTML, a mß dost daleko do XHTML (co₧ je vlastn∞ implementace XML). Obsah t∞chto web∙ tedy nelze t∞mito t°φdami p°φmo zpracovat. SgmlReader je t°φda, kterß d∞dφ z XmlReader, tak₧e poskytuje standardnφ rozhranφ pro Φtenφ Xml dokument∙, avÜak spokojφ se i s pom∞rn∞ "sprasen²m k≤dem". Voliteln∞ lze zapnout logovßnφ nalezen²ch chyb, ovÜem ty mohou b²t ignorovßny, resp. vhodn²m zp∙sobem kompenzovßny. SGML je standard, ze kterΘho vychßzφ HTML i XML a je to pom∞rn∞ star² a velmi tolerantnφ znaΦkovacφ jazyk. SgmlReader je jeÜt∞ tolerantn∞jÜφ. Nejd°φve si tedy tuto knihovnu stßhn∞te, rozbalte, a projekt SgmlReaderDll.csproj ze slo₧ky SgmlReader\SgmlReaderDll p°idejte do naÜφ aplikace (solution). Nezapome≥te tento projekt takΘ p°idat mezi reference naÜeho ASP.NET projektu.

T°φda SgmlReader jako sv∙j vstup oΦekßvß TextReader, tak₧e budeme muset data z HTTP odpov∞di dek≤dovat n∞jak²m k≤dovßnφm, abychom zφskali z binßrnφch dat jejich textovou reprezentaci. Jak zjistφme, v jakΘm k≤dovßnφ je HTML strßnka zapsßna? To je docela oÜemetnß zßle₧itost, proto₧e tato informace se m∙₧e ukr²vat bu∩ v XHTML hlaviΦce, nebo ve znßmΘm META elementu. Programov∞ tyto informace zφskat nenφ prßv∞ hraΦka. Jsme v bezradnΘ situaci. Abychom mohli data p°edat SgmlReaderu, pot°ebujeme v nich najφt specifikaci k≤dovßnφ, tak₧e musφme nejd°φve strßnku analyzovat, a k tomu nejlΘpe slou₧φ prßv∞ SgmlReader. Je to vlastn∞ bludn² kruh.

NaÜt∞stφ tento ·kol je °eÜiteln², a °eÜenφ je ukryto op∞t v tΘto nesmφrn∞ u₧iteΦnΘ knihovn∞. Zapßtrßme-li d∙kladn∞ji ve jmennΘm prostoru Sgml, mo₧nß zavadφme pohledem o t°φdu HtmlStream. Ten nßzev znφ velmi slibn∞, nemyslφte? Tato t°φda d∞dφ z TextReaderu (vzpome≥te si, jde o vy₧adovan² vstupnφ formßt pro SgmlReader), a jejφ konstruktor mß dva parametry: jeden typu Stream (lze p°φmo napojit v²stup z WebResponse), a druh² se naz²vß defaultEncoding a jde o k≤dovßnφ znak∙, kterΘ je pou₧ito implicitn∞, pokud nenφ nalezena explicitnφ definice ve strßnce. Celß t°φda mß podstatnou nev²hodu: je deklarovßna jako internφ, tak₧e ji nelze pou₧φt v naÜem projektu. ZdrojovΘ k≤dy mßme (SgmlParser.cs), nenφ tedy nic snazÜφho, ne₧ p°epsat v hlaviΦce deklarace tΘto t°φdy klφΦovΘ slovo internal na public. Pak projekt SgmlReaderu zkompilujte.

Nynφ tedy znßme zp∙sob, jak z k≤du libovolnΘ strßnky m∙₧eme zφskat XmlReader. NapiÜme si tedy funkci, kterß nßm stßhne XmlDocument z URL adresy p°edanΘ parametrem jako °et∞zec. Takovou funkce bude jist∞ znovupou₧itelnß v ·pln∞ jin²ch projektech. P°ejd∞me do k≤du strßnky a nadeklarujme pot°ebnΘ jmennΘ prostory:

using Sgml;
using System.Net;
using System.IO;
using System.Xml;
using System.Text;

A nynφ ji₧ slφbenß metoda:

public XmlDocument GetPage(string url)
{
  // http po₧adavek
  WebRequest request = WebRequest.Create(url);
  WebResponse response = request.GetResponse();
  TextReader stream = new HtmlStream(
      response.GetResponseStream(),
      Encoding.GetEncoding("iso-8859-2"));

  // zpracovßnφ SgmlReaderem do stringu
  SgmlReader reader = new SgmlReader();
  reader.InputStream = stream;
  reader.DocType = "HTML";
  StringWriter str = new StringWriter();
  XmlTextWriter xml = new XmlTextWriter(str);
  reader.Read();
  while(!reader.EOF)
  {
    xml.WriteNode(reader,true);
  }
  xml.Flush();
  xml.Close();
  response.Close();
  stream.Close();

  // naΦtenφ stringu do XmlDocumentu
  XmlDocument data = new XmlDocument();
  data.LoadXml(str.ToString());
  return data;
}

Mo₧nß vßs p°ekvapilo, ₧e data z XmlReaderu do XmlDocumentu cestujφ pon∞kud krkolomn∞ uzel po uzlu p°es XmlWriter do stringu, a ten je pak cel² naΦten do DOM struktury. ProΦ to, kdy₧ by jist∞ bylo mnohem jednoduÜÜφ a takΘ v²konn∞jÜφ vyu₧φt p°etφ₧enou metodu XmlDocumentu Load, jejφ₧ jedna varianta p°φmo Φte z XmlReaderu? Tedy jednoduÜe takto:

  data.Load(reader);

Je to proto, ₧e XmlDocument nenφ z mi neznßmΘho d∙vodu se SgmlReaderem zcela kompatibilnφ. Zkuste a uvidφte. XmlDocument z°ejm∞ neuznß posloupnost naΦten²ch XML uzl∙ za sprßvnou. Mo₧nß jsem n∞co p°ehlΘdl, a problΘm lze vy°eÜit jinak, ovÜem spolehliv∞ mi fungoval pouze dotyΦn² obchvat p°es string.

Nynφ naÜi metodu pou₧ijeme k naΦtenφ strßnky z Intervalu a jejφmu p°edßnφ do Xml komponenty data. Tuto akci provedeme v t∞le oblφbenΘ metody Page_Load.

private void Page_Load(object sender, System.EventArgs e)
{
  data.Document = GetPage("http://interval.cz/");
}

Transformace

KoneΦn∞ jsme v poslednφ etap∞ projektu. Strßnku mßme ve snadno zpracovatelnΘm tvaru, napsßnφ XSL transformace je ji₧ trivialita. Webovß strßnka je nynφ vydßna na milost (Φi nemilost) vaÜim XPath v²raz∙m. Je jen na Vßs, kolik informacφ z nφ zvlßdnete vydolovat. Nßsledujφcφ p°φklad generuje jen pßr zßkladnφch RSS element∙, ovÜem vy samoz°ejm∞ m∙₧ete jφt dßle a vytvo°it pro svou RSS ΦteΦku pon∞kud komplexn∞jÜφ stravu.

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="1.0"
  xmlns:html="http://www.w3.org/1999/xhtml"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  exclude-result-prefixes="xsl html">
  <xsl:output method="xml" indent="yes" />

  <xsl:template match="/">
    <rss version="2.0">
      <channel>
        <title>
          <xsl:value-of select="//html:title" />
        </title>
        <xsl:for-each
          select="//html:div[@class='anotation']">
          <item>
            <title>
              <xsl:value-of
                select=".//html:h2" />
            </title>
            <link>
              <xsl:text>http://interval.cz/</xsl:text>
              <xsl:value-of
                select=".//html:h2/html:a/@href" />
            </link>
            <description>
              <xsl:value-of
                select=".//html:p" />
            </description>
          </item>
        </xsl:for-each>
      </channel>
    </rss>
  </xsl:template>

</xsl:stylesheet>

K dispozici jsou vßm kompletnφ zdrojovΘ k≤dy aplikace.

Odkazy a zdroje

B°φza, Petr (28. 12. 2004)

Syndikace obsahu - formßty, techniky, v²voj

Syndikace obsahu je modernφ metodou sdφlenφ velk²ch objem∙ rychle se m∞nφcφch informacφ na internetu. Soubor naÜich text∙ popisuje specifikace jednotliv²ch formßt∙ a nßstroje pro prßci s daty v nich p°edßvan²mi. Tato sΘrie Φlßnk∙ ji₧ byla uzav°ena, aΦkoli dalÜφ pokraΦovßnφ nelze vylouΦit.