Blox.pl – feed RSS dla pojedynczej kategorii

Wymaganiem udziału w konkuresie DSP2017 jest podanie feedu RSS zawierającego wyłącznie posty związane z konkursem. Blox.pl spłatał mi niemiłego figla i dał wybór – albo stary, paskudny wygląd, albo brak możliwości pobrania feedu RSS dla pojedynczej kategorii. Sprawę zgłosiłem, ale znając czasy realizacji stwierdziłem, że mam trzy możliwości: zrezygnować z DSP2017, postawić osobnego bloga specjalnie dla DSP2017 albo zrobić coś samemu.

Pierwsze dwie możliwości odrzuciłem. Jakoś nie widział mi się blog z 20 postami, kolejnego prowadzić mi się nie chce, a Blox ma swoje zalety i do porzucenia jeszcze nie dojrzałem. Spojrzałem co też mam do dyspozycji pod ręką, bo w pewne parsowanie feedu RSS bawiłem się już przy pomocy Perla (patrz zawartość mojego konta na blabler.pl). Niestety, stwierdziłem, że niezbyt się to nadaje do użytku.

Poza tym, przeszedłem na Pythona, więc pomyślałem o małej rozgrzewce. Pierwszym pomysłem było przeparsowanie całego feeda i złożenie RSS. Znalazłem przykłady parsowania XML z użyciem beautifulsoup4, z którego zresztą trochę wcześniej korzystałem. Wyciąganie zawartości poszczególnych postów poszło bardzo dobrze.

Stwierdziłem, że zamiast się bawić w tworzenie feedu kolejnym modułem, w ramach eksperymentu dodam przy pomocy chamskich printów nagłówki od XML. O dziwo zadziałało, Firefox widział coś, co wyglądało prawie jak oryginał. Jedynym problemem było kodowanie – zdecydowanie był problem z pl-znakami. Rzecz w tym, że Blox używa iso-8859-2, a Python wypluwa UTF-8. Wystarczyła szybka zmiana deklaracji kodowania w nagłówku XML i już było OK.

Kolejny problem wyszedł przy przepuszczeniu feeda przez validatory feedów RSS. Okazało się, że beautifulsoup4 zamienia wszystkie znaki w tagach na lowercase i – wierząc szybkiemu sprawdzeniu – nie ma prostego sposobu, by go od tego odwieść. Podjąłem próbę zamiany moduł na inny, ale szybko zrezygnowałem na rzecz brzydkich, ale skutecznych sedów na wyniku działania skryptu.

Na koniec zostało dodanie wywołania (z użyciem utworzonego na tę okoliczność virutalenv) do crona:

*/15 * * * * /home/rozie/dsp2017/bin/python /home/rozie/rss_from_category_blox.py 2>/dev/null | /bin/sed s'/pubdate/pubDate/g' | /bin/sed 's/lastbuilddate/lastBuildDate/g' > /tmp/dsp2017.xml && /bin/mv -f /tmp/dsp2017.xml /var/www/dsp2017.xml
*/15 * * * * /home/rozie/dsp2017/bin/python /home/rozie/rss_from_category_blox.py 2>/dev/null > /tmp/dsp2017.xml && /bin/mv -f /tmp/dsp2017.xml /var/www/dsp2017.xml

Całość wrzucam na GitHub, na wypadek, gdyby miało się komuś przydać.

UPDATE: Zgodnie z sugestią z komentarza, parsuję XML prawidłowo i pozbyłem się sed poprawiającego wyjście.

Komunikacyjne podobieństwa

Czasem patrzę na różne rzeczy i stwierdzam, że wszystko to jest do siebie bardzo podobne. Półtora roku temu napisałem:

Blog to zbiór stron z atrybutami author, date, title, body, comments (comment author, comment date). Pewnie jeszcze tags.

No dobrze, zapomniałem jeszcze o category. Przypomniało mi się to w związku z pytaniem, które dostałem na maila, a które dotyczyło eksportu zawartości bloga na WordPressa i wynikającym z tym grzebaniem w skryptach różnych, starych i nowych.

Tyle, że jakby się dobrze zastanowić, to ta struktura jest powszechna w komunikacji. Weźmy takiego Facebooka – mamy wpisy, są tagi, jest treść, autor i data. Jedyne czego nie ma, to tytuł. Pod wpisami oczywiście są komentarze. Czyli w zasadzie blog.

