Snippets vs Clover - Schließe das beliebteste Echtzeit-Quiz ab

April 2018. Ich war 14. Meine Freunde und ich spielten damals im sehr beliebten Online-Quiz „Clover“ von VKontakte. Einer von uns (normalerweise ich) saß immer hinter einem Laptop, um schnell Fragen zu googeln und in den Suchergebnissen nach der richtigen Antwort zu suchen. Aber plötzlich wurde mir klar, dass ich jedes Mal das Gleiche tat, und ich beschloss, es in Python 3 zu schreiben, das mir damals teilweise bekannt war.

Schritt 0. Was ist hier los?


Zunächst werde ich die Mechanik von „Clover“ in Ihrem Gedächtnis auffrischen.

Das Spiel für alle beginnt zur gleichen Zeit - um 13:00 Uhr und um 20:00 Uhr Moskauer Zeit. Um zu spielen, müssen Sie zu diesem Zeitpunkt in die Anwendung gehen und eine Verbindung zur Live-Übertragung herstellen. Das Spiel dauert 15 Minuten. Während dieser Zeit werden Fragen an die Teilnehmer am Telefon gesendet. Die Antwort ist 10 Sekunden. Dann wird die richtige Antwort bekannt gegeben. Alle, die es erraten haben, gehen noch weiter. Insgesamt gibt es 12 Fragen. Wenn Sie alle Fragen beantworten, erhalten Sie einen Geldpreis.
Bild
Es stellt sich heraus, dass unsere Aufgabe darin besteht, neue Fragen sofort vom Clover-Server abzufangen, sie über eine Suchmaschine zu verarbeiten und anhand der Suchergebnisse die richtige Antwort zu ermitteln. Es wurde beschlossen, die Antwort in einem Telegramm-Bot auszugeben, damit Benachrichtigungen direkt während des Spiels auf dem Telefon angezeigt werden. Und das alles ist in wenigen Sekunden wünschenswert, da die Reaktionszeit sehr begrenzt ist. Wenn Sie sehen möchten, wie ein ziemlich einfacher, aber funktionierender Code (und das Betrachten dieses Codes für Anfänger nützlich ist) uns geholfen hat, Clover zu schlagen - willkommen zum Schnitt.

Schritt 1. Fragen vom Server stellen


Zuerst schien es die schwierigste Etappe zu sein. Ich holte bereits tief Luft und war bereit, wie Computer Vision, Abfangen des Datenverkehrs oder Dekompilieren der Anwendung in die Wildnis zu klettern ... Als plötzlich eine Überraschung auf mich wartete - Clover hat eine offene API! Es ist nirgendwo dokumentiert, aber wenn während des Spiels, sobald allen Spielern eine Frage gestellt wurde, eine Anfrage auf api.vk.com gestellt wird, erhalten wir als Antwort die gestellten Fragen- und Antwortoptionen in JSON:

Bild

https://api.vk.com/method/execute.getLastQuestion?v=5.5&access_token=VK_USER_TOKEN 


Als access_token ist es erforderlich, das API-Token eines beliebigen VKontakte-Benutzers zu übertragen. Es ist jedoch wichtig, dass es ursprünglich speziell für Clover ausgestellt wurde. Seine app_id ist 6334949.

Schritt 2. Wir bearbeiten das Problem über eine Suchmaschine


Es gab zwei Möglichkeiten: Verwenden Sie die offizielle Suchmaschinen-API oder fügen Sie Suchargumente direkt zur Adressleiste hinzu und analysieren Sie die Ergebnisse. Zuerst habe ich das zweite ausprobiert, aber manchmal habe ich nicht nur Captcha gefangen, sondern auch viel Zeit verloren, weil die Seiten durchschnittlich in 2 Sekunden geladen wurden. Und ich erinnere Sie daran, dass es für uns ratsam ist, genau diese zwei Sekunden einzuhalten. Nun und vor allem habe ich von den Suchmaschinen keine großen und strukturierten Texte zum erforderlichen Thema erhalten, da nur kleine Teile des erforderlichen Materials, sogenannte Snippets, auf der Suchseite hängen:



Also suchte ich nach einer API. Google passte nicht - ihre Lösungen waren sehr begrenzt und gaben nur sehr wenige Daten zurück. Yandex.XML erwies sich als das großzügigste - es ermöglicht Ihnen, 10.000 Anfragen pro Tag zu senden, nicht mehr als 5 pro Sekunde, und es gibt Daten sehr schnell zurück. Die Anforderung dafür ist optional die Anzahl der Seiten (bis zu 100) und die Anzahl der Passagen - spezielle Werte, die zur Bildung von Snippets verwendet werden. Wir erhalten die Daten in XML. Dies sind jedoch alle die gleichen Schnipsel.

Damit Sie sich mit den Ergebnissen von Yandex vertraut machen und mit ihnen spielen können, finden Sie hier ein Beispiel für eine Antwort auf die Frage „Wie heißt der Hauptgegner in der Videoserie„ The Legend of Zelda? “: Yandex. Fahren .

