python im Internet & HTML

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
evd
User
Beiträge: 34
Registriert: Donnerstag 20. Juni 2013, 19:19

ka... kann sein ... aber bei lxml habe ich auch nicht das Problem, da läuft alles ohne tadel, aber bei "from lxml.cssselect import CSSSelector" findet es schon cssselect nicht ... ka ... lxml habe ich auch über pip installierten können (unter py3) aber da will es nicht mit machen
BlackJack

@evd: Mit `lxml` meine ich das gesamte `lxml`-Package. Da sollte `lxml.cssselect` eigentlich dabei sein. Vielleicht hattest Du vor dem installieren via ``pip`` nicht alle nötigen Abhängigkeiten installiert?

Edit: Habe das gerade mal ausprobiert: Man muss noch das `cssselect`-Modul installieren. Also nicht `lxml.cssselect` sondern nur `cssselect`.
evd
User
Beiträge: 34
Registriert: Donnerstag 20. Juni 2013, 19:19

also ich habe es im Ubuntu software Center gefunden (cssselekt) aber ich habe keine ahnung ob das dann für py2 oder 3 ist ... (da steht nischt dabei) und ich habe keinen Schimmer mehr wie ich das mit dem pip machen kann... entweder ich mache diganze zeit einen fehler oder sie wollen mir wirklich sagen das ich es schon installiert habe (sudo pip3 install cssselect) .... hast du da n schimmer?
BlackJack

@evd: Also ich habe es genau so installiert. Wenn das bei Dir nicht funktioniert, dann müsstest Du mal ein bisschen genauer werden was die Fehlermeldungen angeht (kopieren) und wie Du testest das/ob es installiert ist. Nimmst Du dafür auch einen *neuen* Interpreterlauf?
evd
User
Beiträge: 34
Registriert: Donnerstag 20. Juni 2013, 19:19

Code: Alles auswählen

Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/lxml/cssselect.py", line 16, in <module>
    external_cssselect = __import__('cssselect')
ImportError: No module named 'cssselect'
beim herunterladen

Code: Alles auswählen

pip can't proceed with requirement 'cssselect' due to a pre-existing build directory.
 location: /tmp/pip_build_root/cssselect
This is likely due to a previous installation that failed.
pip is being responsible and not assuming it can delete this.
Please delete it and try again.

Storing debug log for failure in /home/evd/.pip/pip.log
BlackJack

@evd: Na da steht doch ziemlich deutlich warum pip das nicht installiert und was man machen könnte/sollte bevor man es erneut versucht.
evd
User
Beiträge: 34
Registriert: Donnerstag 20. Juni 2013, 19:19

Danke XD

nach einem Neustart hat es mir die Fehlermeldung auch nimmer angezeigt und ich konnte es ohne Probleme Installieren XD
das ist ja schon fast auf dem gleichen lvl wie "ist auch der Stecker eingesteckt" XD

naja...

also ich habe es jetzt soweit

Code: Alles auswählen

import lxml.html
import requests
r=requests.get("http://www.amazon.de/...")
test=lxml.html.fromstring(r.text)
link = test.cssselect('html body div div div div div div div div h3.newaps div a')
for i in range(0,len(link)):
    print (lxml.html.tostring(link[i]))
das Resultat ist jetzt

Code: Alles auswählen

b'<a href="http://www.amazon.de/Greatest-Hits-So-Far-Explicit/dp/artist-redirect/B00F4122W4">P!nk</a>'
wie kann ich da jetzt den Namen (hier P!nk) extrahieren ...
Die URL bekomme ich ja mit

Code: Alles auswählen

link[i].get("href")
also ich könnte natürlich den String bearbeiten ...
aber das ist halt keine so schöne Lösung ...
BlackJack

@evd: Du musst halt in die Dokumentation schauen wie man an den Text heran kommt. Ein Attribut oder eine Methode gibt's dafür.

Die Schleife mit dem `i` ist ein „anti pattern” in Python. Du kannst da *direkt* über die Elemente iterieren, ohne einen Umweg über einen Index.

