Benzinpreise von Website lesen

Du hast eine Idee für ein Projekt?
Antworten
koloui
User
Beiträge: 5
Registriert: Samstag 23. Juni 2018, 12:24

Samstag 23. Juni 2018, 12:31

Hallo zusammen,

wie man sieht bin ich neu hier und hoffe daher, dass ich im richtigen Bereich gelandet bin.
Allerdings bin ich nicht nur neu hier, sondern auch neu was Python angeht.

Ich versuche gerade, die unten angezeigten Benzinpreise von folgender Website auszulesen: http://tankstelle.aral.de/tankstelle/wo ... /18060400/ Dies möchte ich dann am liebsten zyklisch von einem Pi aus machen und als CSV speichern. Zu letzterem habe ich denke ich ein ganz gutes Tutorial gefunden, das werde ich wohl hinbekommen, eine Pause einzubauen wohl auch. Aber an der Auswertung der Website scheitert es leider.

Ich habe bereits herausgefunden, dass BeautifulSoup wohl relativ gut dafür geeignet ist, ich stelle mich aber leider zu doof an. Dazu kommt nämlich auch noch, dass ich nicht wirklich Ahnung von HTML und CSS habe. Herausgefunden habe ich, dass die Informationen die ich brauche in der Klasse "ap-fuel" stehen, in den jeweiligen Headern der Name des entsprechenden flüssigen Goldes ist und die verwendeten Grafiken "Alt"-Texte haben, die sicher wunderbar auszuwerten sind. Da hört es aber leider auf und alle meine weiteren Versuche liefen ins leere.

Auf der Suche nach Tutorials habe ich einiges in Richtung "Website scraping" gefunden, aber keines davon geht genug in die Tiefe, um mich wirklich weiter zu bringen.

Ich wäre für jede Hilfe sehr dankbar!

Viele Grüße
Koloui
Benutzeravatar
__blackjack__
User
Beiträge: 1070
Registriert: Samstag 2. Juni 2018, 10:21

Samstag 23. Juni 2018, 14:14

Das ist alles viel einfacher: Die Daten kommen nicht mit der Webseite sondern werden nach dem laden der Webseite per JavaScript als JSON abgefragt. Da kommt man mit `requests` zum Beispiel so heran:

Code: Alles auswählen

In [41]: r = requests.get('http://ap.aral.de/api/v2/getStationPricesById.php', params={'stationId': '18060400'})

In [42]: r.json()
Out[42]: 
{u'response': {u'disabled': u'false',
  u'lastUpdate': u'201806231450',
  u'openNow': u'Wir haben f\xfcr Sie ge\xf6ffnet.',
  u'prices': [{u'currency': u'EUR',
    u'id': u'001131',
    u'name': u'Aral Super E10',
    u'price': u'140,90',
    u'sort': u'21'},
   {u'currency': u'EUR',
    u'id': u'001040',
    u'name': u'Aral Super 95',
    u'price': u'142,90',
    u'sort': u'22'},
   {u'currency': u'EUR',
    u'id': u'001149',
    u'name': u'Aral SuperPlus 98',
    u'price': u'150,90',
    u'sort': u'23'},
   {u'currency': u'EUR',
    u'id': u'004002',
    u'name': u'Aral Diesel',
    u'price': u'126,90',
    u'sort': u'30'},
   {u'currency': u'EUR',
    u'id': u'004010',
    u'name': u'Aral LKW Diesel',
    u'price': u'125,90',
    u'sort': u'32'}],
  u'stationId': u'18060400'}}
“Capitalism is the astounding belief that the most wickedest of men will do the most wickedest of things for the greatest good of everyone.” – John Maynard Keynes
koloui
User
Beiträge: 5
Registriert: Samstag 23. Juni 2018, 12:24

Samstag 23. Juni 2018, 15:17

Fantastisch, danke! Ich werde mal schauen, was ich so zur Weiterverabeitung von JSON finde. Vielleicht melde ich mich auch nochmal...
Benutzeravatar
__blackjack__
User
Beiträge: 1070
Registriert: Samstag 2. Juni 2018, 10:21

Samstag 23. Juni 2018, 16:31

@koloui: Du brauchst nichts zur Weiterverarbeitung von JSON. Du hast doch da schon eine Python-Datenstruktur.
“Capitalism is the astounding belief that the most wickedest of men will do the most wickedest of things for the greatest good of everyone.” – John Maynard Keynes
koloui
User
Beiträge: 5
Registriert: Samstag 23. Juni 2018, 12:24

Samstag 23. Juni 2018, 21:05

