feat: scraper usage example, fixed session handling
This commit is contained in:
@@ -1,7 +1,20 @@
|
|||||||
from lewy_db import baza as ldb
|
from lewy_db import baza as ldb
|
||||||
|
from lewy_globals import colors as c
|
||||||
import json
|
import json
|
||||||
import lewy_globals
|
import lewy_globals
|
||||||
import requests
|
import requests
|
||||||
|
import time
|
||||||
|
|
||||||
|
def safe_traverse(obj: dict, path: list, default=None):
|
||||||
|
result = obj
|
||||||
|
try:
|
||||||
|
for x in path:
|
||||||
|
result = result[x]
|
||||||
|
except KeyError:
|
||||||
|
result = default
|
||||||
|
# print(f"error reading: {' -> '.join(path)} - returning: {default}")
|
||||||
|
finally:
|
||||||
|
return result
|
||||||
|
|
||||||
class scraper:
|
class scraper:
|
||||||
|
|
||||||
@@ -12,7 +25,7 @@ class scraper:
|
|||||||
db = None
|
db = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
db = lewy_globals.getDb()
|
self.db = lewy_globals.getDb()
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def pobierzDaneNajlepszegoSportowcaNaSwiecie(self) -> dict:
|
def pobierzDaneNajlepszegoSportowcaNaSwiecie(self) -> dict:
|
||||||
@@ -25,10 +38,105 @@ class scraper:
|
|||||||
response = requests.get(f'https://3.flashscore.ninja/3/x/feed/plm_{zewnetrzne_id_sportowca}_{nr_strony}', headers=self.headers)
|
response = requests.get(f'https://3.flashscore.ninja/3/x/feed/plm_{zewnetrzne_id_sportowca}_{nr_strony}', headers=self.headers)
|
||||||
return json.loads(response.text)
|
return json.loads(response.text)
|
||||||
|
|
||||||
def czy_mecz_istnieje(self, zewnetrzne_id_meczu: str):
|
def __czy_x_istnieje(self, typ, **id):
|
||||||
mecz = db.simple_select_all(ldb.mecze, zewnetrzne_id_meczu=zewnetrzne_id_meczu).first
|
rekord = self.db.simple_select_all(typ, **id)
|
||||||
if mecz is not None:
|
if rekord is not None and rekord != []:
|
||||||
return mecz
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def czy_mecz_istnieje(self, zewnetrzne_id_meczu: str):
|
||||||
|
# mecz = db.simple_select_all(ldb.mecze, zewnetrzne_id_meczu=zewnetrzne_id_meczu)
|
||||||
|
# if mecz is not None and mecz != []:
|
||||||
|
# return True
|
||||||
|
# else:
|
||||||
|
# return False
|
||||||
|
return self.__czy_x_istnieje("mecze", zewnetrzne_id_meczu=zewnetrzne_id_meczu)
|
||||||
|
|
||||||
|
def czy_klub_istnieje(self, id_klubu: str):
|
||||||
|
# mecz = db.simple_select_all(ldb.mecze, zewnetrzne_id_meczu=zewnetrzne_id_meczu)
|
||||||
|
# if mecz is not None and mecz != []:
|
||||||
|
# return True
|
||||||
|
# else:
|
||||||
|
# return False
|
||||||
|
return self.__czy_x_istnieje("kluby", id_klubu=id_klubu)
|
||||||
|
|
||||||
|
def aktualizuj_dane_sportowca(self, zewnetrzne_id_sportowca: str = "MVC8zHZD"):
|
||||||
|
stop_scraping = False
|
||||||
|
matches_to_add = []
|
||||||
|
|
||||||
|
# TODO: Sprawdź, czy sportowiec istnieje w bazie.
|
||||||
|
# Jeśli nie, dodaj go w podobny sposób, jak
|
||||||
|
# w sample_data_init() (w lewy_db.py).
|
||||||
|
|
||||||
|
page = 0
|
||||||
|
match_num = 0
|
||||||
|
while not stop_scraping:
|
||||||
|
|
||||||
|
retrieved_page = self.pobierz_pojedyncza_strone(zewnetrzne_id_sportowca=zewnetrzne_id_sportowca, nr_strony=page)
|
||||||
|
if not safe_traverse(retrieved_page, ["hasMoreLastMatches"], default=False):
|
||||||
|
stop_scraping = True
|
||||||
|
break
|
||||||
|
|
||||||
|
print(f"{c.WARNING}Pobrano nową stronę {page}{c.ENDC}:\n{retrieved_page}")
|
||||||
|
|
||||||
|
retrieved_matches = safe_traverse(retrieved_page, ["lastMatches"], default=[])
|
||||||
|
for match in retrieved_matches:
|
||||||
|
|
||||||
|
match_id = safe_traverse(match, ["eventEncodedId"], default="non-existent-match-id")
|
||||||
|
home_club_id = safe_traverse(match, ["homeParticipantUrl"], default="non-existent-club-id")
|
||||||
|
away_club_id = safe_traverse(match, ["awayParticipantUrl"], default="non-existent-club-id")
|
||||||
|
|
||||||
|
# Sprawdź, czy mecz nie znajduje się już w bazie
|
||||||
|
if self.czy_mecz_istnieje(zewnetrzne_id_meczu=match_id):
|
||||||
|
stop_scraping = True
|
||||||
|
break
|
||||||
|
|
||||||
|
# Sprawdź, czy klub znajduje się już w bazie. Jeśli nie,
|
||||||
|
# trzeba go dodać przed meczem.
|
||||||
|
if not self.czy_klub_istnieje(id_klubu=home_club_id):
|
||||||
|
print(f"{c.OKCYAN}Nowy klub{c.ENDC}: {home_club_id}")
|
||||||
|
self.db.simple_insert_one("kluby",
|
||||||
|
id_klubu=home_club_id,
|
||||||
|
pelna_nazwa=safe_traverse(match, ["homeParticipantName"]),
|
||||||
|
skrocona_nazwa=safe_traverse(match, ["homeParticipant3CharName"]))
|
||||||
|
if not self.czy_klub_istnieje(id_klubu=away_club_id):
|
||||||
|
print(f"{c.OKCYAN}Nowy klub{c.ENDC}: {away_club_id}")
|
||||||
|
self.db.simple_insert_one("kluby",
|
||||||
|
id_klubu=away_club_id,
|
||||||
|
pelna_nazwa=safe_traverse(match, ["awayParticipantName"]),
|
||||||
|
skrocona_nazwa=safe_traverse(match, ["awayParticipant3CharName"]))
|
||||||
|
|
||||||
|
# TODO: (opcjonalnie) zamień *słownik match* na *obiekt mecz*
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: dodaj obiekt mecz do bazy (simple_insert_one(), simple_insert_many())
|
||||||
|
print(f"{c.OKCYAN}Nowy mecz ({match_num}){c.ENDC}: {match}")
|
||||||
|
match_num += 1
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: Zaktualizuj statystyki sportowca
|
||||||
|
|
||||||
|
|
||||||
|
# Opcjonalnie: odczekaj kilka sekund (?)
|
||||||
|
# Problem w tym, że time.sleep() jest blokujące,
|
||||||
|
# a asyncio i flask nie idą ze sobą w parze.
|
||||||
|
# Można to załatwić osobnym skryptem, ale
|
||||||
|
# martwmy się tym dopiero, gdy dostaniemy
|
||||||
|
# rate limita. - sherl
|
||||||
|
|
||||||
|
page += 1
|
||||||
|
time.sleep(15)
|
||||||
|
|
||||||
|
|
||||||
|
def aktualizuj_dane(self):
|
||||||
|
"""
|
||||||
|
Pobiera mecze dla każdego sportowca wymienionego
|
||||||
|
w pliku konfiguracyjnym.
|
||||||
|
"""
|
||||||
|
|
||||||
|
for id_sportowca in lewy_globals.config['sportsmen']['tracked_ids']:
|
||||||
|
self.aktualizuj_dane_sportowca(zewnetrzne_id_sportowca=id_sportowca)
|
||||||
|
time.sleep(15)
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from flask import Flask, Response, render_template
|
from flask import Flask, Response, render_template
|
||||||
from flask_apscheduler import APScheduler
|
from flask_apscheduler import APScheduler
|
||||||
from fs_scraper import scraper
|
from fs_scraper import scraper as scr
|
||||||
from lewy_globals import colors as c
|
from lewy_globals import colors as c
|
||||||
import lewy_api
|
import lewy_api
|
||||||
import lewy_db
|
import lewy_db
|
||||||
@@ -73,7 +73,7 @@ def setup():
|
|||||||
app.add_url_rule('/api/<path:received_request>', view_func=lewy_api.api_global_catchall)
|
app.add_url_rule('/api/<path:received_request>', view_func=lewy_api.api_global_catchall)
|
||||||
|
|
||||||
db = lewy_globals.setupDb(app, config)
|
db = lewy_globals.setupDb(app, config)
|
||||||
scrape = scraper()
|
scraper = scr()
|
||||||
|
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
db.create_all()
|
db.create_all()
|
||||||
@@ -92,7 +92,7 @@ def every5seconds():
|
|||||||
def every2hours():
|
def every2hours():
|
||||||
# zaktualizuj bazę danych scrapując FS
|
# zaktualizuj bazę danych scrapując FS
|
||||||
# ...
|
# ...
|
||||||
# print(scrape.pobierz_pojedyncza_strone())
|
# scraper.aktualizuj_dane()
|
||||||
return
|
return
|
||||||
|
|
||||||
@app.route('/<string:val>', methods=['GET'])
|
@app.route('/<string:val>', methods=['GET'])
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
# - json with appropriate data
|
# - json with appropriate data
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
|
from fs_scraper import scraper
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from lewy_globals import getDb, colors as c
|
from lewy_globals import getDb, colors as c
|
||||||
import flask, json, time
|
import flask, json, time
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from functools import wraps
|
|||||||
from sqlalchemy import ForeignKey, select, insert, update
|
from sqlalchemy import ForeignKey, select, insert, update
|
||||||
from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase, Session, relationship
|
from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase, Session, relationship
|
||||||
from typing import List
|
from typing import List
|
||||||
|
import time
|
||||||
import toml
|
import toml
|
||||||
|
|
||||||
global db
|
global db
|
||||||
@@ -39,18 +40,18 @@ class baza():
|
|||||||
nazwisko: Mapped[ str] = mapped_column()
|
nazwisko: Mapped[ str] = mapped_column()
|
||||||
data_urodzenia: Mapped[ str] = mapped_column()
|
data_urodzenia: Mapped[ str] = mapped_column()
|
||||||
czy_aktywny: Mapped[ bool] = mapped_column()
|
czy_aktywny: Mapped[ bool] = mapped_column()
|
||||||
klub_id: Mapped[ List[str]] = mapped_column(ForeignKey(f"{tnp}kluby.id_klubu"))
|
klub_id: Mapped[ List[str]] = mapped_column(ForeignKey(f"{tnp}kluby.id_klubu"), nullable=True)
|
||||||
klub: Mapped[ List["kluby"]] = relationship(back_populates="sportowcy_w_klubie", foreign_keys=[klub_id])
|
klub: Mapped[ List["kluby"]] = relationship(back_populates="sportowcy_w_klubie", foreign_keys=[klub_id])
|
||||||
narodowosc: Mapped[ str] = mapped_column()
|
narodowosc: Mapped[ str] = mapped_column()
|
||||||
ilosc_trofeow: Mapped[ int] = mapped_column()
|
ilosc_trofeow: Mapped[ int] = mapped_column()
|
||||||
ostatnie_trofeum_id: Mapped[ int] = mapped_column(ForeignKey(f"{tnp}trofea.id_trofeum"), nullable=True)
|
ostatnie_trofeum_id: Mapped[ int] = mapped_column(ForeignKey(f"{tnp}trofea.id_trofeum"), nullable=True)
|
||||||
ostatnie_trofeum: Mapped[ "trofea"] = relationship(back_populates="zawodnik", foreign_keys=[ostatnie_trofeum_id])
|
ostatnie_trofeum: Mapped[ "trofea"] = relationship(back_populates="zawodnik", foreign_keys=[ostatnie_trofeum_id])
|
||||||
pierwszy_mecz_id: Mapped[ int] = mapped_column(ForeignKey(f"{tnp}mecze.id_meczu"))
|
pierwszy_mecz_id: Mapped[ int] = mapped_column(ForeignKey(f"{tnp}mecze.id_meczu"), nullable=True)
|
||||||
pierwszy_mecz: Mapped[ "mecze"] = relationship()
|
pierwszy_mecz: Mapped[ "mecze"] = relationship()
|
||||||
wycena: Mapped[ int] = mapped_column()
|
wycena: Mapped[ int] = mapped_column()
|
||||||
ostatni_gol_dla_id: Mapped[ str] = mapped_column(ForeignKey(f"{tnp}kluby.id_klubu"))
|
ostatni_gol_dla_id: Mapped[ str] = mapped_column(ForeignKey(f"{tnp}kluby.id_klubu"), nullable=True)
|
||||||
ostatni_gol_dla: Mapped[ "kluby"] = relationship(back_populates="sportowcy_ostatni_gol", foreign_keys=[ostatni_gol_dla_id])
|
ostatni_gol_dla: Mapped[ "kluby"] = relationship(back_populates="sportowcy_ostatni_gol", foreign_keys=[ostatni_gol_dla_id])
|
||||||
statystyki_id: Mapped[ List[int]] = mapped_column(ForeignKey(f"{tnp}statystyki_sportowcow.id_statystyki"))
|
statystyki_id: Mapped[ List[int]] = mapped_column(ForeignKey(f"{tnp}statystyki_sportowcow.id_statystyki"), nullable=True)
|
||||||
statystyki: Mapped[List["statystyki_sportowcow"]] = relationship(back_populates="sportowiec")
|
statystyki: Mapped[List["statystyki_sportowcow"]] = relationship(back_populates="sportowiec")
|
||||||
trofea: Mapped[ List["trofea"]] = relationship(back_populates="zawodnik", foreign_keys="[trofea.id_zawodnika]")
|
trofea: Mapped[ List["trofea"]] = relationship(back_populates="zawodnik", foreign_keys="[trofea.id_zawodnika]")
|
||||||
|
|
||||||
@@ -168,7 +169,7 @@ class baza():
|
|||||||
except:
|
except:
|
||||||
self.session.rollback()
|
self.session.rollback()
|
||||||
self.session.close()
|
self.session.close()
|
||||||
self.refresh_session()
|
self.refresh_session()
|
||||||
return return_val
|
return return_val
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
@@ -186,6 +187,8 @@ class baza():
|
|||||||
if not isinstance(entity_type, str):
|
if not isinstance(entity_type, str):
|
||||||
entity_type = entity_type.__name__
|
entity_type = entity_type.__name__
|
||||||
|
|
||||||
|
print(f"[{round(time.time())}] SELECT")
|
||||||
|
|
||||||
results = (
|
results = (
|
||||||
self.session.
|
self.session.
|
||||||
query(self.entities[entity_type]).
|
query(self.entities[entity_type]).
|
||||||
@@ -193,10 +196,12 @@ class baza():
|
|||||||
all()
|
all()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
print(f"[{round(time.time())}] SELECT RESULTS: {results}")
|
||||||
|
|
||||||
return results
|
return results
|
||||||
|
|
||||||
@exit_gracefully
|
@exit_gracefully
|
||||||
def simple_insert_one(self, type, **kwargs):
|
def simple_insert_one(self, entity_type, **kwargs):
|
||||||
"""
|
"""
|
||||||
Użycie:
|
Użycie:
|
||||||
simple_insert_one(ldb.kluby, id_klubu="polska", pelna_nazwa="Reprezentacja Polski", skrocona_nazwa="PL")
|
simple_insert_one(ldb.kluby, id_klubu="polska", pelna_nazwa="Reprezentacja Polski", skrocona_nazwa="PL")
|
||||||
@@ -204,12 +209,18 @@ class baza():
|
|||||||
https://docs.sqlalchemy.org/en/20/tutorial/data_insert.html
|
https://docs.sqlalchemy.org/en/20/tutorial/data_insert.html
|
||||||
https://docs.sqlalchemy.org/en/20/orm/session_basics.html
|
https://docs.sqlalchemy.org/en/20/orm/session_basics.html
|
||||||
"""
|
"""
|
||||||
obj = type(**kwargs)
|
|
||||||
with Session(self.db.engine) as session:
|
if not isinstance(entity_type, str):
|
||||||
session.add(obj)
|
entity_type = entity_type.__name__
|
||||||
session.commit()
|
|
||||||
return 0
|
print(f"[{round(time.time())}] INSERT")
|
||||||
return 1
|
|
||||||
|
obj = self.entities[entity_type](**kwargs)
|
||||||
|
#with Session(self.db.engine) as session:
|
||||||
|
self.session.add(obj)
|
||||||
|
self.session.commit()
|
||||||
|
return 0
|
||||||
|
#return 1
|
||||||
|
|
||||||
@exit_gracefully
|
@exit_gracefully
|
||||||
def simple_insert_many(self, objs_list):
|
def simple_insert_many(self, objs_list):
|
||||||
@@ -220,11 +231,11 @@ class baza():
|
|||||||
https://docs.sqlalchemy.org/en/20/tutorial/data_insert.html
|
https://docs.sqlalchemy.org/en/20/tutorial/data_insert.html
|
||||||
https://docs.sqlalchemy.org/en/20/orm/session_basics.html
|
https://docs.sqlalchemy.org/en/20/orm/session_basics.html
|
||||||
"""
|
"""
|
||||||
with Session(self.db.engine) as session:
|
#with Session(self.db.engine) as session:
|
||||||
session.add_all(objs_list)
|
self.session.add_all(objs_list)
|
||||||
session.commit()
|
self.session.commit()
|
||||||
return 0
|
return 0
|
||||||
return 1
|
#return 1
|
||||||
|
|
||||||
@exit_gracefully
|
@exit_gracefully
|
||||||
def sample_data_init(self, override_safety_check=False):
|
def sample_data_init(self, override_safety_check=False):
|
||||||
|
|||||||
Reference in New Issue
Block a user