Beim CSS ist ein klein bisschen IMHO zu viel angegeben. Das findet die Daten ja nur wenn das Dokument *exakt* diesen Pfad dort hin enthält. Der Witz bei solchen Selektoren ist ja gerade dass man die genau genug macht um die Daten zu finden, aber nicht so genau das man sie nicht mehr finden kann wenn auch nur eine Kleinigkeit an der Seitenstruktur geändert wird. 'html' und 'body' kann man schon mal weg lassen weil es nichts ausserhalb von 'html' gibt, und wenn die Informtionen innerhalb der Websseite angezeigt werden, dann ist 'body' auch irgendwo selbstverständlich. Ausserhalb von Body wird es keines der anderen Tags geben.
evd
User
Beiträge: 34
Registriert: Donnerstag 20. Juni 2013, 19:19

danke... habe jetzt soweit alles.... nur jetzt habe ich ein anderes Problem ...

... wenn ich einen Namen mit einem ä,ö,ü,ß auslese bekomme ich (natürlich) die Aussage, das ASCII damit wenig anfangen kann... ich dachte mir UTF-8 ftw

Code: Alles auswählen

lxml.html.tostring(link[i], method="text", encoding="utf-8")
nun gibt er mir immer noch nur Müll aus, nur jetzt ohne meldung, der knallt mit einfach die "F\xc3\" und "\xc3\xa4" in mein Text, das es nimmer feierlich ist.

was für ein encoder soll ich denn verwenden?
BlackJack

@evd: Das kann nicht sein. Wenn Du da UTF-8 angibst, dann werden in den Text keine Escape-Sequenzen eingefügt. Die kommen da erst rein wenn Du die `repr()`-Umwandlung direkt oder indirekt machst und das dann anschaust. Das ist ja auch gut so, denn wenn man das macht möchte man ja wissen welche Bytewerte sich tatsächlich in einer Zeichen-/Bytekette befinden, unabhängig davon was das ”Ausgabemedium” als Kodierung erwartet.

Falls Du `html.tostring()` jetzt tatsächlich zum auslesen des Attributs mit dem Text von dem Link-Tag verwendest, würde ich das als Missbrauch der Funktion ansehen.
evd
User
Beiträge: 34
Registriert: Donnerstag 20. Juni 2013, 19:19

ich verstehe jetzt nicht wo da jetzt der Missbrauch der Funktion "tostring()" ist, ich will doch was in ein string umwandeln ...

ach wenn es dir an glauben fehlt das mit uft-8 diese Meldungen rein donnert bekomme ich bei dem:

Code: Alles auswählen

print(link[23])
print(lxml.html.tostring(link[23], method="text", encoding="utf-8"))
print((link[23].get("href")))
print(lxml.html.tostring(link[23], encoding="utf-8"))
print(lxml.html.tostring(link[23], method="text"))
(Programmcode siehe oben)

bekomme ich als Ergebnis dieses hier:

Code: Alles auswählen

<Element a at 0x7ff8bef618b8>
b'F\xc3\xbcnf Freunde'
b'<a href="http://www.amazon.de/108-die-Entf%C3%BChrung-im-Skigebiet/dp/artist-redirect/B00MBJ4K5Y">F\xc3\xbcnf Freunde</a>'
http://www.amazon.de/108-die-Entf%C3%BChrung-im-Skigebiet/dp/artist-redirect/B00MBJ4K5Y
Traceback (most recent call last):
  File "/home/evd/Schreibtisch/python/Amazon/a.py", line 21, in <module>
    print(lxml.html.tostring(link[23], method="text"))
  File "/usr/lib/python3/dist-packages/lxml/html/__init__.py", line 1621, in tostring
    doctype=doctype)
  File "lxml.etree.pyx", line 3157, in lxml.etree.tostring (src/lxml/lxml.etree.c:69331)
  File "serializer.pxi", line 99, in lxml.etree._tostring (src/lxml/lxml.etree.c:114033)
  File "serializer.pxi", line 71, in lxml.etree._textToString (src/lxml/lxml.etree.c:113825)