Ja, nachdem ich mich dann lange mit den dict-Strukturen rumgeschlagen habe, bis ich herausgefunden habe, dass man ja eigentlich doch recht einfach an die tieferen Ebenen kommt, und das Ganze dann auch irgendwann noch auf meinem seit Jahren nicht mehr benutzten Pi zum Laufen bekommen habe, funktioniert jetzt alles. Vielen Dank nochmal! Ich würde den Code ja eigentlich der Vollständigkeit halber posten, aber in diesem Fall wäre für jeden mit mehr als 2 Stunden Erfahrung sicher kein Lerneffekt gegeben (höchstens ein paar Lacher).
Benutzeravatar
sls
User
Beiträge: 169
Registriert: Mittwoch 13. Mai 2015, 23:52
Wohnort: Tannhauser Gate

Sonntag 24. Juni 2018, 18:38

koloui hat geschrieben:
Samstag 23. Juni 2018, 21:05
Ich würde den Code ja eigentlich der Vollständigkeit halber posten, aber in diesem Fall wäre für jeden mit mehr als 2 Stunden Erfahrung sicher kein Lerneffekt gegeben (höchstens ein paar Lacher).
Wahrscheinlicher ist hier, dass du Feedback erhältst was deinen und anderer Lernprozess beschleunigt.
With great processing power comes great responsibility.
Benutzeravatar
Kebap
User
Beiträge: 401
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

Montag 25. Juni 2018, 14:10

Mich würde der Code ebenfalls interessieren, da ich etwas ähnliches für einen anderen Wohnbereich basteln möchte.

edit: Alternativ baut man auf Andere auf, bspw. existierende Vergleichsportale aus der Markttransparenzstelle für Kraftstoffe
MorgenGrauen: 1 Welt, >12 Gilden, >85 Abenteuer, >1000 Waffen und Rüstungen,
>2500 NPC, >16000 Räume, >170 freiwillige Programmierer, einfach Text, seit 1992.
koloui
User
Beiträge: 5
Registriert: Samstag 23. Juni 2018, 12:24

Montag 25. Juni 2018, 17:14

Kebap hat geschrieben:
Montag 25. Juni 2018, 14:10
Mich würde der Code ebenfalls interessieren, da ich etwas ähnliches für einen anderen Wohnbereich basteln möchte.

edit: Alternativ baut man auf Andere auf, bspw. existierende Vergleichsportale aus der Markttransparenzstelle für Kraftstoffe
Definitiv keine schlechte Idee. Ich habe meinen Code für einen Freund und dessen Tankstelle nochmal angepasst, und da es keine Aral ist läuft das Ganze da dann schon wieder komplett anders (diesmal dann doch mit BeautifulSoup).

Also vorsichtshalber nochmal: Das waren meine allerersten Versuche mit Python.

Bekannte Fehler:
- zu viel direkt im Speicher gearbeitet (ich programmiere normalerweise C, fand ich so einfacher als mich durch die Dokumentationen zu wühlen)
- nicht mehr benötigte Imports nicht aufgeräumt

Aral:

Code: Alles auswählen

import requests
import json
import csv
import sys
import time
from time import gmtime, strftime

while True:
	r = requests.get('http://ap.aral.de/api/v2/getStationPricesById.php', params={'stationId': '18060400'})

	data = r.json()

	resp = data['response']

	updated = resp['lastUpdate']
	prices = resp['prices']

	super95 = prices[1]
	super98 = prices[2]

	super95price = super95['price']
	super98price = super98['price']

	systime = strftime("%Y-%m-%d %H:%M:%S", gmtime())
	outputFile = open("super95.csv", 'a') #load csv file
		
	output = csv.writer(outputFile) #create a csv.write

	output.writerow([systime, updated, super95price])

	outputFile.close()
	
	outputFile = open("super98.csv", 'a') #load csv file
		
	output = csv.writer(outputFile) #create a csv.write

	output.writerow([systime, updated, super98price])

	outputFile.close()
	
	print('polled at ')
	print(systime)
	print('\n')
	
	time.sleep(900)
HEM:

Code: Alles auswählen

import requests
import json
import csv
import sys
import time
import re
from time import gmtime, strftime
from bs4 import BeautifulSoup

while True:
	hemtest = requests.get('https://www.hem-tankstelle.de/partner/HEM/Hockenheim/Heinrich-von-Kleist-Str-16')

	hemdata = BeautifulSoup(hemtest.text,  'html.parser')

	found = hemdata.find(class_='prices-list')

	hemprices = found.contents

	super95 = hemprices[1].contents
	supere10 = hemprices[5].contents

	super95 = super95[1].contents
	supere10 = supere10[1].contents

	super95 = super95[0]
	supere10 = supere10[0]


	super95price = (str(super95))
	supere10price = (str(supere10))


	systime = strftime("%Y-%m-%d %H:%M:%S", gmtime())
	outputFile = open("super95hem.csv", 'a') #load csv file
		
	output = csv.writer(outputFile) #create a csv.write

	output.writerow([systime, super95price])

	outputFile.close()

	outputFile = open("supere10hem.csv", 'a') #load csv file
		
	output = csv.writer(outputFile) #create a csv.write

	output.writerow([systime, supere10price])

	outputFile.close()
	
	print('polled at ')
	print(systime)
	print('\n')
	
	time.sleep(900)
