Table of Contents
Wstęp
Tutorial składa się z dwóch cześci:
Kod (nowszy i lepszy ;) ) dostępny na githubie.
Część z Was na pewno osadza swoje filmiki z konta Facebook także na swoich stronach www. Niektórzy robią to z pomocą odtwarzacza dostarczonego przez Facebooka, inni korzystają np. z JWPlayera. Niestety ostatnimi czasy, Facebook zauważył, że staje się bardzo fajną platformą hostingową dla filmów wszelakich (i to w jakości HD!). Zjadało im to (i zjada ;) ) gigantyczne ilości zasobów - przede wszystkim łącza. Jeśli osadzasz pliki wideo przez ich odtwarzacz - to nie ma problemu - FB ma z tego korzyści (mają swoje logo na filmiku, itd). Korzystając jednak z JWPlayera - nie uda ci się ta sztuczka. Linki bezpośrednie do plików MP4 są zmieniane co 24-36 godzin, w skutek czego umieszczanie ich "w" odtwarzaczu nie ma sensu. Na szczęście da się to bardzo łatwo rozwiązać, jak zawsze w... Rubym :)
Mechanize
Napiszemy prostego bota - który wchodzi na Facebooka, loguje się i sprawdza URL filmu. Warto dodać cacheowanie, tak aby nie odpytywać FB za każdym razem o to samo. Rozbudowę tego narzędzia pozostawię jednak Wam. Wracając do sedna sprawy. Aby napisać tego bota, skorzystamy z Mechanize. Mechanize jest biblioteką stworzoną do łatwej integracji ze stronami wszelakimi. Umożliwia przesyłanie formularzy, odwiedzanie stron, zapewnia obsługę cookies, ssl-a, itp, itd. To głównie dzięki niemu, będziemy mogli odświeżać linki do plików MP4 z FB.
Zanim jednak to zrobimy, musimy dodać do Mechanize pewną małą poprawkę. Domyślnie nie pozwala wyszukiwać formularzy po ID a nam taka metoda się przyda. Tak więc:
class Mechanize::Page def form_id(formId) formContents = (self/:form).find { |elem| elem['id'] == formId } if formContents then return Mechanize::Form.new(formContents) end end end
Tyle :) A teraz pora na FacebookBota.
FacebookBot
Nasz bot będzie miał łącznie 4 metody:
- Initialize - inicjalizacja bota
- Login - logowanie do Facebooka
- Video_Url - pobranie URLa pliku wideo
- (priv) get_url - wyodrębnienie samego urla pliku ze strony z linkiem
Szkielet klasy będzie więc wyglądał tak:
require 'rubygems' require 'mechanize' require 'uri' require 'cgi' require 'time' class FacebookBot # Strona główna Facebooka FB_URL = "http://www.facebook.com/" # Nasza przeglądarka i system :) USER_AGENT = 'Linux Firefox' def initialize(email, pass) end def login end def video_url(video_id) end private def get_url(url) end end
Inicjalizacja naszego bota składa się z zapamiętania e-maila i hasła, utworzenia obiektu Mechanize do eksploracji FB oraz próby zalogowania na nasze konto:
def initialize(email, pass) @email, @pass = email, pass @agent = Mechanize.new @agent.user_agent_alias = USER_AGENT @cookies = File.dirname(__FILE__) + "/../cookies-" + @email + ".yml" if (File.file?(@cookies)) @agent.cookie_jar.load(@cookies) end self.login end
Wartym omówienia jest ten fragment:
@cookies = File.dirname(__FILE__) + "/../cookies-" + @email + ".yml" if (File.file?(@cookies)) @agent.cookie_jar.load(@cookies) end
Zmienna @cookies trzyma ciastka sesji od Facebooka na dysku. Dlaczego jest o katalog wyżej (../) niż sam plik? Ponieważ plik z botem wrzucimy do 'lib/' a plik z ciastkiem będziemy chcieli mieć w katalogu głównym naszej małej aplikacji. Tak więc, ustalamy sobie ścieżkę, sprawdzamy czy plik już istnieje i jeśli tak jest, to ładujemy jego zawartość do kontenera na ciacho - tak żeby można się było przedstawiać nim w Facebooku. Następnie podejmujemy próbę logowania.
Pora na logowanie:
def login page = @agent.get(FB_URL) if (loginf = page.form_id("login_form")) loginf.set_fields(:email => @email, :pass => @pass) page = @agent.submit(loginf, loginf.buttons.first) end @agent.cookie_jar.save_as(@cookies) body = page.root.to_html @uid = %r{\\"user\\":(\d+),\\"hide\\"}.match(body)[1] @post_form_id = %r{<input type="hidden" id="post_form_id" name="post_form_id" value="([^"]+)}.match(body)[1] end
Logowanie przebiega w następujący sposób:
- Próbujemy przejść na stronę główną FB.
- Jeśli się to udało - tzn że nasze ciastka były i były prawidłowe
- Jeśli nie, to wypełniamy formularz logowania i klikamy na "Zaloguj"
- Zapamiętujemy uaktualnione ciastka
- Przechodzimy na stronę główną i zapamiętujemy id usera (UID)
- Zapamiętujemy klucz do przesyłania żądań typu POST
Prawda że proste? :)
Video_url:
def video_url(id) begin pa = @agent.get("#{FB_URL}video/video.php?v=#{id}") pr = pa.body get_url(pr) rescue "Niepoprawne ID pliku wideo lub wideo nie jest publiczne." end end
Pobieramy stronę pliku wideo, wyciągamy link do MP4 i zwracamy. Jeśli coś pójdzie nie tak, to zwracamy info że wideo jest nieprawidłowe lub nie jest publiczne.
Samo parsowanie przebiega w sposób trochę "brutalny" - nie chciało mi się bawić i myśleć to wyciągnąłem tak niezbyt elegancko:
private def get_url(url) url = url.scan(/addVariable\(\"highqual_src\",\s\"http.+\"\)/ix).first url = url.split(')').first.gsub('\u00253A', ':') url = url.gsub('\u00252F', '/') url = url.gsub('\u00253F', '?') url = url.gsub('\u00253D', '=') url = url.gsub('\u002526', '&') url = "http://#{url.split('http://')[1]}".split('"').first CGI.unescapeHTML(url) end
Grunt że działa.
Oto jak działa całość:
fb = FacebookBot.new('moj@mail.pl', 'moje_tajne_haslo') p fb.video_url(ID_PLIKU_VIDEO)
Warto dodać do tego system cacheowanie, tak aby nie odpytywać FB za każdym razem o to samo!
W związku z szerokim zainteresowaniem, udostępniam prosty interfejs: www.fbc.mensfeld.pl. Linki do odcinków buduje się tak:
www.fbc.mensfeld.pl/ID-ODCINKA.mp4
Tutorial składa się z dwóch cześci:
Kod (nowszy i lepszy ;) ) dostępny na githubie.
March 23, 2011 — 19:25
genialne! :D