UnicodeEncodeError: 'ascii' codec can't encode character '\xfc' in position 1: ordinal not in range(128)
ergo UTF-8 habe ich "\xc3\xbc" an stelle von einem "ü"
und ich bekomme das was ich will (dafür ja ' method="text" ')
ich habe sonst keinen Ahnung wie ich aus "<Element a at 0x7ff8bef618b8>" einen string machen soll also "lxml.html.tostring()
BlackJack

@evd: Du willst den Text eines Elements haben, dafür haben die ein Attribut und eine Methode um diesen Text *direkt* zu bekommen. Stattdessen verwendest Du eine Funktion die deutlich mehr macht, und in diesem Sonderfall halt zufällig das gleiche liefert. Das würde ich Missbrauch nennen.

Das was Du da ausgegeben bekommst ist die Darstellung eines `bytes`-Objekts als Zeichenkette. Also die Zeichenkette die `repr()` auf diesem Wert liefert. Und Bytes ausserhalb von ASCII werden halt als Escapesequenzen dargestellt. Das ist sehr praktisch weil man dann sehen kann welche Bytewerte enthalten sind, ohne das das von einem Terminal noch mal als irgend eine andere Kodierung interpretiert wird.

In Bytes gibt es auch kein 'ü' sondern höchstens eine irgendwie geartete Kodierung eines 'ü'. In UTF-8 sind das die beiden Bytes mit den Werten 195 und 188, beziehungsweise hexadezimal ausgedrückt c3 und bc.

Einen String, also eine *Zeichen*kette, machst Du übrigens auch gar nicht aus dem Element, die Funktion heisst nur aus historischen Gründen so und liefert nur eine *Zeichen*kette wenn man als `encoding` den Wert `unicode` angibt. Aber wie gesagt, um an den Text im <a>-Tag zu kommen ist die Funktion sowieso nicht wirklich das richtige.
evd
User
Beiträge: 34
Registriert: Donnerstag 20. Juni 2013, 19:19

So, ich habe es soweit...

nur noch ein Problem stellt sich mir noch im punkto Internet...

wenn ich eine Seite herunterlade

z.b.:

Code: Alles auswählen

self.r=requests.get("http://www.amazon.de")
am Anfang geht es, doch am ende bekomme ich einen Fehler.
Es kann die Verbindung nicht zu amazon aufbauen...

Code: Alles auswählen

requests.exceptions.ConnectionError: HTTPConnectionPool(host='www.amazon.de', port=80): Max retries exceeded with url: "....." (Caused by <class 'socket.gaierror'>: [Errno -2] Name or service not known)

Habe ich da zu viele aufrufe pro sec. gemacht?
wurde ich da von dem Server gesperrt oder wie?
oder warum kann ich da keine Verbindung aufbauen?
evd
User
Beiträge: 34
Registriert: Donnerstag 20. Juni 2013, 19:19

ok, sorry für den Doppel post ... habe aber verstanden warum die Fehlermeldung gekommen ist... wurde wahrscheinlich einfach von Amazon server gebannt für kurze zeit XD

jetz habe ich habe ein anderes problem...

dieses ganze hin und her habe ich für ein kleines Programm gemacht, welches bei Amazon die Seiten auslesen und analysieren soll... ich habe den code für diesen teil jetzt geschrieben... aber es will nicht so wie ich es will

Code: Alles auswählen

import threading, queue, lxml.html, requests

#Thread welches die Seiten in Amazon herunterlädt (queue1=1-400 welches den Link vervollständigt, queue2=die Seite)
class suchen():
    def __init__(self, queue1, queue2):
        self.queue1=queue1
        self.queue2=queue2
        self.sucher()
        
    def sucher(self):
        while  True:
            self.i = self.queue1.get()
            if self.i is None:
                self.queue1.task_done()
                break
            else:
                try:
                    self.r=requests.get("http://www.amazon.de/s/ref=sr_pg_"+str(self.i)+"?rh=n%3A77195031%2Cp_36%3A200-499%2Cp_n_format_browse-bin%3A180848031&page="+str(self.i)+"&bbn=77195031&ie=UTF8&qid=1395167727", timeout=60)
                    self.queue2.put(self.r)
                    print(self.i)
                except:
                    print("-")
                self.queue1.task_done()
            