Facebook oczywiście nie jest wyjątkiem, podobnie jest na Twitterze czy G+. A jakby się zastanowić głębiej, to początki sięgają pewnie NNTP lub emaili. Tam również były w postach data, autor i tytuł. Na komentarze trzeba by tam spojrzeć inaczej: każdy post mógł być komentarzem – decydowało o tym umiejscowienie w hierarchii. W pewien sposób rozwiązanie lepsze i bardziej elastyczne, niż to z blogów – tu protezą jest trackback lub linkowanie. Za to nie było tagów, które zapewniają komunikację/połączenia poziome pomiędzy wpisami.

Nie wiem czy pisałem już kiedyś o tym, ale zastanawiam się, na ile realne i sensowne byłoby użycie serwerów NNTP do komunikacji rozproszonej, niezależnej, powiedzmy „obywatelskiej”. Coś jak Diaspora. Oczywiście z jakimś sensownym frontendem do czytania. I pewnie z tagami i kategoriami, które łatwo można by było uzyskać przy pomocy wprowadzenia dodatkowych nagłówków, np. X-Category oraz X-Tags. Po co? Cóż, wydaje mi się, że istnieje gotowy, dojrzały soft, sprawdzony w działaniu w sporej skali. Pytanie, czy soft ten przypadkiem nie jest już przestarzały. Ale mam wrażenie, że sporo pary projektów typu Diaspora idzie w pisanie istniejących rzeczy, zamiast w układanie flow i dopasowywanie istniejących narzędzi. Rozumiem, że tworzenie jest fajne, ale jeśli ma być to wymyślanie koła od nowa, to IMHO przestaje mieć sens.

I jeszcze jedna sprawa, pasująca do układanki. Jakiś czas temu został zamknięty serwis Delicious, grupujący linki. Znalazłem backup i co? Jest to link, pełniący rolę treści, jego tytuł, są tagi i data. W związku z tym bliski jestem eksportu starych linków do minibloga i napisania prostego skryptu do dodawania nowych wpisów. Oczywiście pelican jako silnik, a skrypt w Pythonie.

Python

Tak się składa, że ostatnio podstawowym językiem programowania, którego używam, jest Python. W związku z tym kilka przemyśleń na jego temat i na temat rozpoczynania zabawy z nim. Disclaimer: do Pythona podchodziłem już kiedyś, jeśli muszę coś napisać, to domyślnie korzystam z Perla. Niekoniecznie pięknego, często poganiającego polecenia systemowe, ale działającego. Historycznie bawiłem się Pascalem, C i oczywiście miałem kontakt z Bashem i PHP (do ostatniego oficjalnie się nie przyznaję).

Po pierwsze, istnieją czytelne reguły, np. PEP 8, którego wszyscy używają[1] czy PEP 20. Są też narzędzia, które ułatwiają zachowanie zasad dotyczących formatowania kodu, aby był zgodny z wytycznymi. Nie sposób tu nie wspomnieć o edytorze Atom, którego kiedyś włączyłem i wydał mi się straszną kobyłą. Jednak w połączeniu z pluginami działa naprawdę dobrze. Na tyle na ile mogę stwierdzić przy tak małym doświadczeniu. W każdym razie ma wszystko, do czego używałem kate. I sporo więcej. Vim nigdy mi nie podszedł na tyle, bym go używał w bardziej zaawansowany sposób. Do szybkich poprawek zawsze było albo nano, albo właśnie vim. Ale tryb tekstowy to nie jest to, co preferuję przy pracy nad dłuższym kodem.

Po drugie, dostępnych jest dużo materiałów do nauki Pythona. Także bezpłatnych. Samych książek/kursów jest kilkanaście (choćby oficjalny The Hitchhiker’s Guide to Python). Istnieją też gry, które uczą programować w Pythonie. Choćby Codecombat, którym kiedyś się bawiłem, o którym miała być notka, ale które ostatecznie mnie nie wciągnęło, a pomysł na notkę jakoś się rozmył. Mi do gustu przypadł jednak interaktywny kurs Learn Python na Androida. Jednak jeśli coś piszę i są interaktywne testy, a nie tylko czytam, to zapamiętuję więcej. Oczywiście nawet jeśli zapamiętam to do poziomu „potrafię użyć w programie” jest jeszcze kawałek, ale przynajmniej rozumiem przykłady i gotowy kod.

Po trzecie, istnieje sporo gotowych modułów, które ułatwiają pisanie. Jednak nawet gdyby nie istniały, to kod można „pożyczać” z innych skryptów przy pomocy import. I można mieć różne wersje bibliotek za sprawą środowisk wirtualnych. Z jednej strony fajne, z drugiej tworzy bałagan. Do developerki przydatne i zgodne z modną ostatnio ideą konteneryzacji. Ogólnie nauczyłem się z tym żyć, ale odruchu, żeby wszystko robić w virutalenv jeszcze nie mam.

