Twitch Viewer Bot

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
warcoach
User
Beiträge: 5
Registriert: Sonntag 12. Oktober 2014, 01:25

Einen guten Morgen wünsche ich!

Das Thema "Viewer Bot" wird bei Twitch immer größer und scheinbar werden diese auch viel genutzt.
Da ich eh gern mit neuen / anderen Programmiersprachen rumspiele, wollte ich mir so einen Bot selbst in Python umsetzen - Gewissensbisse habe ich dabei nicht. ;)

Folgenden Code habe ich als Basis genommen:
https://gist.github.com/Xeroday/6468146#file-twitch-py
http://www.ericzhang.me/faking-views-on-twitch-tv/

-----

Ich habe jetzt mehrere Stunden mit dem kleinen Versuch hinter mir und denke, dass sich mein Python Code dabei doch verbessert - So, dass ich heute einen Neubeginn startete.
Ich nutze nun Klassen und versuche vorerst die einfache Basis STABIL umzusetzen.

Das Heißt:
Pro Thread ein Proxy. Dieser Thread holt sich die nötige URL (Livestreamer Lib - exp. Date 24h) und generiert die Zugriffe (HEAD) auf diese URL => 1 View.
Wenn ein Thread abstürzt, soll dieser getötet werden und ein neuer mit dem nächsten Proxy gestartet werden. Die Proxy Liste kommt aus einer Datenbank.
Es kann vorkommen, dass Proxys von Twitch blockiert / blacklisted sind. Dann soll diese IP geflagt werden und ebenfalls eine neue eingebunden werden.
http://www.proxy-listen.de/Proxy/Proxyliste.html