queue1=queue.Queue(500)
queue2=queue.Queue(500)
knuenztlerliste=[]
albumnamenliste=[]
linkalbumliste=[]
bildalbumliste=[]
preisalbumliste=[]
anzahlderrunden=400        #wie viele Seiten ich durchsuchen will
anzahlderthreads=5          #wie viele threads ich dafür starten will (5 weil ich da hoffe nicht so schnell gesperrt zu werden
for n in range (1,anzahlderrunden + anzahlderthreads+1):
    if n > anzahlderrunden:
        queue1.put(None)
    else:
        queue1.put(n)
for s in range (anzahlderthreads):
    threading.Thread(target=suchen, args=(queue1,queue2)).start()
queue1.join()

#hier werden die Seiten durchsucht, nach den Werten die ich haben will
for anzahl in range (queue2.qsize()):
    print(anzahl)
    r=queue2.get()
    test=lxml.html.fromstring(r.text)
    link = test.cssselect('h3.newaps div a')
    n = 0
    for element in link:
#weil in 'schauen1' bei zu langen namen "..." am ende steht
        schauen1=len((element.text))
        try:
            schauen2=len((link[n].get("title")))
        except:
            schauen2=0
        if schauen2>schauen1:
            knuenztlerliste.append((link[n].get("title")))
        else:
            knuenztlerliste.append(element.text)
        n=n+1
    link = test.cssselect('div div h3.newaps a')
    n=0
    for element in link:
        Fehlerbehebung1=n%2          #weil 2 "href" hintereinander sind (ich will nur den ersten)
        if Fehlerbehebung1==0:
            linkalbumliste.append(link[n].get("href"))
        n=n+1
    link = test.cssselect('div h3.newaps a span.lrg.bold')
    n=0
    for element in link:
        schauen1=len((element.text))
        try:
            schauen2=len((link[n].get("title")))
        except:
            schauen2=0
        if schauen2>schauen1:
            albumnamenliste.append(link[n].get("title"))
        else:
            albumnamenliste.append((element.text)) 
        n=n+1
    link = test.cssselect('a div.imageBox img.productImage.cfMarker')
    n=0
    for element in link:
            
        bildalbumliste.append(link[n].get("src"))
        n=n+1
    link = test.cssselect('span.a-button.a-button-primary.MusicCartBuyButton span.a-button-inner a.a-button-text')
    n=0
    for element in link:   
        a=(element.text)    
        preisalbumliste.append(a[21:len(a)])
        n=n+1

#Ausgabe der Werte (im Hauptprogramm wird dies dann anders realisiert)       
print(len(preisalbumliste))
print(len(bildalbumliste))
print(len(linkalbumliste))
print(len(albumnamenliste))
print(len(knuenztlerliste))
print("Es sollten",anzahlderrunden*24,"sein")

Hier habe ich das Problem das es mir fast immer einen anderen wert zurück gibt, als würde es mal mehr mal weniger finden ... aber das kann doch nicht sein ...

ich verstehe die Welt nimmer
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@evd: ich versteh auch nicht, was der ganze Code macht, er ist einfach zu lang. Du solltest den Spaghetti-Code auch unbedingt in etliche Funktionen aufteilen.
Was willst Du erreichen? Was kommt raus, was erwartest Du?
BlackJack

@evd: Also wenn diese Listen alle gleich lang sein sollen dann legt das irgendwie den Verdacht nahe das die Informationen am gleichen Index jeweils zusammengehören sollen. In dem Fall sollte man die Daten nicht auf mehrere ”parallele” Listen verteilen sondern jeweils zu einem Objekt zusammenfassen. Im einfachsten Fall reicht dafür vielleicht ein Typ den man sich mit `collections.namedtuple()` erstellt.

