BeautifulSoup

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.
Antworten
Stay
User
Beiträge: 4
Registriert: Donnerstag 4. Februar 2016, 23:53

Hi, ich bin relativ neu in Python und habe direkt eine Frage.

Ich versuche vom Netzwerkserver eine html auszulesen.

Code: Alles auswählen

soup = BeautifulSoup (urlopen("http://localhost/m.html"))

result1 = soup.find_all('li', {'class': 'lvgrad'})

print (result1)
Als Antwort bekomme ich.

Code: Alles auswählen

<li class="lvgrad prc">
<span  class="bold">
<b>Grad</b> 33,59</span></li>
Wie beklomme ich das hin, dass ich nur
Grad; 33,59
Als Antwort bekomme?
BlackJack

@Stay: Die passende Methode wird zufällig in der Beautiful Soup Documentation im ersten Abschnitt „Quick Start“ schon gezeigt. Und weiter unten steht eine ausführlichere Beschreibung von der Methode.
Benutzeravatar
pixewakb
User
Beiträge: 1412
Registriert: Sonntag 24. April 2011, 19:43

Danke! Das ist auch bei mir eine Standardaufgabe und ich habe jetzt wieder länger nicht mit dem Modul gearbeitet.

Code: Alles auswählen

from bs4 import BeautifulSoup

html = """<body><li class="lvgrad prc">
<span  class="bold">
<b>Grad</b> 33,59</span></li></body>"""

soup = BeautifulSoup(html, "html.parser")

results = soup.find_all("li", {"class": "lvgrad"})

for result in results:
    print([result.get_text()])  # Angucken der Lieferung
    print(result.get_text().strip("\n"))
@Stay: Ich muss schon mal häufiger Webseiten für Daten parsen und ich habe mir inzwischen angewöhnt in der Entwicklung bei print-Anweisungen die Variable einfach mal in eckige Klammern zu setzen (z. B. "[result.get_text()]"), weil mir in der Konsole dann die Sonderzeichen konkret und genau angezeigt werden (also " ", "\n", "\r" oder "\t") und ich dann genau weiß, was ich vielleicht noch rausschmeißen muss. Schau es dir mal an, vielleicht ist es für Dich auch nützlich. Vielleicht löst du das Problem auf eine andere Weise, dann lass es mich wissen.
Stay
User
Beiträge: 4
Registriert: Donnerstag 4. Februar 2016, 23:53

Hi,