Skoro mowa o bałaganie, to pora na wady, a tych jest zaskakująco wiele. Po pierwsze, Python 2 nadal żyje, ma się dobrze i można go spotkać na wolności. Taka umowna wada i na razie nie bardzo przeszkadza. Niemniej, nie każdy kod napisany w Pythonie 3 daje się wykorzystać w wersji drugiej.

Inna trochę dziwna na początku rzecz, to struktury danych. Jest tablica (list, w Perlu array), jest tablica asocjacyjna (dict, w Perlu hash). Ale są też tuple (niemodyfikowalne listy) i sety (nieuporządkowane zbiory unikatowych wartości). Nie żebym nie widział zalet, ale żeby aż wydzielać to? Zwł. sety (normalnie to po prostu się hasha tworzy, klucze są nieuporządkowane i unikatowe)…

To co się najbardziej rzuca w oczy: wcięcia. Są wymagane i rzutują na kod. Najprostszy przykład to:

for i in range (3):
    print ("wartosc: ")
    print (i)

Usunięcie wcięcia w ostatnim wierszu totalnie zmieni sposób działania programu. Drażni szczególnie na początku, gdy chcemy coś na szybko przetestować i np. coś wklejamy. Trzeba poprawić i dokładnie sprawdzić wcięcia, inaczej mogą być kwiatki jak wyżej.

Kolejna rzecz, która irytuje, to wciskanie filozofii w stylu zen. Na siłę i niekoniecznie zgodnie z prawdą. Weźmy mój ulubiony przykład:

There should be one– and preferably only one –obvious way to do it.

No to teraz bierzemy na tapetę set. Można go utworzyć przez

mojset = set([1, 2, 3])

Ale od wersji 2.7 można użyć do tworzenia równoważnej formy

mojset = {1, 2, 3}

Nie ma problemu? No to jak utworzyć pustego seta? Gdyby ktoś wpadł na całkiem logiczny pomysł użycia

mojset = {}

to niech wie, że nie pustego seta stworzył, a pustego dicta i za chwilę dostanie błędem po oczach. BTDT, dobry kwadrans dumania, czemu to nie działa. Dopiero wezwana pomoc uratowała sytuację, bo nie wiem ile bym jeszcze nad tym dumał.

Także one way my ass, skoro Tim Toady wita nas już na samym wstępie, przy tworzeniu jednego z podstawowych typów danych.

Możliwe, że dla ludzi, którzy zaczynają programowanie od Pythona, wady nie są tak zauważalne czy irytujące.

Niemniej, oddaję sprawiedliwość – w Pythonie pisze się przyjemnie i w praktyce jest to łatwiejsze, niż wygląda na pierwszy rzut oka. Wystarczy przejrzeć jakiś tutorial. Zdarzało mi się pisać rozszerzenia do istniejących skryptów po kilku dniach styczności z językiem i nie było większych problemów. Więc faktycznie, może być czytelnie, prosto i wygodnie. Oczywiście mogło być tak, że na ładne skrypty trafiłem. 😉

Z Perlem oczywiście nie kończę, bo do pewnych rzeczy nadaje się IMHO lepiej. Trochę gratów mam w nim napisanych, ale wersji 6 raczej już nie zacznę.

[1] A przynajmniej prawie wszyscy. A przynajmniej starają się. Prawie wszyscy.

UPDATE: Wspomniany kurs Learn Python skończyłem i szczerze polecam. Świetna podstawka, traktująca szeroko o różnych zagadnieniach, przy czym, jednak, te praktyczne części są lepsze, a teoretyczne kuleją. Jest w sumie o wszystkim najważniejszym, z programowaniem obiektowym i regexpami włącznie. Zapomniałem też wspomnieć o ipython – bardzo fajny do szybkiego sprawdzenia jakichś drobiazgów.

Swoją drogą, dziwi mnie to wciskanie regexpów do nauki programowania. To jest IMHO osobna działka zupełnie i nijak się nie klei z resztą.

Przy okazji kolejny kamyczek do ogródka w Pythonie jest inaczej. Tym razem chodzi o re.match, które działa totalnie nieintuicyjnie, choć przyznaję, że w sposób zgodny z dokumentacją. Otóż ww. funkcja dopasowuje wyrażenie tylko na początku stringa. Czyli ma takie niejawne, hardcodowane ^ – i albo .* na początku, albo korzystamy z re.search, żeby było normalnie. One way my ass, ponownie. Szczęśliwie wyszło to na Learn Python, nie w praktyce. Nie wiem co za piękny umysł to wymyślił i w imię czego…