Dann müsste man die Informationen zu einem Produkt (nehme ich mal an) auch auf einmal zusammen sammeln und nicht jeden Teil einzeln.

Dieses `n` in den Schleifen ist eigenartig und verwirrend. Du iterierst über `link` und zählst gleichzeitig manuell `n` hoch um ab und zu mit diesem `n` als Index auf `link` zuzugreifen, wobei eigentlich immer nur noch mal das Objekt heraus kommen kann was Du in der Schleife sowieso schon mal an den Namen `element` gebunden hast. Was soll dieser Unsinn? `link` steht nicht für einen Link sondern für *viele*, sollte also eher `links` heissen.

Dann sind da ``except``\s ohne konkrete Ausnahmen drin. Das sollte man nicht machen denn das kann dazu führen dass man Ausnahmen behandelt die man nicht erwartet hat, und die *so* gar nicht sinnvoll behandelbar sind.

Die Klasse `suchen` ist unsinnig. Warum ist das eine Klasse und keine Funktion? Die `__init__()` erledigt die komplette Aufgabe. Ausser der `__init__()` gibt es nur eine weitere Methode. Klassenname und Methodenname sind genau falsch herum, Klassen stellen ”Dinge” dar und Methoden tun etwas.

An eine Klasse gehören nur Attribute die den Zustand des Objekts ausmachen und nicht einfach *alles* was man irgendwo mal in einer Methode an einen Namen binden möchte. Ausserhalb der `__init__()` sollte man auch keine weiteren Attribute einführen.

`queue1` und `queue2` sind schlechte Namen. Namen sollen dem Leser die *Bedeutung* vermitteln, nicht einfach den Datentypen mit einer nichtssagenden Nummer angehängt.

`knuenztlerliste`? ;-) Der Datentyp sollte nicht im Namen stecken. Wenn man den mal ändert, muss man auch überall den Namen ändern, oder man hat falsche, irreführende Namen im Programm. Containerobjekten kann man zum Beispiel einfach die Mehrzahl von dem Namen geben dem man einem Element geben würde. Wobei dann englische Namen praktischer sind, weil es dort sehr selten vorkommt das Einzahl und Mehrzahl gleich benannt sind (artist/artists vs. kuenstler/kuenstler).

Statt das herunterladen und das verarbeiten durch ein `join()` auf der Queue zu trennen, wo der Hauptthread also eine ganze Weile nichts macht, könnte man im Hauptthread auch schon die Verarbeitung machen. Oder sogar in den Threads parallel und in die `queue2` dann schon fertige Ergebnisse stecken.
evd
User
Beiträge: 34
Registriert: Donnerstag 20. Juni 2013, 19:19

so, und da bin ich auch wieder...

ich habe nun versucht die von euch kritisierten Sachen entweder zu beheben oder durch eine Erklärung darunter verständlich zu machen.

Meine Erwartung von diesem Programm ist das ich dadurch von der Internetseite amazon jegliche Seiten herunterlade um sie dann auszuwerten.
hinein gebe ich den "Start" Befehl, heraus soll eine Liste von Künstlern, Alben, preisen usw (siehe Beschreibung) kommen, welche ich dann in meinem weiteren Programm verwenden will.

1)Hier habe ich die Frage, warum kann ich nicht das Programm ohne "productImage cfMarker" auf die Suche schicken? es wird dann immer gesagt das es nicht enthalten sei... verstehe ich nicht... ich meine wenn ich eine Ebene weiter oben bin müssten doch immer noch die Nachfahren enthalten sein

2)Das Problem das sich mir hier stellt (was neu ist... zeit dem ich das alles in eine Klasse ein gehauen habe...) ist das es am Anfang die werte aus dem queue heraus liest, aber diese nicht verwendet, und dann mehrmals versucht NONE in die URL ein zu setzen, obwohl ich das eigentlich vorher durch eine if abfrage abfangen müsste ...
so ergibt sich aus 5 Seiten 2 Threads:

Code: Alles auswählen