vielen Dank für die Hilfe. Ich habe mir jetzt was zurecht gebastelt :-(
Es funktioniert nur semioptimal. In der Konsole werden mir die Werte richtig ausgeworfen, aber leider in meiner text.csv nicht. Die Werte aus result1 sind alle die gleichen. In der Konsole sind sie korrekt.

Code: Alles auswählen

soup = BeautifulSoup (urlopen("http://localhost/m.html"), "html.parser")


f = csv.writer(open("test.csv", "w"))
f.writerow(["Grad","Format","Datum"]) 

result1 = soup.find_all("li", {"class": "lvgrad"})
for result_back_one in result1:
   one = result_back_one.get_text().strip() 
   one_new = one.replace('Grad', 'Grad;')
   print([one_new])
   
   
   result2 = soup.find_all("li", {"class": "lvformat"})
for result_back_two in result2:
   two = result_back_two.get_text().strip() 
   print([two])     
   
   

result3 = soup.find_all("li", {"class": "lvdate"})
for result_back_three in result3:
   three = result_back_three.get_text().strip() 
   print([three_new])   
    
   
   f.writerow([one_new, two, three_new])
Benutzeravatar
pixewakb
User
Beiträge: 1412
Registriert: Sonntag 24. April 2011, 19:43

Das ist so schlicht kein lauffähiges Skript. Falls möglich könntest Du mal hier etwas HTML-Code posten (aus deiner HTML-Seite), allerdings mit geänderten Werten und vielleicht nur 2, 3 Datenzeilen. Im geposteten Quellcode fehlen die import-Anweisungen, die Einrückungen sind falsch und damit geht die Logik verloren, weil du anderes machst, als du eigentlich willst. Du könntest für die Zukunft auch mal die Fehlermeldungen posten, die dir dein Programm auswirft oder beispielhaft mal das, was in der Datei steht und was du eigentlich erwartet hättest...

BTW: Lies das Tutorial und arbeite es durch, ich habe aber irgendwie bei dir den Eindruck, dass Du schon länger programmierst.
Stay
User
Beiträge: 4
Registriert: Donnerstag 4. Februar 2016, 23:53

Hi,

hier mal einen Ausschnitt aus der html. Habe jetzt nur einen Datensatz gepostet, dass es nicht zu lange wird. Insgesamt sind es 50x solche Daten je html

Code: Alles auswählen

<li fid="4243b1c99" _sp="p2046732.m1686.l1" Id="531698" class="sresult lvresult clearfix li shic" r="2" >
	
	<li class="lvgrad"><span  class="bold"><b>Grad</b>38,49</span>
	</li>
    
	<li class="lvformat"><span >MEZ</span>
	</li>
	
    <li class="lvdate"><span class="date">05-02-2016</span>
    </li>
	
        
</ul>
</li>
Ich möchte am Ende eine Tabelle erstellen die in etwa so aussieht.

Bild



Und hier noch das komplette script.

Code: Alles auswählen

# coding: utf-8
import csv
from bs4 import BeautifulSoup 
from urllib.request import urlopen

soup = BeautifulSoup (urlopen("http://localhost/m.html"), "html.parser")


f = csv.writer(open("test.csv", "w", newline=''))
f.writerow(["Grad","Format","Datum"]) 

result1 = soup.find_all("li", {"class": "lvgrad"})
for result_back_one in result1:
   one = result_back_one.get_text().strip() 
   one_new = one.replace('Grad', 'Grad;')
   print([one_new])
   
   
   result2 = soup.find_all("li", {"class": "lvformat"})
for result_back_two in result2:
   two = result_back_two.get_text().strip() 
   print([two])     
   
   

result3 = soup.find_all("li", {"class": "lvdate"})
for result_back_three in result3:
   three = result_back_three.get_text().strip() 
   print([three_new])   
    
   
   f.writerow([one_new, two, three_new])

pixewakb hat geschrieben: BTW: Lies das Tutorial und arbeite es durch, ich habe aber irgendwie bei dir den Eindruck, dass Du schon länger programmierst.
Heute nicht mehr. Und Jein, das was ich mache kann man nicht programmieren nennen. Ich habe mich eine Zeit lang mit html und c++ beschäfftigt, aber nie wirklich auf einem stand der es mir ermöglicht hätte ohne google auszukommen. Seit ca. 6 Jahren habe ich garnichts mehr gemacht und nun mache ich meine ersten Versuche mit Python.
Benutzeravatar
pixewakb
User
Beiträge: 1412
Registriert: Sonntag 24. April 2011, 19:43

Der von dir bereitgestellte HTML-Code ist m. E. fehlerhaft, jedenfalls habe ich mal korrigiert, wie das aussehen könnte. Es wäre gut, du würdest zukünftig bei Wertelisten nicht ein fehlerhaftes Teillistenelement (!) bringen. Kann sein, dass deshalb unten stehender Quellcode bei dir nicht läuft. Ich habe den Code auch nicht geladen, sondern im Skript beigegeben.

Der Quellcode läuft bei mir jetzt, sieht aber alles (!) nicht gut aus. Für mehr reichte gerade die Zeit nicht, d. h. mir gefallen die Namen nicht, die Art des Auslesens (sollte sinnvoller gehen) usw. usf.

Code: Alles auswählen

# coding: utf-8
import csv
from bs4 import BeautifulSoup 
from urllib.request import urlopen

# Dein HTML-Code ist kaputt!?
html_code = """
<ul>
    <li fid="4243b1c99" _sp="p2046732.m1686.l1" Id="531698" class="sresult lvresult clearfix li shic" r="2" ></li>
    <li class="lvgrad"><span class="bold"><b>Grad</b>38,49</span></li>
    <li class="lvformat"><span >MEZ</span></li>
    <li class="lvdate"><span class="date">05-02-2016</span></li>     
</ul>
<ul>
    <li fid="4243b1c99" _sp="p2046732.m1686.l1" Id="531699" class="sresult lvresult clearfix li shic" r="2" ></li>
    <li class="lvgrad"><span class="bold"><b>Grad</b>39,51</span></li>
    <li class="lvformat"><span >MEZ</span></li>
    <li class="lvdate"><span class="date">06-02-2016</span></li>     
</ul>"""


soup = BeautifulSoup (html_code, "html.parser")

list_id = []
list_temp = []
list_format = []
list_datum = []

# Daten auslesen
results0 = soup.find_all("li", {"class": "lvresult"})
# print([results0])
for result_back_one in results0:
   zero = result_back_one
   list_id.append(zero.get('id'))


results1 = soup.find_all("li", {"class": "lvgrad"})
# print([results1])
for result_back_one in results1:
   one = result_back_one.get_text().strip() 
   one_new = one.replace('Grad',"")
   list_temp.append(one_new)


results2 = soup.find_all("li", {"class": "lvformat"})
for result_back_two in results2:
   two = result_back_two.get_text().strip() 
   list_format.append(two)     


result3 = soup.find_all("li", {"class": "lvdate"})
for result_back_three in result3:
   three = result_back_three.get_text().strip()
   list_datum.append(three)   
    
# Ausgabe
with open('test.csv', 'w') as csvfile:
    resultwriter = csv.writer(csvfile,
                              delimiter=';',
                              lineterminator='\n',
                              quotechar='|',
                              quoting=csv.QUOTE_MINIMAL)
    resultwriter.writerow(["ID", "Grad", "Format", "Datum"])
    for i, value in enumerate(list_id):
        resultwriter.writerow([value, list_temp[i], list_format[i], list_datum[i]])
BlackJack

Das Problem bei beiden Quelltexten ist das der Programmfluss, und im zweiten Code dann auch die Datenstrukturen, nicht zu den Eingabedaten, dem eigentlichen Problem, und der Ausgabe entsprechen.

Man hat <li>-Elemente die jeweils Daten für einen kompletten Datensatz enthalten und will am Ende komplette Datensätze in eine Datei schreiben. Da ist es nicht sinnvoll alle <li>-Elemente für jedes Datensatzelement erneut von vorne bis hinten durch zu arbeiten, und dabei eine Liste pro Spalte zu erstellen, statt ein mal jedes <li>-Element zu einer Liste mit allen Werten zu verarbeiten. Denn genau in *dem* Format braucht man das ja hinterher auch um die Datensätze der Reihe nach in eine CSV-Datei zu schreiben. Statt das man sich da dann die zusammengehörigen Daten aus verschiedenen Listen wieder zusammenführen muss. (Was im Code auch auf recht „unpythonische“ Weise über einen unnötigen Indexwert passiert, statt die `zip()`-Funktion zu verwenden.)
Stay
User
Beiträge: 4
Registriert: Donnerstag 4. Februar 2016, 23:53

Hi,

nochmal Danke für eure Hilfe.

Ich habe am Ende doch noch das ein oder andere geändert. Ich persönlich habe Probleme mit dem erstellen und verstehen der csv... Deshalb habe ich dann nach langem googeln dafür entschieden das auf excel zu machen.

Code: Alles auswählen

# coding: utf-8
import csv
import xlsxwriter
from bs4 import BeautifulSoup 
from urllib.request import urlopen

urlpfad = ("april-2015")

soup = BeautifulSoup (urlopen("http://www.xyz.de/"+urlpfad.rstrip()+"/m.html"), "html.parser")

list_id = []
list_temp = []
list_format = []
list_datum = []

#start scrapping de

results0 = soup.find_all("li", {"class": "lvresult"})
# print([results0])
for result_back_one in results0:
   zero_de = result_back_one
   list_id.append(zero_de.get('id'))
   #print (list_id_de)   

result1
    ...
    

try:         
    result2 
        ... 
except Exception:
        pass
     
     
result3 
    ... 
 
  
  #Ausgabe xlsx
   
workbook = xlsxwriter.Workbook(urlpfad+'.xlsx')

worksheet = workbook.add_worksheet("Sheet1")


index = 1
for entry in list_id:
    worksheet.write_string(index, 0, entry)
    index = index + 1
index = 1
for entry in list_temp:
    worksheet.write_string(index, 1, entry)
    index = index + 1
index = 1
for entry in list_format:
    worksheet.write_string(index, 2, entry)
    index = index + 1
index = 1
for entry in list_datum:
    worksheet.write_string(index, 3, entry)
    index = index + 1


print(urlpfad+'.xlsx wurde erstellt')  
workbook.close()

In diesem Code verwende ich urlpfad

Jetzt dachte ich mir, ich mache mir eine Textdatei auf dem PC, da stehen Zeilengetrennt die einzelnen "urlpfad". Und lasse das Script für jede Zeile erneut durchlaufen. Allerdings habe ich starke Probleme beim Loop. Alles was ich angefangen habe zu machen, habe ich wieder verworfen da es einfach nicht geklappt hat.


Ich habe angefangen, die Textdatei einzulessen.

Code: Alles auswählen

fileobject = open("test-import.txt")
for urlpfad in fileobject:
        
   print (urlpfad.rstrip())
fileobject.close()

for ergebnis in urlpfad:
    if ergebnis == "EoF":
        break
    print(ergebnis)
else:
    print("Keine weiteren Pfade")
print("Finish")       


Die erste Printausgabe print (urlpfad.rstrip()) hat mir das angezeigt was ich sehen wollte. Genauso wie in der Textdatei Zeilengetrennt die urlpfad

Aber die zweite Printausgabe print(ergebnis) macht nach jedem Buchstaben oder Zahl eine neue Zeile.


Nun die frage, wie bekomme ich auch im zweiten print das sauber angezeigt, so dass ich das dann später in das Hauptscript einbauen kann?
BlackJack

@Stay: Du hast Dir da mit `xlswriter` etwas deutlich komplexeres ins Boot geholt weil Du mit dem deutlich einfacheren `csv`, was hier aber völlig ausreicht, nicht klargekommen bist. Und Du bist sehr wahrscheinlich nicht damit klar gekommen weil der Code das ganze falsch angeht. Die Datensätze sind im HTML zeilenweise organisiert, und CSV ist zeilenweise organisiert, aber Dein Code versucht das unsinnigerweise alles spaltenweise zu verarbeiten. Das führt sowohl zu unnöttig komplexerem Code, als auch zu längerer Laufzeit.

Konkrete Grunddatentypen haben in Namen nichts zu suchen. Es kommt durchaus häufiger mal vor das man den Datentyp ändert und dann entweder irreführende Namen hat oder die überall anpassen muss.

Namen sollte man auch nicht durchnummerieren. Das deutet in der Regel darauf hin, dass man sich nicht genug Gedanken über einen vernünftigen Namen gemacht hat, oder das man keine Einzelnamen haben möchte, sondern eine Datenstruktur. Oft eine Liste. Bei `results1`, `result1`, und so weiter, ist eine Nummerierung einfach nur überflüssig.

`urlpfad` ist *eine Zeile* der Datei, also *eine* Zeichenkette. Wenn die erste Schleife durchgelaufen ist, dann ist das die *letzte* Zeile der Datei.

`ergebnis` ist deshalb immer an *ein* *Zeichen* dieser letzten Zeile gebunden. Das macht keinen Sinn. Du musst *in* der Schleife über das Dateiobjekt etwas mit jedem Wert tun. Und nicht nach der Schleife etwas mit dem letzten Wert.

Insgesamt ist der Code auch viel zu komplex für *eine* Funktion — und auf Modulebene sollte er schon mal gar nicht stehen. Teil das mal sinnvoll auf Funktionen auf. Das Hauptprogramm steht üblicherweise in einer Funktion mit dem Namen `main()`. Ein übliche, grobe Aufteilung ist zwischen Eingabe, Verarbeitung, und Ausgabe von Daten. Und wenn man etwas mit einem „Ding“ anstellt und das in eine Funktion steckt, kann man diese Funktion mehrfach, beispielsweise in einer Schleife, aufrufen, wenn man mehrere dieser „Dinge“ hat, mit denen nahezu das gleiche gemacht werden soll. Das parsen einer Webseite und das schreiben in eine CSV oder Excel-Datei gehören sinnvollerweise in jeweils eigene Funktionen.
Antworten