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()