Bei 5 Threads läuft der Code einwandfrei.
Bei 10 Threads läuft der Code einwandfrei.
Bei 15 Threads kommen ~14 an
Bei 20 schmeisst der Code Fehler, die ich mir nicht erklären kann. Ggf. auch Memory Probleme, oder es kommen keine Viewer (ggf. wegen fehlendem Timeout Parameter in urllib2.open() - Mit gibt's leider nur Fehler)


Hier mein aktueller Code, der folgende Punkte kann:
- Holt eine Proxyliste aus der Datenbank
- Erstellt Threads, die sich die URL holen und Zugriffe verursachen

Die anderen Punkte habe ich im vorherigen Code bereits teils umgesetzt. Allerdings soll der Grund vorerst stabil auch mit 20+ Threads laufen.

Hier der aktuelle Code (Einige Libs noch enthalten, die auf diesem Stand nicht nötig sind):
http://www.python-forum.de/pastebin.php?mode=view&s=406

Einen passenden Streamer findet Ihr hier:
http://www.twitch.tv/directory/game/Dota%202


Ich würde mich auch gern über TS oder Skype über einzelne Fragen zu einigen Funktionen unterhalten.
Warum z.B der Parameter "timeout" bei urllib2.open() zum Crash führt nach einiger Zeit.


Fand Python vorerst eigensinnig - Aber mittlerweile eine ziemlich nette Sache!

Gruß
BlackJack

Die Module `requests` und `Queue` werden importiert aber nicht verwendet. Wenn Du schon weisst das unnötige Importe da sind, warum wirfst Du sie dann nicht raus bevor Du den Quelltext zur Diskussion stellst?

`requests` wäre allerdings etwas was ich verwenden würde, statt "manuell" die Standardbibliothek zu verwenden.

Das Hauptprogramm gehört in eine Funktion damit man nicht irgendwann absichtlich oder aus versehen aus Funktionen oder Methoden auf etwas zugreift auf das man nicht direkt zugreifen sollte. Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Noch unübersichtlicher wird es wenn man das Hauptprogramm zwischen andere Definitionen verstreut wie Du das mit den Definitionen nach den Importen gemacht hast. Die werden alle erst viel weiter unten verwendet. Ein Wert sollte möglichst nah an der Stelle definiert werden an dem er auch verwendet wird, damit man sich den Code nicht mühsam über das gesamte Programm verteilt zusammen suchen muss.

`ViewerThread` ist unnötigerweise eine Klasse. Faustregel: Wenn eine Klasse nur zwei Methoden hat und eine davon `__init__()` heisst, hat man in der Regel eine Funktion die unnötigerweise ein einer Klasse steckt.

Zeilen sollten möglichst nicht länger als 80 Zeichen werden.

Abkürzungen bei Namen sollte man vermeiden solange sie nicht allgemein bekannt sind. In einer Funktion `prox` und `proxy` zu haben, die an sehr unterschiedliche Werte gebunden sind, ist zum Beispiel gar keine gute Idee. Es reicht nicht das Python die beiden Namen auseinander halten kann, ein menschlischer Leser muss das können. Bei `proxy_handler` und `proxy_url` ist das deutlich besser möglich. `threads` steht nicht für `Thread`-Objekte sondern für eine Anzahl.

Bei den `ProxyHandler`-Objekten kann man dann auch eine Frage zum Programmablauf stellen. Wie viele denkst Du denn das man davon braucht‽ Und was `install_opener()` eigentlich macht wenn in der Dokumentation steht das damit *der* Default-Opener erstellt wird‽

Ebenfalls eigenartig ist das Absetzen einer 'HEAD'-Abfrage ohne die Antwort abzurufen und das Verbindungsobjekt ohne es zu schliessen der Speicherverwaltung zu überlassen. Auf Serverseite würde ich so etwas relativ schnell anfangen zu blocken weil das extrem nach DOS-Angriff aussieht, und auf Clientseite stellt sich die Frage wie viele Verbindungen man auf diese Weise erstellen kann wenn die *nicht* zeitnah von der Speicherverwaltung wieder zerstört würden — was nicht wirklich garantiert wird.

Aus Verbindungsfehlern werden keine Konsequenzen gezogen. Wenn man Fehler bekommt ist es vielleicht keine gute Idee weiterhin alle 300ms eine Anfrage zu stellen.

Kommentare sollten einen Mehrwert über den Code liefern und nicht das offensichtlich noch mal als Kommentar enthalten. In der Regel kommentiert man deshalb *warum* der Code das macht was er tut, denn *was* er tut, ist ja schon aus dem Code selbst ersichtlich. Falls nicht sollte man den Code so umschreiben das es das wird. Sehr schlecht sind auch inhaltlich falsche Kommentare. Zum Beispiel wenn man Listen als Arrays bezeichnet.

`vThreadSet` ist überflüssig, der Wert erfüllt keinen Zweck. Dort wo er getestet wird ist er *immer* 0 und nach dem der Name an einen anderen Wert gebunden wurde, wird er nie wieder verwendet. Wenn das ein Flag ist währen Wahrheitswerte auch besser als 0 und 1. Beim Namen greift das was weiter oben zu Abkürzungen steht, und die Schreibweise entspricht nicht dem Style Guide for Python Code.

Die Verbindung zur Datenbank wird nicht wieder geschlossen obwohl nach dem Abrufen der Proxy-URLs nicht mehr darauf zugegriffen wird.

Das sammeln der Datensätze in einer Liste ist relativ umständlich gelöst. Ein `list()`-Aufruf hätte es an der Stelle auch getan. Allerdings ist das nicht wirklich genug wenn man sich ansieht was für ein Unsinn später mit den Elementen angestellt wird. Tupel in Zeichenketten umzuwandeln um dann die Zeichen zu entfernen die überhaupt erst durch die Umwandlung in Zeichenketten entstanden sind, ist äusserst unglücklich. Um es mal vorsichtig zu formulieren. Wenn man ein Element aus einem Tupel heraus holen will dann benutzt man einen Indexzugriff. Und das dann am besten auch schon an der Stelle wo man die Datensätze aus der Datenbank holt damit man auch nur die Werte in der Liste speichert die man später auch tatsächlich haben möchte. Hier bietet sich eine „list comprehension” (LC) an.

`proxyIndex` ist überflüssig. Das ist *immer* der selbe Wert wie `i` in der Schleife.

In jedem Fall ist dort ein Programmfehler: Wenn es weniger Proxy-URLs als Threads gibt, rennt man in der Schleife in einen `IndexError`. Besser wäre es direkt über die passende Anzahl von Proxy-URLs zu iterieren, ohne einen unnötigen Index.

Ich komme dann als Zwischenergebnis ungefähr bei so etwas heraus (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python
from __future__ import absolute_import, division, print_function
import json
import sys
import time
import urllib2
from contextlib import closing
from itertools import islice
from subprocess import PIPE, Popen
from threading import Thread

import MySQLdb


def fake_viewers(proxy_url):
    output = Popen(
        ['livestreamer', 'twitch.tv/shingshady', '-j'], stdout=PIPE
    ).communicate()[0]
    url = json.loads(output)['streams']['worst']['url']
    # 
    # BUG: Setting a global opener from a thread is of course not thread safe
    #   and leads to *all* threads requesting through the last set
    #   proxy handler.
    # 
    proxy_handler = urllib2.ProxyHandler({'http': proxy_url})
    opener = urllib2.build_opener(proxy_handler)
    urllib2.install_opener(opener)

    headers = {
        'User-Agent':
            'Mozilla/5.0 (Windows NT 6.1)'
            ' AppleWebKit/537.36 (KHTML, like Gecko)'
            ' Chrome/36.0.1985.143'
            ' Safari/537.36'
            ' OPR/23.0.1522.77'
    }
    while True:
        request = urllib2.Request(url, None, headers)
        request.get_method = lambda: 'HEAD'  # TODO: Use `requests` instead.
        # 
        # TODO: Limit the number of allowed connection errors per proxy.
        # 
        try:
            _response = urllib2.urlopen(request)
        except urllib2.HTTPError, error:
            print('HTTP ERROR:', error.code)
            print('-------', proxy_url, 'died! -----')
        except urllib2.URLError, error:
            print('URL ERROR:', error.reason)
            print('-------', proxy_url, 'died! -----')

        time.sleep(0.3)
    

def main():
    thread_count = int(sys.argv[1])

    with closing(MySQLdb.connect(host='xxxxxxxxx')) as database:
        cursor = database.cursor()
        cursor.execute('SELECT proxy FROM proxylist WHERE isStable = 1')
        proxy_urls = [row[0] for row in cursor.fetchall()]
 
    for proxy_url in islice(proxy_urls, thread_count):
        thread = Thread(target=fake_viewers, args=(proxy_url,))
        thread.start()
        print('Viewer Thread (', proxy_url, ') started')


if __name__ == '__main__':
    main()
warcoach
User
Beiträge: 5
Registriert: Sonntag 12. Oktober 2014, 01:25

Guten Morgen,

vielen Dank für die vielen Tipps und dem sogar umgeschriebenen Quellcode und deiner damit verbundenen Zeit!
Der Code funktionierte bereits ungetestet :)

Frage 1:

Ich habe nun den urllib2 Bereich durch requests ersetzt - Gilt der "Bug" dafür auch? Der Opener scheint ja im Requests Objekt zu stecken und das Ganze mit urllib3 zu laufen.

#
# BUG: Setting a global opener from a thread is of course not thread safe
# and leads to *all* threads requesting through the last set
# proxy handler.
#

Code: Alles auswählen

    http_proxy = "http://" + proxy_url

    proxyDict = {
        "http" : http_proxy
    }

    headers = {
        'User-Agent':
            'Mozilla/5.0 (Windows NT 6.1)'
            ' AppleWebKit/537.36 (KHTML, like Gecko)'
            ' Chrome/36.0.1985.143'
            ' Safari/537.36'
            ' OPR/23.0.1522.77'
    }

    while True:
        #
        # TODO: Limit the number of allowed connection errors per proxy.
        #
        try:
            response = requests.head(url, proxies=proxyDict,  headers=headers)
        except requests.exceptions.Timeout:
            print('-------', proxy_url, 'timed out! -----')
        except requests.exceptions.TooManyRedirects:
            print('-------', proxy_url, 'too many redirects on destination! -----')
        except requests.exceptions.RequestException as error:
            print('-------', error)
        time.sleep(0.3)
Anmerkung:
`ViewerThread` ist unnötigerweise eine Klasse. Faustregel: Wenn eine Klasse nur zwei Methoden hat und eine davon `__init__()` heisst, hat man in der Regel eine Funktion die unnötigerweise ein einer Klasse steckt.
Das fand man sehr häufig bei Google, sodass ich meinen Thread erst in eine Klasse gesteckt habe. Ich stimme dir allerdings zu, dass das nicht der Sinn einer Klasse sein sollte / ist.

Anmerkung:
Bei den `ProxyHandler`-Objekten kann man dann auch eine Frage zum Programmablauf stellen. Wie viele denkst Du denn das man davon braucht‽ Und was `install_opener()` eigentlich macht wenn in der Dokumentation steht das damit *der* Default-Opener erstellt wird‽
Wie genau läuft das mit install_opener() beim Requests Objekt ab? Ich werde nochmal durch die Doku schauen. Allerdings sollen die Server Ressourcen das Limit für die Fake-Views sein - Sprich es ist so noch kein Limit vorgesehen. Der Server ist ein kleiner - Mit Google Go kam jemand auf ~1100 Viewer, aber mit einer anderen Methode:

Main Programm -> X Mother Threads mit Proxy -> Jeweils 10x Child Threads pro Mother Thread, die auf eine angeforderte URL "gebombt" haben.
Sprich 10 Views pro Proxy.
Das ist mir allerdings zu instabil und unkontrolliert. Ich möchte wie gesagt schön stabil laufende Threads und Views.

Hoffe ich habe die Frage richtig verstanden?

Frage 2:
Ebenfalls eigenartig ist das Absetzen einer 'HEAD'-Abfrage ohne die Antwort abzurufen und das Verbindungsobjekt ohne es zu schliessen der Speicherverwaltung zu überlassen. Auf Serverseite würde ich so etwas relativ schnell anfangen zu blocken weil das extrem nach DOS-Angriff aussieht, und auf Clientseite stellt sich die Frage wie viele Verbindungen man auf diese Weise erstellen kann wenn die *nicht* zeitnah von der Speicherverwaltung wieder zerstört würden — was nicht wirklich garantiert wird.

Aus Verbindungsfehlern werden keine Konsequenzen gezogen. Wenn man Fehler bekommt ist es vielleicht keine gute Idee weiterhin alle 300ms eine Anfrage zu stellen.
Da komme ich etwas ins Trudeln. Wenn ich einen Error Counter einbaue und dieser dann anschlägt, wie schaffe ich es eine neue Proxy IP in den bestehenden Thread zu übergeben?
Da Threads laut Google nicht geschlossen werden sollten ...
Mein Lösungsweg wäre jetzt eine Queue gewesen, in der alle Proxies enthalten sind. Dann könnten die Threads die Proxy IP's direkt aus der Queue ziehen, falls einer den Error Counter erreicht hat.

Wie würdest du die Antwort minimal abrufen? Einfach nur mit einer Abfrage HTTP Code = 200? Das Thema Speicherverwaltung lässt meinen Wissensstand auch gerade hängen. Scheint aber der Fehler zu sein, dass ich nicht über ~15 - 20 Zugriffe hinaus komme.


Anmerkung:
In jedem Fall ist dort ein Programmfehler: Wenn es weniger Proxy-URLs als Threads gibt, rennt man in der Schleife in einen `IndexError`. Besser wäre es direkt über die passende Anzahl von Proxy-URLs zu iterieren, ohne einen unnötigen Index.
Das könnte man mit dem Queue Ansatz kontrollieren. Oder die Gesamtanzahl der Proxies ermitteln. Ich würde allerdings vorerst über die 20 Views Grenze hinaus kommen. Das Proxy-Management soll später über eine MySQL Datenbank mit Flags laufen. (Wo ich auch schon Probleme mit den Zugriffen innerhalb der Threads sehe ...)


Gruß und nochmals vielen Dank für deine Mühen!
BlackJack

@warcoach: Wenn jeder Request seinen eigenen Proxy bekommt, dann gilt der Bug natürlich nicht.

Noch zum alten Quelltext: Egal wie viele Threads Du hast und wie viele Anfragen da gestellt werden, man braucht nur ein `ProxyHandler`-Objekt pro Proxy. Du erstellst davon für den jeweils selben Proxy immer wieder neue Objekte. Wohl um den Bug zumindest abzumildern. Solange es aber eine globale Ressource ist die von allen Threads verwendet wird, bestand halt immer die Gefahr das ein Thread gar nicht den Proxy benutzt für den er gestartet wurde. Und das wird um so wahrscheinlicher je mehr Threads diesen globalen Wert nebenläufig setzen und verwenden.

Eine Lösung zum Problem dass man eventuell weniger Proxies als angeforderte Threads hat, habe ich doch bereits ins Programm eingebaut.
warcoach
User
Beiträge: 5
Registriert: Sonntag 12. Oktober 2014, 01:25

So ...

Da das URL aus der livestreamer Lib beziehen nicht hinterher kommt, wenn man es in dem jeweiligen Thread macht, habe ich diese vorher bezogen und in eine Queue gesteckt.
Diese blockiert, bis zum task_done().

Zusätzlich habe ich die Proxies in eine Queue gesteckt, um bei einem Fehlerfall einen neuen Proxy in den Thread laden zu können.
Der Fall, dass keine Proxies mehr vorhanden sind, ist noch nicht umgesetzt - ist aber auch nicht der Grund für den derzeitigen Fehler.

Das Programm schafft keine ~15 Viewer zu generieren. Starte ich das Programm mit dem Argument 15, baut sich alles einwandfrei auf. Es kommen allerdings KEINE Viewer.
Die Access.log von meinem Server bekommt die Zugriffe allerdings.

Mit dem Argument 10 (Threads) klappt alles einwandfrei

So sieht ein Zugriff aus der Access.log aus, der sich dann wiederholt.
185.49.15.25 - - [12/Oct/2014:14:50:36 +0200] "HEAD / HTTP/1.1" 200 - "-" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36 OPR/23.0.1522.77"
Kann es sein, dass ich bestimmte Timings beachten muss, um einen wirklichen Zugriff und das Anschauen des Streams "faken" zu können, sodass es ab einer bestimmten Anzahl Threads zu lange dauert?
Ich komme nicht einmal an einen Fehler und stehe gerade völlig auf dem Schlauch.

Konsolen Ausgabe:
Viewer Thread ( 0 ) started
Proxy URL 134.19.178.70:7808
Twitch URL http://video11.iad02.hls.twitch.tv/hls4 ... 4e6e374de5
Viewer Thread ( 1 ) started
Proxy URL 185.49.15.25:8089
Twitch URL http://video12.sfo01.hls.twitch.tv/hls4 ... 5d4b6b8621
Viewer Thread ( 2 ) started
Proxy URL 185.49.15.25:3127
Twitch URL http://video11.iad02.hls.twitch.tv/hls4 ... 552de94fbc
Viewer Thread ( 3 ) started
Proxy URL 93.115.8.229:7808
Twitch URL http://video12.sfo01.hls.twitch.tv/hls4 ... c968cf904e
Viewer Thread ( 4 ) started
Proxy URL 93.115.8.229:8089
Twitch URL http://video12.sfo01.hls.twitch.tv/hls4 ... ac64c0c199
Viewer Thread ( 5 ) started
Proxy URL 89.46.101.122:7808
Twitch URL http://video11.iad02.hls.twitch.tv/hls4 ... 81a744bfad
Viewer Thread ( 6 ) started
Proxy URL 198.52.217.44:3127
Twitch URL http://video12.sfo01.hls.twitch.tv/hls4 ... 6fdecc77c2
Viewer Thread ( 7 ) started
Proxy URL 93.115.8.229:3127
Twitch URL http://video12.sfo01.hls.twitch.tv/hls4 ... 6acb30c826
Viewer Thread ( 8 ) started
Proxy URL 198.52.217.44:8089
Twitch URL http://video12.lax01.hls.twitch.tv/hls4 ... 38611dbe2e
Viewer Thread ( 9 ) started
Proxy URL 89.46.101.122:3127
Twitch URL http://video12.sfo01.hls.twitch.tv/hls4 ... e9afa47409
Viewer Thread ( 10 ) started
Proxy URL 198.52.217.44:7808
Twitch URL http://video12.lax01.hls.twitch.tv/hls4 ... 7f21d1a012
Viewer Thread ( 11 ) started
Proxy URL 217.112.131.63:7808
Twitch URL http://video11.iad02.hls.twitch.tv/hls4 ... c1d3b7477e
Viewer Thread ( 12 ) started
Proxy URL 199.200.120.36:3127
Twitch URL http://video11.iad02.hls.twitch.tv/hls4 ... e98c8c7091
Viewer Thread ( 13 ) started
Proxy URL 62.210.56.250:7808
Twitch URL http://video12.sfo01.hls.twitch.tv/hls4 ... 0fdb2238d9
Viewer Thread ( 14 ) started
Proxy URL 185.49.15.25:7808
Twitch URL http://video11.iad02.hls.twitch.tv/hls4 ... 6f7374d387

Aktueller Quellcode:

Code: Alles auswählen

#!/usr/bin/env python
from __future__ import absolute_import, division, print_function
import json
import sys
import time
import requests
from Queue import Queue
from contextlib import closing
from subprocess import PIPE, Popen
from threading import Thread

import MySQLdb

def get_url():
    output = Popen(
        ['livestreamer', 'twitch.tv/mag1x', '-j'], stdout=PIPE
    ).communicate()[0]
    return json.loads(output)['streams']['worst']['url']


def fake_viewers():
    error_counter = 0
    isProxySet = 0

    url = url_queue.get()
    url_queue.task_done()

    while True:
        if isProxySet == 0:
            proxy_url = proxy_queue.get()
            print('Proxy URL', proxy_url)
            print('Twitch URL', url)

            http_proxy = "http://" + proxy_url

            proxyDict = {
                "http" : http_proxy
            }

            headers = {
                'User-Agent':
                    'Mozilla/5.0 (Windows NT 6.1)'
                    ' AppleWebKit/537.36 (KHTML, like Gecko)'
                    ' Chrome/36.0.1985.143'
                    ' Safari/537.36'
                    ' OPR/23.0.1522.77'
            }

        isProxySet = 1

        try:
            response = requests.head(url, proxies=proxyDict,  headers=headers)
        except:
            print('-------', proxy_url, 'ERROR -----')
            error_counter += 1

#        except requests.exceptions.Timeout:
#            print('-------', proxy_url, 'timed out! -----')
#            error_counter += 1
#        except requests.exceptions.TooManyRedirects:
#            error_counter += 1
#            print('-------', proxy_url, 'too many redirects on destination! -----')
#        except requests.exceptions.RequestException as error:
#            error_counter += 1
#            print('-------', error)

        if error_counter == 5:
            error_counter = 0
            isProxySet = 0
            proxy_queue.task_done()

        time.sleep(0.3)

def main():
    thread_count = int(sys.argv[1])

    with closing(MySQLdb.connect(host="localhost", xxxxxxxxxx)) as database:
        cursor = database.cursor()
        cursor.execute('SELECT proxy FROM proxylist WHERE isStable = 1')
        proxy_urls = [row[0] for row in cursor.fetchall()]

    for proxy_url in proxy_urls:
        proxy_queue.put(proxy_url)

    for i in range(0, thread_count):
        url = get_url()
        url_queue.put(url)
        thread = Thread(target=fake_viewers)
      thread.start()
        print('Viewer Thread (', i , ') started')
        url_queue.join()

if __name__ == '__main__':
    proxy_queue = Queue()
    url_queue = Queue()
    main()
BlackJack

@warcoach: Globale Variablen sind keine gute Idee. Was eine Funktion oder Methode benötigt, ausser Konstanten, sollte als Argument übergeben werden und nicht auf magische Weise irgendwo aus der „Umgebung” kommen.

`isProxySet` ist überflüssig wenn man das am Wert von `proxyDict` selber ablesen könnte, also zum Beispiel `None` für ”kein Proxy gesetzt” verwenden würde. Konkrete Datentypen sollten in Namen nicht vorkommen. Irgendwann möchte man den Typen dann mal ändern und muss auch gleich den Namen überall ändern oder man hat irreführende Namen im Quelltext.

Das mit der URL-Queue verstehe ich nicht wirklich. Du erzeugst eine URL pro Proxy, verwendest dann aber nur eine URL davon pro Thread. Und machst dann einen `join()` auf diese Queue. Der kann niemals erfolgreich sein wenn nicht alle URLs auch mit einem `task_done()` quittiert werden, was nicht passiert solange es mehr Proxies als Threads gibt. Wenn es eine URL pro Thread gibt, kann man die auch direkt übergeben, ohne eine Queue. Bei `proxy_queue` macht das `task_done()` auch keinen Sinn denn der Sinn dieser Queue ist es ja nicht das alle Proxies am Ende abgearbeitet sein müssen.

Was bedeutet URLs aus livestreamer beziehen kommt nicht hinterher? Der einzige Unterschied zu vorher ist, dass die Aufrufe nicht mehr parallel ablaufen können.
warcoach
User
Beiträge: 5
Registriert: Sonntag 12. Oktober 2014, 01:25

@warcoach: Globale Variablen sind keine gute Idee. Was eine Funktion oder Methode benötigt, ausser Konstanten, sollte als Argument übergeben werden und nicht auf magische Weise irgendwo aus der „Umgebung” kommen.
Queue's sind doch als "Thread sicher" definiert, meine ich gelesen zu haben. Ich hab halt keine andere Möglichkeit gesehen, eine neue Proxy IP in den laufenden Thread zu bekommen.
Das mit der URL-Queue verstehe ich nicht wirklich. Du erzeugst eine URL pro Proxy, verwendest dann aber nur eine URL davon pro Thread. Und machst dann einen `join()` auf diese Queue. Der kann niemals erfolgreich sein wenn nicht alle URLs auch mit einem `task_done()` quittiert werden, was nicht passiert solange es mehr Proxies als Threads gibt.
Wenn es eine URL pro Thread gibt, kann man die auch direkt übergeben, ohne eine Queue. Bei `proxy_queue` macht das `task_done()` auch keinen Sinn denn der Sinn dieser Queue ist es ja nicht das alle Proxies am Ende abgearbeitet sein müssen.
Völlig richtig - Habe ich verbessert. Mein Hirn ist schon etwas matsche bei dem Script.
Was bedeutet URLs aus livestreamer beziehen kommt nicht hinterher? Der einzige Unterschied zu vorher ist, dass die Aufrufe nicht mehr parallel ablaufen können.
Wenn ich die URL innerhalb des Threads bezogen hab, erzeuge die Lib einen Fatal Error nach einiger Zeit. Per Argument geht's aber auch.


Leider weiss ich immer noch nicht, wieso das Script ab 15 keine Viewer mehr auf Twitch sichtbar erzeugt. Manchmal kurzzeitig 4, die dann aber wieder verschwinden.
Ich finde auch keine Möglichkeit mir in irgendeiner Form den Fehler auszugeben. Stecke da fest. Eine Idee, warum das sein könnte, oder wie ich das debugge?


Änderungen im Code:
Gebe nun nur die URL als Argument mit in den Thread
BlackJack

@warcoach: Auch wenn Queues thread-sicher sind, sollte man keine globalen Variablen verwenden. Man kann die Queue ja problemlos als Argument an jeden Thread übergeben.

Was genau meinst Du mit „Fatal Error”? Gibt das eine Python-Ausnahme? Oder stürzt Python hart ab? Oder nur der externe Aufruf von ``livestreamer``?

Vielleicht erkennt Twitch ja mittlerweile solche Versuche besser‽
warcoach
User
Beiträge: 5
Registriert: Sonntag 12. Oktober 2014, 01:25

Der Error:
Traceback (most recent call last):
File "vbotforum.py", line 90, in <module>
main()
File "vbotforum.py", line 83, in main
url = get_url()
File "vbotforum.py", line 18, in get_url
return json.loads(output)['streams']['worst']['url']
KeyError: 'streams'
Ich denke nicht, dass Twitch da was geändert hat.
BlackJack

@warcoach: Da scheint die Ausgabe von dem Programm nicht das zu sein was erwartet wird. Also müsste man mal schauen *was* dort ausgegeben wird.
Raven
User
Beiträge: 2
Registriert: Sonntag 9. Juni 2019, 13:13

Ich habe ein Problem mit fertigen Programm ich habe dies in LiClipse exportiert (File System->Desktop-) wenn ich diese Datei jetzt öffne kommt einfach nichts. Vielen Dank für ihre antworten.
DerPlaner
User
Beiträge: 3
Registriert: Freitag 29. Januar 2021, 13:04

Hallo zusammen,

ich wollte das Skript einmal testen aber es funktioniert nicht.

Kann mir vielleicht jemand helfen?

Wäre echt super!
Benutzeravatar
__blackjack__
User
Beiträge: 13004
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@DerPlaner: Welcher Quelltext konkret und was bedeutet „funktioniert nicht“ genau?

Das ist auch noch Python 2 — das sollte man ändern.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
DerPlaner
User
Beiträge: 3
Registriert: Freitag 29. Januar 2021, 13:04

Hallo blackjack,

vielen dank für deine Rückmeldung!

Ich benutze Pycharm 2020.3.3 und Python 3.8.

Ich habe erst dein Quelltext genommen und dann von warcoach.

Bei deinem (blackjack) Quelltext kommt :
---------------------------------------------------------------------------------------------------------------------------------------
File "C:/Users/Planer/PycharmProjects/pythonProject2/Skrip blackjack.py", line 45
except urllib2.HTTPError, error:
^
SyntaxError: invalid syntax
----------------------------------------------------------------------------------------------------------------------------------------
Bild: https://ibb.co/58zJwDS

Bei dem Quelltext von warcoach kommt :
----------------------------------------------------------------------------------------------------------------------------------------

C:\Users\Planer\PycharmProjects\pythonProject2\venv\Scripts\python.exe "C:/Users/Planer/PycharmProjects/pythonProject2/Skript Warcoach.py"
File "C:/Users/Planer/PycharmProjects/pythonProject2/Skript Warcoach.py", line 89
thread.start()
^
IndentationError: unindent does not match any outer indentation level

Process finished with exit code 1

------------------------------------------------------------------------------------------------------------------------------------------

Bild: https://ibb.co/w0DQwF4
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist Python 2 Code, den du mit Python 3 ausfuehren willst. Das muesstest du also erstmal umschreiben.

Aber das ist *uralter* Code, selbst wenn du den in Python 3 hingefummelt bekaemest, laeuft der nicht.

Such nach neuerem Code.
DerPlaner
User
Beiträge: 3
Registriert: Freitag 29. Januar 2021, 13:04

hallo deets, ich habe schon bei github geguckt aber leider noch nichts gefunden.

Hast du vielleicht noch andere Quellen?

VG
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nein.
Antworten