Ich hatte Glück und es stellte sich heraus, dass es in pypi bereits ein separates Yandex-Suchmodul dafür gibt. Und so habe ich versucht, die Frage vom Server zu bekommen, sie in Yandex zu finden, einen großen Text aus Ausschnitten zu erstellen und ihn in Sätze zu zerlegen:

 import requests as req import yandex_search import json apiurl = "https://api.vk.com/method/execute.getLastQuestion?access_token=VK_USER_TOKEN&v=5.5" clever_response = (json.loads(req.get(apiurl).content))["response"] # {'text': '          «   »?', 'answers': [{'id': 0, 'users_answered': 0, 'text': '« »'}, {'id': 1, 'users_answered': 0, 'text': '« »'}, {'id': 2, 'users_answered': 0, 'text': '«»'}], 'stop_time': 0, 'is_first': 0, 'is_last': 1, 'number': 12, 'id': 22, 'sent_time': 1533921436} question = str(clever_response["text"]) ans1, ans2, ans3 = str(clever_response["answers"][0]["text"]).lower(), str(clever_response["answers"][1]["text"]).lower(), str(clever_response["answers"][2]["text"]).lower() def yandexfind(question): finded = yandex.search(question).items snips = "" for i in finded: snips += (i.get("snippet")) + "\n" return snips items = yandexfind(question) itemslist = list(items.split(". ")) 


Schritt 3. Nach Antworten suchen


Anfangs schien mir die Aufgabe, die Antwort anhand von Ausschnitten genau zu erkennen, unrealistisch (ich erinnere Sie daran, dass ich zum Zeitpunkt des Schreibens des Codes ein absoluter Anfänger war). Aus diesem Grund habe ich mich entschlossen, die von uns durchgeführte Aufgabe zunächst mit einer manuellen Suche zu vereinfachen.

Was haben meine Freunde und ich getan, als wir unsere Frage in eine Suchmaschine eingegeben haben? Sie begannen schnell durch die Augen nach Antworten in den Ergebnissen zu suchen. Was ist das Problem bei diesem Ansatz? In Mehrfachbriefen gibt es eine große Anzahl unnötiger, nicht enthaltener Informationen zu Antworten und Vorschlägen. Das Suchen mit meinen Augen dauerte manchmal lange. Daher entschied ich mich als erstes, alle Sätze mit einer Erwähnung einer der Antworten auszuwählen und anzuzeigen, damit wir die Antwort in einem sehr kleinen Text suchen, der genau die Informationen enthält, die wir benötigen.

 hint = [] # ,      for sentence in itemslist: #     if (ans1 in sentence) or (ans2 in sentence) or (ans3 in sentence): hint.append(sentence) if len(hint) > 4: break 


Es scheint, dass Sie die richtigen Angebote erhalten, sie lesen und richtig antworten. Aber was ist, wenn wir keinen einzigen Satz gefunden haben? In diesem Fall habe ich beschlossen, die Wörter so zu kürzen, dass sie nicht übersehen werden, wenn sie sich in einem anderen Fall befinden. Und auch um diejenigen zu erfassen, die aus der Quelle gebildet werden. Kurz gesagt, ich habe ihr Ende nur in zwei Zeichen zerlegt:

 if len(hint) == 0: def cut(string): if len(string) > 2: return string[0:-2] else: return string short_ans1, short_ans2, short_ans3 = cut(ans1), cut(ans2), cut(ans3) for pred in itemslist: #     if (short_ans1 in pred) or (short_ans2 in pred) or (short_ans3 in pred) hint.append(pred) 