wert: 1
wert: 2
benutze wert: 2
wert: 3
benutze wert: 3
wert: 4
benutze wert: 4
wert: 5
benutze wert: 5
wert: None
Ende
benutze wert: None
wert: None
Ende
Warum benutzt er noch ein mal wert "None", und wo bleibt das benutzen von wert 1? :K

3) Ich lade die Seiten herunter, und sie sind einfach nicht vollständig ... ka warum ... wenn ich die selbe Seite noch ein mal herunterlade dann ist wieder alles in Butter... ist das einfach der eine unter Hundert ... (hier dann doch ein wenig mehr) ... oder habe ich das was Falsch gemacht?

Code: Alles auswählen

"""
in den 3 Dreidimensionalen Feld ist die erste ebene die Seite (1,2,3,...). 
die Zweite Ebene die Unterteilung (kuenztler,albumname,link,bild,preis)
die Dritte Ebene ist für die exakten Daten freigegeben
"""


import threading, queue, lxml.html, requests
class sucher():
    """
    Diese Klasse sucht auf den Seiten von Amazon nach den Künstlern, Preisen, Alben ... und gibt dieser in
    gegebener Reihenfolge in einem Feld "Scanresultat" wieder.
    """
    def __init__(self):
        self.threadinput=queue.Queue(500)
        self.threadoutput=queue.Queue(500)
        self.zulange=0
        self.n = 0
        self.Scanresultat=[]
        self.Fehler=[]
        self.r=[]
        self.banzahl1=0
        self.banzahl2=0
        self.banzahl3=0
        self.banzahl4=0
        self.banzahl5=0
        for self.i in range (0,400):
            self.Scanresultat.append([])
            for self.n in range (5):
                self.Scanresultat[self.i].append([])
    
    #Dies sind die Threads die gestartet werden sollen, und welche dann die Seite herunter laden
    # 2)
    # 3)
    def suchen(self, threadinput, threadoutput):
        self.athreadinput=threadinput
        self.athreadoutput=threadoutput
        while  True:
            self.r=[]
            self.i = self.athreadinput.get()
            print("wert:",self.i)
            if self.i == None:
                self.athreadinput.task_done()
                print("Ende")
                break
            else:
                try:
                    #als erstes kommt die Seite, und dann die Seiten Nr. um es bei Buggs besser zu unterscheiden und um die Ordnung aufrecht zu erhalten
                    self.r.append(requests.get("http://www.amazon.de/s/ref=sr_pg_"+str(self.i)+"?rh=n%3A77195031%2Cp_36%3A200-499%2Cp_n_format_browse-bin%3A180848031&page="+str(self.i)+"&bbn=77195031&ie=UTF8&qid=1395167727", timeout=60))
                    self.r.append(self.i)
                    self.athreadoutput.put(self.r)
                    print("benutze wert:",self.i)
                except:
                    #wenn die Seite nicht heruntergeladen werden kann 
                    print("-",self.i)
                    self.r.append("-")
                    self.r.append(self.i)
                    self.athreadoutput.put(self.r)
                    
                self.athreadinput.task_done()
    
    #Ruft die Threads auf und setzt die werte für die queue
    def aufrufen (self,anzahl,threads):
        self.anzahlderrunden=anzahl
        self.anzahlderthreads=threads
        for self.n in range (1,self.anzahlderrunden + self.anzahlderthreads+1):
            if self.n > self.anzahlderrunden:
                self.threadinput.put(None)
            else:
                self.threadinput.put(self.n)
        for self.s in range (self.anzahlderthreads):
            threading.Thread(target=self.suchen, args=(self.threadinput,self.threadoutput)).start()
        self.threadinput.join()
        self.auswerten()
        
    #Liest die werte aus, und Speichert die Ergebnisse in ein Feld, Sortierung siehe anfangs-Kommentar     
    def auswerten(self):
        for self.anzahl in range (self.anzahlderrunden):
            self.r=self.threadoutput.get()
            self.test=lxml.html.fromstring(self.r[0].text)
            self.links = self.test.cssselect('h3.newaps div a')
            for element in self.links:
                self.schauen1=len(element.text)
                #wenn der Name zu lang ist wird er durch ein "..." am ende ersetzt, hier wird geschaut ob das der Fall ist
                try:
                    self.schauen2=len((element.get("title")))
                except:
                    self.schauen2=0
                if self.schauen2>self.schauen1:
                    self.Scanresultat[self.r[1]][0].append(element.get("title"))
                else:
                    self.Scanresultat[self.r[1]][0].append(element.text)
            self.links = self.test.cssselect('div div h3.newaps a')
            self.n=0
            for element in self.links:
                #Hier muss ich n benutzen, denn ich brauche nur jedes 2. Suchergebnis... und dies erschien mir als die einfachste und schlichteste Möglichkeit
                self.Fehlerbehebung1=self.n%2
                if self.Fehlerbehebung1==0:
                    self.Scanresultat[self.r[1]][2].append(element.get("href"))
                self.n=self.n+1
            self.links = self.test.cssselect('div h3.newaps a span.lrg.bold')
            for element in self.links:
                #wenn der Name zu lang ist wird er durch ein "..." am ende ersetzt, hier wird geschaut ob das der Fall ist
                self.schauen1=len(element.text)
                try:
                    self.schauen2=len(element.get("title"))
                except:
                    self.schauen2=0
                if self.schauen2>self.schauen1:
                    self.Scanresultat[self.r[1]][1].append(element.get("title"))
                else:
                    self.Scanresultat[self.r[1]][1].append(element.text)
            self.links = self.test.cssselect('div.imageBox img.productImage.cfMarker') # 1)
            for element in self.links:
                self.Scanresultat[self.r[1]][3].append(element.get("src"))
            self.links = self.test.cssselect('a.a-button-text')
            self.n=0
            for element in self.links:
                #Ich benötige den "n" Zähler, denn es gibt auch noch Ergebnisse die mich nicht interessieren (elemente > als 24)
                if self.n<24:
                    self.a=(element.text)    
                    self.Scanresultat[self.r[1]][4].append(self.a[21:len(self.a)])
                    self.n=self.n+1
        print(self.Scanresultat)