Sirius3
User
Beiträge: 8266
Registriert: Sonntag 21. Oktober 2012, 17:20

Montag 25. Juni 2018, 17:59

@koloui: ich weiß jetzt nicht, was mit "zu viel direkt im Speicher gearbeitet" meint, aber man sollte sich nicht darauf verlassen, dass immer der zweite Eintrag Super95 ist. Du könntest ein paar Funktionen schreiben, auch um doppelten Code zu vermeiden. Je eine Funktion, die die entsprechende Seite nach den Preisen parst und eine, die die csv-Dateien schreibt.

Code: Alles auswählen

import csv
import time
from datetime import datetime as DateTime
import requests

URL_ARAL = 'http://ap.aral.de/api/v2/getStationPricesById.php'
STATION_ARAL = '18060400'

ARAL_NAMES = {
    u'Aral Super 95': 'super95',
    u'Aral SuperPlus 98': 'super98',
}

def fetch_aral():
    r = requests.get(URL_ARAL, params={'stationId': STATION_ARAL})
    data = r.json()
    resp = data['response']
    updated = resp['lastUpdate']
    result = {}
    for entry in resp['prices']:
        if entry['name'] in ARAL_NAMES:
            result[ARAL_NAMES[entry['name']]] = entry['price']
    return result, updated

def append_to_csv(name, price, systime, updated):
    with open(name + '.csv', 'a') as output:
        output = csv.writer(output)
        output.writerow([systime, updated, price])

while True:
    systime = "{:%Y-%m-%d %H:%M:%S}".format(DateTime.utcnow())
    prices, updated = fetch_aral()
    
    for name, price in prices.items():
        append_to_csv(name, price, systime, updated)
    print('polled at ')
    print(systime)
    print('\n')
    time.sleep(900)
Dann kann man das Programm auch leicht um weitere Stationen erweitern.
koloui
User
Beiträge: 5
Registriert: Samstag 23. Juni 2018, 12:24

Montag 25. Juni 2018, 19:00

Sirius3 hat geschrieben:
Montag 25. Juni 2018, 17:59
... aber man sollte sich nicht darauf verlassen, dass immer der zweite Eintrag Super95 ist. ...
Genau das meinte ich damit. Deine Variante ist natürlich ohne Frage die deutlich schönere.
Eine kleine Frage noch dazu: Wieso bist du einen anderen Weg gegangen, um an die Systemzeit gekommen? Was ist der Unterschied? Lässt sich mit deiner Variante evtl. leichter das Problem lösen, dass ich eigentlich gerne UTC+2 geloggt hätte statt UTC (ohne die Systemzeit des Raspi umzustellen :lol: )?
Benutzeravatar
__blackjack__
User
Beiträge: 1070
Registriert: Samstag 2. Juni 2018, 10:21

Montag 25. Juni 2018, 20:14

@koloui: Willst Du UTC+2 oder die lokale Zeit? Wenn lokale Zeit, dann darfst Du halt nicht `gmtime()` oder `utcnow()` verwenden, sondern `localtime()` bzw. `now()`. Andererseits *will* man in der Regel UTC. Für die Anzeige kann man das dann immer noch in lokale Zeit umrechnen. Vorteil von UTC ist etwas jederzeit einfach vergleichbares zu haben, was auch eindeutig ist, weil es zum Beispiel keine Sommer-/Winterzeit-Umstellung bei diesen Zeitangaben gibt.

Der Grund für das `datetime`-Modul ist, das es dort ordentliche Datentypen gibt, statt dem low level `time`-Modul was nur eine dünne Schicht über die C-Funktionen ist.
“Capitalism is the astounding belief that the most wickedest of men will do the most wickedest of things for the greatest good of everyone.” – John Maynard Keynes
DB7WN
User
Beiträge: 20
Registriert: Samstag 18. März 2017, 22:11

Dienstag 17. Juli 2018, 21:53

Bei tankerkönig (https://creativecommons.tankerkoenig.de/) bekommt man direkten Zugriff auf die Daten der Markttransparenzstelle, ohne dass man die unterschiedlichsten individuellen Websites analysieren und auslesen muss.
Antworten