Myślałeś kiedyś może nad napisaniem własnego bota do jakiejkolwiek gry, by otrzymywać extra bonusy lub po prostu wykonać jakąś akcję, podczas, gdy nie ma Cię przy komputerze? Jeśli tak, to właśnie w tym poście dowiesz się, jak w łatwy sposób to osiągnąć.
Na początku parę słów sprostowania – w tytule wprawdzie są gry przeglądarkowe, jednakże chodzi mi o wysyłanie zapytań do serwera HTTP i odbieranie odpowiedzi, więc nie wszystkie gry przeglądarkowe będzie dało się w poniższy sposób zautomatyzować, jednakże z drugiej strony znajdą się pewnie tytuły, które nie są przeglądarkowe, jednakże korzystają z HTTP.
Wykorzystamy Google App Engine oraz język Python w wersji 2.7 (w tej wersji jest wspierany przez GAE). GAE wprawdzie wspiera Javę, jednakże pisząc w Pythonie mamy to udogodnienie, iż kodu nie trzeba kompilować i możemy skorzystać z linii Pythona, w której to można wydawać polecenie po poleceniu zachowując bieżący stan VMki Pythona. GAE wspiera także język GO oraz całkiem niedawno dodany PHP.
Obsługa protokołu HTTP
Gdy już zainstalujesz interpreter w systemie zalecam teraz go włączyć i czytając to, co znajduje się poniżej, na bieżąco testować różne możliwości. Odradzam wykorzystywanie interpreterów online (niektóre akcje trwają bardzo długo).
Na początku należy zaimportować potrzebne moduły:
1 |
import httplib, urllib |
Następnie określamy parametry zapytania POST/GET/(…) oraz nagłówki HTTP jako słowniki {nazwa: wartość}. Należy pamiętać, by nie okroić zbytnio zapytania (jak np. z poprawnej nazwy przeglądarki), bo serwer może nie wykonać naszego zapytania i przekierować nas przykładowo na stronę główną. Na ile możemy sobie pozwolić można sprawdzić przy wykorzystaniu dowolnego debuggera proxy, jak np. Charles czy Fiddler 2.
1 2 |
params = urllib.urlencode({'username': 'mrowqa', 'password': 'sweet secret'}) headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain", "User-Agent": "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:20.0) Gecko/20100101 Firefox/20.0"} |
Kolejną ważną sprawą, o której zapomnieć nie można, jest nawiązanie połączenia HTTP. Uwaga! Z własnych doświadczeń i innych internautów wiem, iż warto jest odnawiać połączenie przed każdorazowym wykonaniem zapytania, ponieważ w przeciwnym bardzo często występują błędy (połączenie keep-alive bez tego ‚keepu’). Można więc pokusić się o stworzenie specjalnej metody/funkcji.
1 |
connection = httplib.HTTPConnection("jakas-gra.pl") |
Następnie należy wysłać zapytanie do serwera i odebrać odpowiedź. W przypadku wystąpienia timeoutu próba uzyskania odpowiedzi zakończy się rzuceniem wyjątku. Zwracam uwagę na odpowiednie wcięcia w kodzie – w Pythonie to od nich zależy przynależność danej instrukcji do konkretnego bloku, np. pętli, funkcji czy warunku.
1 2 3 4 5 6 7 8 9 10 11 12 |
# wykonaj zapytanie POST do /user/login connection.request("POST", "/user/login", params, headers) # sprobuj uzyskac odpowiedz serwera try: response = connection.getresponse() except (httplib.HTTPException, socket.error) as ex: # w przypadku wyjatku print "Error: %s" % ex # wypisz jego tresc exit(1) # i zakoncz skrypt # wypisz stan zapytania (np. "200 OK") print '%s %s' % (response.status, response.reason) |
W tym momencie ważne jest przeanalizowanie odpowiedzi. Przy logowaniu serwer wyśle inną odpowiedź przy poprawnym zalogowaniu się niż przy podaniu złych danych. Tu zalecam za pomocą dowolnego debuggera proxy przeanalizować reakcję serwera na różne przypadki. Może być tak, że np. przy poprawnym zalogowaniu się zostaniemy przekierowani na inną stronę niż ta dedykowana logowaniu (logiczne, nie?) – np. odpowiedź „302 Found”.
Gdy już stwierdzimy, iż zostaliśmy poprawnie zalogowani, trzeba zadbać o to, by serwer przy następnym zapytaniu nas rozpoznał. Zazwyczaj trzeba ustawić ciasteczka (często wystarcza jedno konkretne), które przysyła nam serwer w nagłówku HTTP odpowiedzi, jednakże warto ustawić wszystkie:
1 2 3 4 5 6 7 8 9 |
for header in response.getheaders(): if header[0] == 'Set-Cookie': nameEnd = header[1].find('=') name = header[1][:nameEnd] valueEnd = header[1].find(';', nameEnd + 1) value = header[1][nameEnd + 1 : valueEnd] # dodaj ciasteczko do naglowka HTTP, ktory bedzie wykorzystany przy nastepnym zapytaniu headers['Cookie'] += name + "=" + value + "; " |
W tym momencie z odpowiednio zmodyfikowanym nagłówkiem możemy wysyłać kolejne zapytania do serwera, a on nas rozpozna. Gwoli wyjaśnienia powyższego kodu – pojedynczy nagłówek jest reprezentowany jako dwuelementowa tupla, gdzie pierwszym elementem jest nazwa nagłówka (np. ‚Set-Cookie’), a drugim jego wartość. Więcej o httplibie dla Pythona możesz znaleźć tutaj.
W tym momencie jesteś już w stanie napisać prosty skrypt, który zaloguje Cię do gry i np. podpije ofertę w domu aukcyjnym czy znajdzie jakąś pracę dla naszej postaci. Jakie i gdzie zapytania należy konkretnie wysłać? To zależy od gry. Po prostu popatrz w źródło strony (a konkretniej formularz, poprzez który wysyłasz zapytanie) i będzie wszystko jasne. Można także posłużyć się w tym celu debuggerem proxy.
Wprowadzenie do Google App Engine
Jeżeli nie stoi Ci na przeszkodzie pozostawienie komputera 24/7 w celach bocenia tudzież masz już własny serwer, na którym możesz uruchomić skrypt Pythona to GAE nie jest Ci potrzebne, jednakże zachęcam Cię do zapoznania się z tą ciekawą usługą. Na początku powinieneś zarejestrować własną aplikację na stronie GAE: https://appengine.google.com/ (wymagane konto Google) oraz ściągnąć Google App Engine Launcher – program dedykowany deploymentowi – dostarczony wraz z odpowiednim SDK dla Pythona. W GAEL utwórz nową aplikację o tej samej nazwie, co zarejestrowana na GAE online. W folderze powinieneś znaleźć conajmniej: app.yaml, main.py, cron.yaml. Jeżeli jakiś plik nie istnieje możesz go spokojnie utworzyć.
app.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
application: appname version: 3-0-rc runtime: python27 api_version: 1 threadsafe: no handlers: - url: /favicon\.ico static_files: favicon.ico upload: favicon\.ico - url: /.* script: main.app libraries: - name: webapp2 version: "2.5.2" |
Pole application jest bardzo ważne – to nazwa naszej aplikacji zarejestrowanej online. Jednym z ważniejszych pól jest tutaj handlers. To ono określa, co się stanie w zależności od konkretnej końcówki URL (określonej regexem). script: main.app oznacza, że obiekt o nazwie app w pliku main.py jest handlerem zapytania (o tym poniżej).
main.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import webapp2 class MainHandler(webapp2.RequestHandler): # jeden z handlerow def get(self): # ktory wypisuje 'Hello, World!' self.response.write('<b>Hello, World!</b> (c) by Mrowqa</br>' class BotHandler(webapp2.RequestHandler): # kolejny handler def get(self): # uruchamia skrypt znajdujacy sie w ./catalog/bot.py i o tym powiadamia import sys sys.path.insert(0, 'catalog') execfile('catalog/bot.py') self.response.write('Bot runned.') # tworzenie obiektu handlera app = webapp2.WSGIApplication([ ('/', MainHandler), # wykorzystaj MainHandler dla '/' ('/run', BotHandler) # wykorzystaj BotHandler dla '/run' ], debug=True) |
Obiekt app, który wskazaliśmy w pliku app.yaml, służy do skierowania sterowania w odpowiednie miejsce – w zależności od końcówki adresu URL, np. appname.appspot.com/run spowoduje w tym przykładzie przekazanie sterowania do BotHandler.
cron.yaml
1 2 3 4 5 6 7 8 9 10 11 12 |
cron: - description: daily summary job url: /tasks/summary schedule: every 24 hours - description: monday morning mailout url: /mail/weekly schedule: every monday 09:00 timezone: Australia/NSW - description: new daily summary job url: /tasks/summary schedule: every 24 hours target: version-2 |
Więcej informacji o konfigurowaniu crona możesz znaleźć tutaj.
Deployment aplikacji
Teraz wystarczy uruchomić Google App Engine Launcher, wybrać utworzoną wcześniej aplikację, nacisnąć przycisk „Deploy”, podać swoje dane konta Google i gotowe! Możesz sprawdzić czy aplikacja działa, przy okazji zerknąć na logi i nacieszyć się tym jakże wspaniałym widokiem :).
Jeżeli zainteresowałeś się technologią GAE to zachęcam do zerknięcia tutaj. Możesz również poszukać informacji o obsłudze innych języków lub konkretnych elementów platformy, jak np. baza danych.
Podsumowanie i przykład
Przedstawiłem w tym poście w wielkim skrócie obsługę protokołu HTTP w języku Python i przy okazji zaprezentowałem minimum wymagane do umieszczenia aplikacji na serwerach Google’a. Przykład (kod źródłowy) bota do pewnej gry możesz znaleźć tutaj. KOD ŹRÓDŁOWY ZAMIESZCZONY WYŁĄCZNIE W CELACH EDUKACYJNYCH ZA ZGODĄ ADMINISTRATORA GRY – ZAKAZ UŻYWANIA W CELU OSIĄGNIĘCIA JAKIEJKOLWIEK KORZYŚCI W GRZE.
Mam nadzieję, iż ten post bardzo Wam się spodobał. Zachęcam do subskrypcji poprzez kanał RSS tudzież mailowo i serdecznie pozdrawiam 🙂
Dzięki wielkie! Szacun!
Cieszę się, że pomogłem 🙂
Wprawdzie po dwóch latach, ale to zawsze coś i daje niesamowitą satysfakcję!
Wcześniej nie interesowałem się tym zagadnieniem, trochę mnie te POSTy i GETy przerażały. A to wcale nie takie trudne :> Myslę, że się odezwę w razie jakichś strasznych problemów 😉
dlaczego mój pythonowy kod ciągle zwraca to samo (200 OK) mimo tego, że za każdym razem zmieniam hasło?
Wszystko jest zależne od kodu po stronie serwera. Kod 200 OK oznacza, że zapytanie do serwera zostało wykonane poprawnie. Wyświetlenie komunikatu o błędnych danych do logowania także jest poprawnym zachowaniem. Wykorzystaj jakiegoś debuggera proxy (polecam Fiddlera 2/Fiddlera 4 – świetny i darmowy) i przejrzyj komunikację z serwerem – co się zmienia, gdy podasz dobre hasło, a co się stanie, gdy podasz złe hasło. Może się okazać, że będziesz musiał przeglądać zwrócony kod HTML (i do tego, jak niemalże wszystkiego, są już istniejące pythonowe moduły).
PS jeśli nie chcesz męczyć się z ustawianiem ciasteczek samemu, przyjrzyj się modułowi ‚requests’ (nie wiedziałem o nim w trakcie pisania postu).