asucher=sucher()
asucher.aufrufen(5,2)
Ich hoffe das ich eure Nerven nicht über strapaziere...
und ihr mit weiterhelft (vor allem bei dem Punkt 2 und 1)

Mit freundlichen Grüßen

EvD
BlackJack

@evd: Das ist mir ehrlich gesagt immer noch viel zu konfus zum lesen. Fast sogar noch schlimmer als vorher. Die Klasse ist keine, das ist doch eigentlich nur modulglobales Zeug in eine Klasse verschoben die viel zu viele Attribute hat und viel zu viel weiss und kann. Die ”Methoden” sind auch nicht sinnvoll aufgeteilt und schrecklich benannt, und diese grosse, vorerstellte Ergebnisstruktur ist auch unschön.

Und dann bindest Du anscheinend ausnahmslos alle Namen an das eine Objekt. Was soll das? Irgendein lokales `i` trägt sicher nicht zum Zustand des `sucher`-Objekts bei, im Gegenteil, dieser Unsinn ist wahrscheinlich eine der Problemquellen wenn das von mehr als einem Thread benutzt wird.

Ich würde das alles wegwerfen und anfangen einfache Funktionen zu schreiben die man leicht einzeln Testen kann. Und daraus dann nach und nach ein grösseres Programm zusammen zu bauen.

Zum Beispiel könnte man eine Funktion schreiben die genau eine Webseite auswertet. Nur auswertet — nicht herunter lädt! Und zwar in dem sie die einzelnen Elemente ermittelt die jeweils ein Produkt enthalten. Die Daten aus so einem Element kann man mit einer weiteren Funktion extrahieren die dann für jedes Produkt aufgerufen wird.

