Czytnik RSS i Atom w PHP
RSS (Really Simple Syndication) i Atom to dwa standardy wykorzystywane do dystrybucji treści w sieci, zwłaszcza wiadomości i aktualizacji z blogów, serwisów informacyjnych i innych źródeł online. Oba mają na celu umożliwienie użytkownikom subskrybowania treści ze stron internetowych aby byli na bieżąco z najnowszymi aktualizacjami bez konieczności odwiedzania tych stron.
RSS (Really Simple Syndication)
- Został stworzony w oparciu o język XML.
- Istnieje kilka wersji RSS z których najpopularniejsze to RSS 2.0.
- Struktura RSS jest stosunkowo prosta co sprawia, że jest łatwy w implementacji.
- Standard RSS umożliwia udostępnianie podstawowych informacji o treści takich jak tytuł, link i krótki opis.
Atom
- Również oparty na języku XML, ale jest bardziej rozbudowany i elastyczny niż RSS.
- Stworzony w odpowiedzi na różne wersje i niejasności w specyfikacjach RSS.
- Atom zawiera bardziej szczegółowe informacje takie jak autor, identyfikator (unikatowy dla każdego wpisu) oraz informacje o aktualizacjach.
- Dzięki swojej bogatej strukturze Atom jest bardziej wszechstronny, ale może być też nieco bardziej skomplikowany w implementacji niż RSS.
Chociaż oba standardy mają podobne cele różnią się strukturą i możliwościami. Wybór między nimi zależy od konkretnych potrzeb i wymagań projektu. Wielu twórców treści udostępnia kanały zarówno w formacie RSS jak i Atom, aby zaspokoić potrzeby różnych użytkowników i czytników.
Popularne czytniki
- Feedly – Jedna z najpopularniejszych aplikacji do czytania kanałów RSS. Działa na przeglądarkach internetowych oraz posiada aplikacje mobilne.
- Inoreader – Kolejny popularny czytnik, który oferuje wiele funkcji oraz aplikacje na różne platformy.
- The Old Reader – Prosty i intuicyjny czytnik dla tych, którzy szukają podstawowego rozwiązania.
Aby korzystać z czytnika
- Znajdź link do kanału RSS/Atom na stronie, którą chcesz subskrybować (często jest to ikonka w postaci fal radiowych lub napis „RSS”).
- Skopiuj link.
- Otwórz swój czytnik RSS/Atom i dodaj nowy kanał wklejając skopiowany link.
Teraz gdy strona doda nową treść będzie ona automatycznie pojawiać się w Twoim czytniku. Dzięki temu możesz śledzić wiele stron w jednym miejscu bez konieczności przeszukiwania Internetu w poszukiwaniu najnowszych informacji. To nie tylko oszczędza czas, ale również pozwala na bardziej efektywne zarządzanie treścią, gdyż wszystko jest gromadzone w jednym, uporządkowanym interfejsie.
Dodatkowo, większość czytników RSS/Atom pozwala na organizację kanałów w kategorie, co ułatwia segregację informacji według interesujących Cię tematów. Możesz także oznaczać poszczególne wpisy jako przeczytane lub nieprzeczytane, co jest szczególnie przydatne, gdy śledzisz wiele kanałów i chcesz wrócić do pewnych artykułów później.
Niektóre czytniki oferują również funkcje społecznościowe pozwalając użytkownikom na udostępnianie interesujących ich artykułów z innymi czy też komentowanie wpisów bezpośrednio w aplikacji. Dzięki temu czytniki RSS/Atom nie tylko upraszczają konsumpcję treści, ale również mogą stać się platformą interakcji i wymiany informacji między użytkownikami.
Własny czytnik RSS w PHP
Pełna specyfikacja formatu RSS 2.0 znajduje się na stronie rssboard.org. Dla celów niniejszego kursu nie będziemy opracowywać pełnej obsługi formatu, a skupimy się na odczytaniu kanału RSS Google News.
Google udostępnia kanał RSS o lemurach jako przykład kanału wyników wyszukiwania
Przyjrzyjmy się budowie tego pliku
<rss xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
<channel>
<generator>NFE/5.0</generator>
<title>"lemurs" - Wiadomości Google</title>
<link>https://news.google.com/search?q=lemurs&hl=pl&gl=PL&ceid=PL:pl</link>
<language>pl</language>
<webMaster>news-webmaster@google.com</webMaster>
<copyright>2023 Google Inc.</copyright>
<lastBuildDate>Sun, 10 Sep 2023 00:58:12 GMT</lastBuildDate>
<description>Wiadomości Google</description>
<item>
<title>Lemur – opis, występowanie i zdjęcia. Zwierzę lemur ciekawostki - Portal ekologiczny</title>
<link>https://news.google.com/rss/articles/...</link>
<guid isPermaLink="false">...</guid>
<pubDate>Thu, 13 Oct 2022 01:30:50 GMT</pubDate>
<description>opis</description>
<source url="https://www.ekologia.pl">Portal ekologiczny</source>
</item>
<item>
...
</item>
</channel>
</rss>
Wszystkie informacje zawierają się w tagach rss
i channel
wewnątrz którego są elementy item
z konkretnymi wiadomościami.
Wyciąganie danych
Wyciąganie danych z pliku możemy spróbować zrealizować w podobny sposób jak to robiliśmy z parsowaniem strony w HTML, albo skorzystać z dedykowanej, wbudowanej i popularnej biblioteki SimpleXML.
$url = 'https://news.google.com/news?ned=pl_pl&q=lemurs&output=rss';
$data = new SimpleXMLElement($url, 0, true);
W pierwszym parametrze podaliśmy URL i dlatego trzeci parametr powinien być ustawiony na true. Drugi natomiast zawiera dodatkowe opcje i często jest używany jak musimy np. pobrać dane z zewnętrzego webservice (np. LIBXML_PARSEHUGE
jeżeli dokument jest duży, LIBXML_NOCDATA
jeżeli zawiera dane w formie CDATA). Do tego konkretnego pliku nie musimy ustawiać żadnych dodatkowych parametrów.
Informacje o wersji RSS wyciągniemy z atrybutu version
w tagu głównym rss
(nie musimy go podawać podczas wyciągania danych). Zanim rozpoczniemy parsowanie, sprawdzimy jeszcze czy deklarowana w pliku wersja RSS się zgadza z obsługiwana. Zrobimy to za pomocą wbudowanej funkcji version_compare
. Do wyciągania danych z taga służy metoda attributes()
.
$rss_version = (string) $data->attributes()->version;
if (version_compare ($rss_version, "2.0") != 0) die('Nieobsługiwana wersja RSS');
Zwróć uwagę, że atrybut musimy zawsze zamieniać na oczekiwany format danych (najczęściej string).
Informacje o kanale znajdziemy w tagu channel
.
$channel = [];
$channel['generator'] = (string) $data->channel->generator;
$channel['title'] = (string) $data->channel->title;
$channel['link'] = (string) $data->channel->link;
$channel['language'] = (string) $data->channel->language;
$channel['webMaster'] = (string) $data->channel->webMaster;
$channel['copyright'] = (string) $data->channel->copyright;
$channel['lastBuildDate'] = (string) $data->channel->lastBuildDate;
$channel['description'] = (string) $data->channel->description;
Informacje o poszczególnych elementach są zaś przekazywane w item
. Aby je wyciągnąć wystarczy skorzystać z pętli.
$items = [];
foreach ($data->channel->item as $item) {
$itemData = [];
$itemData ['title'] = (string) $item->title;
$itemData ['link'] = (string) $item->link;
$itemData ['guid'] = (string) $item->guid;
$itemData ['isPermalink'] = (bool) $item->guid->attributes()->isPermaLink;
$itemData ['pubDate'] = (string) $item->pubDate;
$itemData ['description'] = (string) $item->description;
$itemData ['source'] = (string) $item->source;
$itemData ['url'] = (string) $item->source->attributes()->url;
$items[] = $itemData;
}
Prezentacja danych
Pozostaje tylko już wyświetlić pobrane dane. Skorzystamy przy tym z biblioteki bootstrap
.
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.1/dist/js/bootstrap.bundle.min.js"></script>
<div class="container">
<div class="card">
<div class="card-header">Czytnik RSS Google News</div>
<div class="card-body">
<dl class="row">
<?php
foreach ($channel as $name => $value) {
echo '<dt class="col-sm-3">'.$name.'</dt>';
echo '<dd class="col-sm-9">'.$value.'</dd>';
}
?>
</dl>
<div class="accordion" id="news">
<?php
$id = 1;
foreach ($items as $item) {
$itemName = 'item_'.$id;
$id++;
?>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#<?php echo $itemName?>" aria-expanded="false" aria-controls="#<?php echo $itemName?>">
<?php echo $item['title']; ?>
</button>
</h2>
<div id="<?php echo $itemName?>" class="accordion-collapse collapse" data-bs-parent="#news">
<div class="accordion-body">
<?php echo $item['description']; ?>
</div>
</div>
</div>
<?php } ?>
</div>
</div>
</div>
</div>
Dla przykładu podpiąłem kanał z wiadomościami. Jak widać na poniższym zrzucie ekranu, Google zagregował podobne wiadomości z wielu różnych portali i wyświetlił linki do stosownych artykułów.
Z linków możemy wyciągnąć informacje z poszczególnych serwisów. Nie będziemy się tym tutaj zajmować, ale jak jesteś zainteresowany do odsyłam do artykułu o pobieraniu strony offline w PHP.
Własny czytnik Atom w PHP
Jak już wspomniałem – Atom jest nowszym standardem. Oficjalna specyfikacja powszechnie używanej wersji Atom 1.0 znajduje się na stronie jetf.org.
Nie będziemy dodawać obsługi wszystkiego, a podobnie jak poprzednio skupimy się na kanale o lemurach:
Ponownie przyjrzyjmy się budowie pliku
<feed xmlns="http://www.w3.org/2005/Atom">
<id>https://news.google.com/atom/search?q=lemurs&hl=pl&gl=PL&ceid=PL:pl</id>
<generator>NFE/5.0</generator>
<title type="html">"lemurs" - Wiadomości Google</title>
<subtitle>Wiadomości Google</subtitle>
<updated>2023-09-10T12:13:20.000000000Z</updated>
<author>
<name>Google Inc.</name>
<email>news-webmaster@google.com</email>
<uri>https://news.google.com/</uri>
</author>
<link href="https://news.google.com/atom/search?q=lemurs&hl=pl&gl=PL&ceid=PL:pl" rel="self" type="application/atom+xml"/>
<link href="https://news.google.com/search?q=lemurs&hl=pl&gl=PL&ceid=PL:pl" rel="alternate" type="text/html"/>
<rights>2023 Google Inc.</rights>
<entry>
<id>https://news.google.com/articles/CBMicmh0dHBzOi8vd3d3LmVrb2xvZ2lhLnBsL3Nyb2Rvd2lza28vcHJ6eXJvZGEvbGVtdXItb3Bpcy13eXN0ZXBvd2FuaWUtaS16ZGplY2lhLXp3aWVyemUtbGVtdXItY2lla2F3b3N0a2ksMjkzMDcuaHRtbNIBAA?oc=5</id>
<title type="html">Lemur – opis, występowanie i zdjęcia. Zwierzę lemur ciekawostki - Portal ekologiczny</title>
<updated>2022-10-13T01:30:50.000000000Z</updated>
<link href="https://news.google.com/atom/articles/CBMicmh0dHBzOi8vd3d3LmVrb2xvZ2lhLnBsL3Nyb2Rvd2lza28vcHJ6eXJvZGEvbGVtdXItb3Bpcy13eXN0ZXBvd2FuaWUtaS16ZGplY2lhLXp3aWVyemUtbGVtdXItY2lla2F3b3N0a2ksMjkzMDcuaHRtbNIBAA?oc=5" type="text/html"/>
<content type="html"><a href="https://news.google.com/atom/articles/CBMicmh0dHBzOi8vd3d3LmVrb2xvZ2lhLnBsL3Nyb2Rvd2lza28vcHJ6eXJvZGEvbGVtdXItb3Bpcy13eXN0ZXBvd2FuaWUtaS16ZGplY2lhLXp3aWVyemUtbGVtdXItY2lla2F3b3N0a2ksMjkzMDcuaHRtbNIBAA?oc=5" target="_blank">Lemur – opis, występowanie i zdjęcia. Zwierzę lemur ciekawostki</a> <font color="#6f6f6f">Portal ekologiczny</font></content>
</entry>
Wszystkie informacje zawierają się w tagu feed
wewnątrz którego są elementy entry
z konkretnymi wiadomościami.
Wyciąganie danych
Tutaj działamy podobnie jak poprzednio. Korzystamy z klasy SimpleXML
. Zmieni się tylko link do kanału oraz sposób kontroli wersji.
$url = 'https://news.google.com/news?ned=pl_pl&q=lemurs&output=atom';
$data = new SimpleXMLElement($url, 0, true);
$atom_spec = (string) $data->attributes()->xmlns;
if ($atom_spec == 'http://www.w3.org/2005/Atom') die('Nieobsługiwana wersja Atom');
Informacje o kanale znajduje się w głównym elemencie feed
.
$channel = [];
$channel['id'] = (string) $data->channel->id;
$channel['generator'] = (string) $data->generator;
$channel['title'] = (string) $data->title;
$channel['subtitle'] = (string) $data->subtitle;
$channel['updated'] = (string) $data->updated;
$channel['author_name'] = (string) $data->author->name;
$channel['author_mail'] = (string) $data->author->email;
$channel['author_uri'] = (string) $data->author->uri;
$channel['rights'] = (string) $data->rights;
$channel['link'] = (string) $data->link;
Poszczególne elementy są natomiast w entry
$items= [];
foreach ($data->entry as $item) {
$itemData = [];
$itemData ['id'] = (string) $item->id;
$itemData ['title'] = (string) $item->title;
$itemData ['link'] = (string) $item->link->attributes()->href;
$itemData ['update'] = (string) $item->update;
$itemData ['description'] = (string) $item->content;
$items[] = $itemData;
}
Pozostała część kodu jest identyczna jak poprzednio, a efekt jest zbliżony:
Kod w serwisie GitHub
Filtrowanie danych
Tutaj jeszcze trzeba napomknąć o sanityzacji danych. Wszystko co odbieramy z zewnątrz może spowodować problemy po stronie serwera. Nie jest trudno sobie wyobrazić, że w powyższy sposób można uruchomić złośliwy kod po stronie naszego czytnika. Wystarczy w dowolnym polu przekazać skrypt, albo kod php otoczony <?php ?>
. Dlatego jeżeli korzystamy z niesprawdzonych źródeł, albo dajemy użytkownikowi możliwość samodzielnego wyboru źródła, powinniśmy pokusić się o filtrowanie danych.
Podsumowanie
- RSS 2.0 służy do dystrybuowania treści.
- Atom 1.0 jest nowszym standardem przeznaczonym do dystrybuowania treści.
- Google udostępnia informacje zagregowane z różnych serwisów w formie plików XML tworzonych w standardzie RSS oraz Atom.
- Do parsowania pliku XML w PHP służy klasa
SimpleXMLElement
. - Aby zapobiec potencjalnym atakom XSS warto dodać sanityzację danych do parsera.