Aber auch nach einem solchen Sicherheitsnetz gab es immer noch Fälle, in denen der Hinweis leer blieb, einfach weil die Ergebnisse die Antworten nicht immer berührten. Sagen Sie zu der Frage: "Welcher dieser Autoren hat eine Geschichte, die genau wie das Lied der Gruppe Bi 2 benannt ist?" Es kann keine genaue Antwort gefunden werden. In diesem Fall habe ich den umgekehrten Ansatz gewählt - ich habe nach den Antworten gefragt und die Option basierend darauf abgeleitet, wie oft die Wörter aus der Frage in den Ergebnissen erwähnt werden.

 if len(hint) == 0: questionlist = question.split(" ") blacklist = ["", "", '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''] for w in questionlist: if w in blacklist: questionlist.remove(w) yandex_ans1 = yandexfind(ans1) yandex_ans2 = yandexfind(ans2) yandex_ans3 = yandexfind(ans3) #      ,     count_ans1, count_ans2, count_ans3 = 0, 0, 0 for w in questionlist: count_ans1 += yandex_ans1.count(w) count_ans2 += yandex_ans2.count(w) count_ans3 += yandex_ans3.count(w) if (count_ans1 + count_ans2 + count_ans3) > 5: if count_ans1 > (count_ans2 + count_ans3): print(ans1) elif count_ans2 > (count_ans1 + count_ans3): print(ans2) elif count_ans3 > (count_ans2 + count_ans1): print(ans3) 


Zu diesem Zeitpunkt erhielt das Skript grundlegende Funktionen. Und jetzt, nur anderthalb Wochen nach der Veröffentlichung von Clover, sitzen wir und spielen bereits mit solch einem selbstgemachten „Cheat“. Sie sollten unsere Gesichter mit einem Freund gesehen haben, als wir das Spiel zum ersten Mal gewonnen haben, indem Sie Vorschläge auf der Befehlszeile wie von Zauberhand gelesen haben!

Schritt 4. Zeigen Sie klare Antworten an


Aber bald ist dieses Format müde. Zuerst musste man bei jedem Spiel mit einem Laptop sitzen. Zweitens haben meine Freunde nach dem Skript gefragt, und ich bin es leid, allen zu erklären, wie sie ihr VKontakte-Token einfügen, wie sie Yandex.XML konfigurieren (es ist an IP gebunden, dh es musste ein Konto für jeden Benutzer des Skripts erstellt werden) und wie Python auf dem Computer installiert wird.

Es wäre viel besser, wenn die Antworten direkt während des Spiels in Push-Benachrichtigungen auf dem Telefon erscheinen würden! Ich habe gerade auf den oberen Bildschirmrand geschaut und geantwortet, wie es in der Push-Benachrichtigung steht! Und Sie können dies für alle organisieren, wenn Sie Ihren Telegrammkanal für das Skript erstellen! Wunderbar!

Es ist jedoch keine Option, einfach dieselben Sätze in Telegrammen anzuzeigen. Das Lesen von Ihrem Telefon ist äußerst unpraktisch. Daher musste ich das Skript selbst lernen, um zu verstehen, welche Antwort richtig ist.

Wir importieren Telebot und ändern alle print () - Funktionen in send_tg () und notsure () , die wir in der letzten Methode verwenden werden, da sie etwas häufiger fehlen als andere:

 def send_tg(ans): bot.send_message("@autoclever", str(ans).capitalize()) print(str(ans)) return def notsure(ans): send_tg(ans.capitalize() + ".  !") hint.append("WE TRIED!") 


Und in diesem Moment wurde mir klar, dass Schnipsel viel besser sind als lange Texte! Weil die Suchmaschine sehr bemüht ist , eine Antwort auf unsere Anfrage zu geben und nicht nur Übereinstimmungen in Worten zu finden. Und es gelingt ihm - Schnipsel enthielten oft die richtigen Antworten als die falschen, das heißt, es bestand keine Notwendigkeit, den Text zu analysieren. Und ich wusste tatsächlich nicht wie.

Wir sind also einfach, die Erwähnung von Wörtern in den Ergebnissen zu zählen:

 anscounts = { ans1: 0, ans2: 0, ans3: 0 } for s in hint: for a in [ans1, ans2, ans3]: anscounts[a] += s.count(a) right = (max(anscounts, key=anscounts.get)) send_tg(right) #! 


Was ist als Ergebnis passiert:
Bild

Weiteres Schicksal


Fairerweise muss ich sagen, dass mir die Todesmaschine nicht gelungen ist. Im Durchschnitt beantwortete der Bot nur 9-10 von 12 Fragen richtig. Es ist verständlich, weil es knifflige gab, die der Analyse der Yandex-Suche nicht erlegen waren. Ich und meine Freunde hatten es satt, ständig über ein paar Fragen zu fliegen und auf ein erfolgreiches Spiel zu warten, in dem der Bot endlich alles richtig beantwortet. Ein Wunder geschah nicht, ich wollte das Drehbuch nicht mehr wirklich ändern und nachdem wir aufgehört hatten, auf einen leichten Sieg zu hoffen, gaben wir das Spiel auf.

Mit der Zeit schlich sich meine Idee in die Köpfe anderer junger Entwickler. Bis zum Sonnenuntergang 2018 gab es mindestens 10 Bots und Sites, die ihre Vermutungen zu Problemen in Clover zeigten. Die Aufgabe ist nicht so schwierig. Was jedoch überrascht, ist, dass keiner von ihnen jemals die Grenze von 9 bis 10 Fragen pro Spiel überschritten hat und später alle wie mein Bot auf 7 bis 8 gefallen sind. Anscheinend haben die Verfasser der Fragen klar gemacht, wie die Fragen zusammengesetzt werden sollen, so dass die Arbeit der Suchmaschinen irrelevant war.

Leider kann der Bot nicht mehr finalisiert werden, da Clover am 31. Dezember die letzte Sendung ausgegeben hat und ich keine Fragen hatte. Es war jedoch eine großartige Erfahrung für einen unerfahrenen Programmierer. Und für Fortgeschrittene wäre es sicherlich eine große Herausforderung - stellen Sie sich das Duo word2vec und text2vec vor, asynchrone Anfragen an Yandex, Google und Wikipedia gleichzeitig, einen fortgeschrittenen Klassifikator von Fragen und einen Algorithmus zur Neuformulierung der Frage im Fehlerfall ... Eh! Vielleicht habe ich dieses Spiel für solche Gelegenheiten mehr geliebt als für das Gameplay selbst.

Source: https://habr.com/ru/post/de435360/


All Articles