Die Funktion zum Auswerten einer Seite kann man dann für jede Seite aufrufen. Jede Funktion sollte dabei ihr Teilergebnis zurück geben und ggf. aus dem von aufgerufenen Unterfunktionen zusammen setzen. Also nicht irgendwo eine grosse Struktur vorinitialisieren, sondern Stück für Stück die Teilergebnisse zu einem Gesamtergebnis zusammen setzen.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@evd: warum sind r, n und i Klassenattribute? Das sind zum einen Laufvariablen zum Initialisieren von Listen, zum anderen Elemente aus der Queue. Laufvariablen sind nie Klassenattribute, weil sie sich nur innerhalb einer Methode sogar nur innerhalb einer Schleife sinnvoll sind. Zum zweiten handelst Du Dir damit bei Deinen Threads Probleme ein, weil sie alle die selben Attribute verwenden, sich also gegenseitig überschreiben, daher das None. In einer threaded Methode dürfen also im Normalfall keine Attribute überschrieben werden! Dann nummerierst Du Attribute durch, was ein deutliches Zeichen dafür ist, dass Du eigentlich eine Liste verwenden willst.
Das vorinitialisieren von Listen ist in Python unüblich, weil man alles dynamisch aufbauen kann. Du führst weitere Attribute außerhalb der __init__-Methode ein. Methoden sollten nicht irgendwie wild irgendwelche Listen verändern. Du weißt doch später nicht mehr, wo was mit welcher Abhängigkeit gemacht wird. Schreib Funktionen, mit definierten Aufgaben, Inputparametern und Rückgabewerten.
BlackJack

Mal ein etwas übersichtlicherer Ansatz:

Code: Alles auswählen

#!/usr/bin/env python
# coding: utf8
from __future__ import absolute_import, division, print_function
import json
import logging

import requests
from concurrent.futures import ThreadPoolExecutor
from lxml import html

logging.basicConfig()
LOG = logging.getLogger('main')
LOG.setLevel(logging.INFO)

RESULT_URL_TEMPLATE = (
    'http://www.amazon.de/s/ref=sr_pg_{0}'
        '?rh=n%3A77195031%2Cp_36%3A200-499%2Cp_n_format_browse-bin%3A180848031'
        '&page={0}'
        '&bbn=77195031'
        '&ie=UTF8'
        '&qid=1395167727'
)


def parse_product(node):
    title_node, artist_node = node.cssselect('h3 a')
    title_span = title_node.find('span')
    buy_button_nodes = node.cssselect('.MusicCartBuyButton a')
    product_image_nodes = node.cssselect('img.productImage')
    if not product_image_nodes:
        product_image_nodes = node.cssselect('img.placeholder')
    return {
        'image_url': product_image_nodes[0].get('src'),
        'url': title_node.get('href'),
        'title': title_span.get('title') or title_span.text,
        'artist': artist_node.text,
        'price': float(
            buy_button_nodes[0].text.rpartition(' ')[2] .replace(',', '.')
        ) if buy_button_nodes else None,
    }


def load_products(url):
    result = {'source': url, 'error': None, 'products': list()}
    response = requests.get(url)
    if response.ok:
        result['products'] = map(
            parse_product,
            html.fromstring(response.content)
                .get_element_by_id('resultsCol')
                .cssselect('div.prod')
        )
    else:
        result['error'] = {
            'status_code': response.status_code,
            'reason': response.reason,
        }
    return result


def load_page(number):
    LOG.info('loading page %d', number)
    result = load_products(RESULT_URL_TEMPLATE.format(number))
    result['page'] = number
    return result


def load_pages(max_workers, max_pages):
    executor = ThreadPoolExecutor(max_workers)
    futures = [
        (i, executor.submit(load_page, i)) for i in xrange(1, max_pages + 1)
    ]
    pages = list()
    for i, future in futures:
        try:
            page = future.result()
        except Exception as error:
            page = {
                'error': str(error),
                'page': i,
            }
        pages.append(page)
    return {
        'pages': pages,
    }


def main():
    pages = load_pages(5, 20)
    print(json.dumps(pages, indent=2))


if __name__ == '__main__':
    main()
Edit: Produktverarbeitung um die Möglichkeit erweitert, dass statt einem Produkbild auch ein Platzhalter vorhanden sein könnte.
Antworten