This commit is contained in:
2026-07-21 03:49:47 +03:00
commit e8a0b2ba42
65 changed files with 40163 additions and 0 deletions

527
parser_test.go Executable file
View File

@@ -0,0 +1,527 @@
package flashscore
import (
"bytes"
"fmt"
"os"
"strings"
"github.com/PuerkitoBio/goquery"
// xhtml "golang.org/x/net/html"
//"strings"
"encoding/json"
"testing"
)
/*
func TestParseFootballZoneAndChamp(t *testing.T) {
var src = []byte(`<span class="description__country">ENGLAND - SERIA A - DIVISION 1: <a href="#" onclick="window.open('/football/england/premier-league/'); return false;">Premier League - Round 30</a></span>
<div class="team-text tname-home">
<div class="tname">
<div class="tname__text">
<span style="display: none" class="dw-icon ico">&nbsp;</span>
<a href="#" class="participant-imglink" onclick="window.open('/team/newcastle-utd/p6ahwuwJ'); return false;">Newcastle &amp; Co</a>
</div>
</div>
</div>
<div class="team-text tname-away">
<div class="tname">
<div class="tname__text">
<span style="display: none" class="dw-icon ico">&nbsp;</span>
<a href="#" class="participant-imglink" onclick="window.open('/team/sheffield-utd/MeKPSerA'); return false;">Sheffield Utd (Eng)</a>
</div>
</div>
</div>`)
doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(src))
if err != nil {
t.Fatal(err)
}
zone, champ, home, away, err := parseTeamMatchInfo(doc, Football)
if err != nil {
t.Fatal(err)
}
fmt.Printf("zone: %s\n", zone.Name)
fmt.Printf("zoneID: %s\n\n", zone.ZoneID)
fmt.Printf("champ.ChampID: %s\n", champ.ChampID)
fmt.Printf("champ.Name: %v\n", champ.Name)
fmt.Printf("champ.ZoneID: %v\n\n", champ.ZoneID)
fmt.Printf("home.CanonicalName: %s\n", home.CanonicalName)
fmt.Printf("home.TeamID: %s\n", home.TeamID)
fmt.Printf("home.ZoneID: %s\n\n", home.ZoneID)
fmt.Printf("away.CanonicalName: %s\n", away.CanonicalName)
fmt.Printf("away.TeamID: %s\n", away.TeamID)
fmt.Printf("away.ZoneID: %s\n", away.ZoneID)
}
*/
//var reChampName = regexp.MustCompile(`([^])`)
func TestTeamZone(t *testing.T) {
var teamZone = []byte(`<h2 class="tournament">
<div class="sportIcon sportIcon--soccer"></div>
<a href="/football/">Football</a>
<span class="breadcrumb__arrow"></span>
<span class="flag fl_198"></span>
<a href="/football/england/">England</a>
</h2>`)
doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(teamZone))
if err != nil {
t.Fatal(err)
}
zone, err := parseZoneOnTeamPage(doc, Football)
if err != nil {
t.Fatal(err)
}
fmt.Printf("zone.ZoneID: %s\nzone.Name: %s\n", zone.ZoneID, zone.Name)
}
func TestFullTimeTotalOdds(t *testing.T) {
var src = []byte(`<div id="block-under-over-ft" class="odds-table-wrapper" style="display: block;">
<table id="odds_ou_0.5" class="odds sortable" cellspacing="0">
<colgroup>
<col/>
<col width="60"/>
<col width="60"/>
<col width="60"/>
</colgroup>
<thead>
<tr>
<th class="bookmaker no-padding unsortable">Bookmaker</th>
<th class="unsortable">Total</th>
<th>Over</th>
<th>Under</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td class="bookmaker">
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_bookmaker');"><a href="/bookmaker/417/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="1xBet" class="elink"><span style="background-image:url(/res/image/data/bookmakers/30-417.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span></a></div>
<div title="This match will be available for LIVE betting!" onmouseover="tt.show(this, event)" onmouseout="tt.hide(this)" class="live-icon-bookmaker" onclick="window.open('/bookmaker/417/?sport=1&from=live-detail' + (window.fs_detail && fs_detail.DA ? (fs_detail.DA == cjs.Api.constantsManager.getEventStageTypes().LIVE ? '-live-match' : '') : ''));"><span class="icon live-offer1" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_live-offer');">&nbsp;</span></div>
</td>
<td>0.5</td>
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_over');"><span class=" odds-wrap up" alt="1.07[u]1.09:">1.09</span></td>
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_under');"><span class=" odds-wrap down" alt="7.00[d]5.90:">5.90</span></td>
</tr>
<tr class="even">
<td class="bookmaker">
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_bookmaker');"><a href="/bookmaker/16/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="bet365" class="elink"><span style="background-image:url(/res/image/data/bookmakers/30-16.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span></a></div>
<div title="This match will be available for LIVE betting!" onmouseover="tt.show(this, event)" onmouseout="tt.hide(this)" class="live-icon-bookmaker" onclick="window.open('/bookmaker/16/?sport=1&from=live-detail' + (window.fs_detail && fs_detail.DA ? (fs_detail.DA == cjs.Api.constantsManager.getEventStageTypes().LIVE ? '-live-match' : '') : ''));"><span class="icon live-offer1" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_live-offer');">&nbsp;</span></div>
</td>
<td>0.5</td>
<td class="kx max" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_over');"><span class=" odds-wrap up" alt="1.10[u]1.11:">1.11</span></td>
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_under');"><span class=" odds-wrap down" alt="7.00[d]6.50:">6.50</span></td>
</tr>
</tbody>
</table>
<table id="odds_ou_2.5" class="odds sortable" cellspacing="0">
<colgroup>
<col/>
<col width="60"/>
<col width="60"/>
<col width="60"/>
</colgroup>
<thead>
<tr>
<th class="bookmaker no-padding unsortable">Bookmaker</th>
<th class="unsortable">Total</th>
<th>Over</th>
<th>Under</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td class="bookmaker">
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_bookmaker');"><a href="/bookmaker/417/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="1xBet" class="elink"><span style="background-image:url(/res/image/data/bookmakers/30-417.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span></a></div>
<div title="This match will be available for LIVE betting!" onmouseover="tt.show(this, event)" onmouseout="tt.hide(this)" class="live-icon-bookmaker" onclick="window.open('/bookmaker/417/?sport=1&from=live-detail' + (window.fs_detail && fs_detail.DA ? (fs_detail.DA == cjs.Api.constantsManager.getEventStageTypes().LIVE ? '-live-match' : '') : ''));"><span class="icon live-offer1" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_live-offer');">&nbsp;</span></div>
</td>
<td>2.5</td>
<td class="kx max" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_over');"><span class=" odds-wrap up" alt="2.41[u]2.91:">2.91</span></td>
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_under');"><span class=" odds-wrap down" alt="1.54[d]1.52:">1.52</span></td>
</tr>
<tr class="even">
<td class="bookmaker">
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_bookmaker');"><a href="/bookmaker/16/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="bet365" class="elink"><span style="background-image:url(/res/image/data/bookmakers/30-16.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span></a></div>
<div title="This match will be available for LIVE betting!" onmouseover="tt.show(this, event)" onmouseout="tt.hide(this)" class="live-icon-bookmaker" onclick="window.open('/bookmaker/16/?sport=1&from=live-detail' + (window.fs_detail && fs_detail.DA ? (fs_detail.DA == cjs.Api.constantsManager.getEventStageTypes().LIVE ? '-live-match' : '') : ''));"><span class="icon live-offer1" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_live-offer');">&nbsp;</span></div>
</td>
<td>2.5</td>
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_over');"><span class=" odds-wrap up" alt="2.50[u]2.75:">2.75</span></td>
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_under');"><span class=" odds-wrap down" alt="1.53[d]1.44:">1.44</span></td>
</tr>
<tr class="odd">
<td class="bookmaker">
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_bookmaker');"><a href="/bookmaker/15/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="William Hill" class="elink"><span style="background-image:url(/res/image/data/bookmakers/30-15.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span></a></div>
</td>
<td>2.5</td>
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_over');"><span class="not-published odds-wrap " alt=":">2.38</span></td>
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-under-over_ft_under');"><span class="not-published odds-wrap " alt=":">1.53</span></td>
</tr>
</tbody>
</table>
</div>`)
doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(src))
if err != nil {
t.Fatal(err)
}
odds, err := parseOddsTotal(doc)
if err != nil {
t.Fatal(err)
}
buf, _ := json.MarshalIndent(odds, "", " ")
fmt.Printf("Total odds: %s\n", buf)
}
// <table id="odds_1x2"
func TestParse1x2Odds(t *testing.T) {
var oddssrc = []byte(`<table id="odds_1x2"><tr class="odd">
<td class="bookmaker">
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_bookmaker');">
<a href="/bookmaker/417/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="1xBet" class="elink">
<span style="background-image:url(/res/image/data/bookmakers/30-417.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span>
</a>
</div>
<div title="This match will be available for LIVE betting!" onmouseover="tt.show(this, event)" onmouseout="tt.hide(this)" class="live-icon-bookmaker" onclick="window.open('/bookmaker/417/?sport=1&from=live-detail' + (window.fs_detail && fs_detail.DA ? (fs_detail.DA == cjs.Api.constantsManager.getEventStageTypes().LIVE ? '-live-match' : '') : ''));">
<span class="icon live-offer1" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_live-offer');">&nbsp;</span>
</div>
</td>
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_1');">
<span class=" odds-wrap up" alt="3.01[u]3.24:">3.24</span>
</td>
<td class="kx max" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_0');">
<span class=" odds-wrap down" alt="3.56[d]3.50:">3.50</span>
</td>
<td class="kx max" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_2');">
<span class=" odds-wrap down" alt="2.49[d]2.37:">2.37</span>
</td>
</tr>
<tr class="even">
<td class="bookmaker">
<div class="blink" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_bookmaker');">
<a href="/bookmaker/17/?sport=1&from=odds-comparison" onclick="return !window.open(this.href)" title="1xBet" class="elink">
<span style="background-image:url(/res/image/data/bookmakers/30-417.png); background-size: 100%; background-repeat: no-repeat;" class="detail-blogos"></span>
</a>
</div>
<div title="This match will be available for LIVE betting!" onmouseover="tt.show(this, event)" onmouseout="tt.hide(this)" class="live-icon-bookmaker" onclick="window.open('/bookmaker/417/?sport=1&from=live-detail' + (window.fs_detail && fs_detail.DA ? (fs_detail.DA == cjs.Api.constantsManager.getEventStageTypes().LIVE ? '-live-match' : '') : ''));">
<span class="icon live-offer1" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_live-offer');">&nbsp;</span>
</div>
</td>
<td class="kx" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_1');">
<span class=" odds-wrap up" alt="3.01[u]3.24:">2.24</span>
</td>
<td class="kx max" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_0');">
<span class=" odds-wrap down" alt="3.56[d]3.50:">5.50</span>
</td>
<td class="kx max" onclick="e_t.track_click('bookmaker-button-click', 'block-1x2_ft_2');">
<span class=" odds-wrap down" alt="2.49[d]2.37:">5.37</span>
</td>
</tr>
</table>`)
doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(oddssrc))
if err != nil {
t.Fatal(err)
}
table1x2Odds := doc.Find("#odds_1x2")
offers, err := parseOdds1x2(table1x2Odds)
if err != nil {
t.Fatal(err)
}
buf, _ := json.MarshalIndent(offers, "", " ")
fmt.Printf("%s\n", buf)
}
func TestParseNextRoundFixtures(t *testing.T) {
/*
var handballSchedule = ``
Postponed: (разные чемпионаты)
~AA÷6R6Qjxol
¬AD÷1602514800
¬ADE÷1602514800
¬AB÷3
¬CR÷3
¬AC÷4
¬CX÷Butel Skopje
¬RW÷0
¬AX÷0
¬BX÷-1
¬WQ÷
¬WN÷TIK
¬AF÷Tikvesh
¬JB÷M10TCQre
¬WV÷tikvesh
¬WM÷BUT
¬AE÷Butel Skopje
¬JA÷rB1PD6Tl
¬WU÷butel-skopje¬
~AA÷CzK7rvtg
¬AD÷1602531000
¬ADE÷1602531000
¬AB÷3
¬CR÷3
¬AC÷4
¬CX÷FH Hafnarfjordur
¬RW÷0
¬AX÷0
¬BX÷-1
¬WQ÷
¬WN÷GRO
¬AF÷Grotta
¬JB÷06YZR1Pr
¬WV÷grotta
¬WM÷FHH
¬AE÷FH Hafnarfjordur
¬JA÷bcn8WsHR
¬WU÷fh-hafnarfjordur
¬AM÷Postponed due to safety reasons.¬
Normal:
~AA÷MyARroUi
¬AD÷1602520200
¬ADE÷1602520200
¬AB÷1
¬CR÷1
¬AC÷1
¬CX÷Ribe-Esbjerg
¬RW÷0
¬AX÷0
¬BX÷-1
¬WQ÷
¬WM÷RIB
¬AE÷Ribe-Esbjerg
¬JA÷xrSUrVlt
¬WU÷ribe-esbjerg
¬WN÷FRE
¬AF÷Fredericia
¬JB÷lIVYsk4n
¬WV÷fredericia
¬AN÷n
¬MW÷417|453|455|512|471¬
~AA÷xMZpVA2J
¬AD÷1602522000
¬ADE÷1602522000
¬AB÷1
¬CR÷1
¬AC÷1
¬CX÷Redbergslids
¬RW÷0
¬AX÷0
¬BX÷-1
¬WQ÷
¬WN÷SKO
¬AF÷Skovde
¬JB÷AyzHGCHi
¬WV÷skovde
¬WM÷RED
¬AE÷Redbergslids
¬JA÷dYVCHh2o
¬WU÷redbergslids
¬AL÷{"2":[{"BU":"\/bookmaker\/16\/?from=live-streaming&sport=0","IU":"\/res\/image\/data\/bookmakers\/17-16.png","BN":"bet365","BI":16,"BT":"GEO restrictions apply. Funded account required or to have placed a bet in the last 24 hours. GamblingTherapy.org. 18+"},{"BU":"\/bookmaker\/539\/?from=live-streaming&sport=0","IU":"\/res\/image\/data\/bookmakers\/17-539.png","BN":"Betclic.pl","BI":539},{"BU":"\/bookmaker\/45\/?from=live-streaming&sport=0","IU":"\/res\/image\/data\/bookmakers\/17-45.png","BN":"Chance","BI":45},{"BU":"\/bookmaker\/165\/?from=live-streaming&sport=0","IU":"\/res\/image\/data\/bookmakers\/17-165.png","BN":"STS.pl","BI":165},{"BU":"\/bookmaker\/49\/?from=live-streaming&sport=0","IU":"\/res\/image\/data\/bookmakers\/17-49.1.png","BN":"Tipsport","BI":49},{"BU":"\/bookmaker\/411\/?from=live-streaming&sport=0","IU":"\/res\/image\/data\/bookmakers\/17-411.1.png","BN":"Tipsport SK","BI":411},{"BU":"\/bookmaker\/454\/?from=live-streaming&sport=0","IU":"\/res\/image\/data\/bookmakers\/17-454.png","BN":"Winlinebet","BI":454},{"BU":"\/bookmaker\/523\/?from=live-streaming&sport=0","IU":"\/res\/image\/data\/bookmakers\/17-523.png","BN":"Wplay","BI":523}]}
¬AN÷y
¬MW÷16|417|392|419|453|454|455|49|45|33|165|539|544|38|512|508|24|27|471|411|43|507|549¬
res, err := parseNextRoundFixtures(handballSchedule)
if err != nil {
t.Fatal(err)
}
for _, m := range res.Matches {
fmt.Printf("%s: %s\n", m.StartTime, m.MatchID)
}
*/
}
/*
func TestListScheduledHandballMatches(t *testing.T) {
result, err := listScheduledHandballMatches()
if err != nil {
t.Fatal(err)
}
for _, m := range result {
fmt.Printf("%s: %s\n", m.StartTime, m.MatchID)
}
}
func TestGetUpcomingHandballMatchReport(t *testing.T) {
report, err := GetUpcomingMatchReport("MyARroUi")
if err != nil {
t.Fatal(err)
}
buf, _ := json.MarshalIndent(report, "", " ")
fmt.Printf("%s\n\n", buf)
//for _, m := range result {
// /fmt.Printf("%s: %s\n", time.Unix(m.StartTime, 0), m.MatchID)
//
}
*/
// HANDBALL
func TestParseHandballOdds(t *testing.T) {
// Строку возвращает отдельный запрос на адрес handballOddsURL
// flashscore format
buf, err := os.ReadFile("for_parse/oddsHandball.fsf")
if err != nil {
t.Fatal(err)
}
str := string(buf)
odds, err := parseFlashscoreEncodedOdds(str)
if err != nil {
t.Fatal(err)
}
buf, _ = json.MarshalIndent(odds, "", " ")
fmt.Printf("%s\n\n", buf)
}
func TestParseHandballH2H(t *testing.T) {
// flashscore format
buf, err := os.ReadFile("for_parse/h2hHandball.fsf")
if err != nil {
t.Fatal(err)
}
str := string(buf)
homeTeamName := "HSG Wetzlar"
awayTeamName := "MT Melsungen"
homeTeamMatches, awayTeamMatches, h2hMatches, err := parseFlashscoreEncodedH2H(str, homeTeamName, awayTeamName)
if err != nil {
t.Fatal(err)
}
buf, _ = json.MarshalIndent(homeTeamMatches, "", " ")
fmt.Printf("HomeTeamMatches:\n%s\n\n", buf)
buf, _ = json.MarshalIndent(awayTeamMatches, "", " ")
fmt.Printf("AwayTeamMatches:\n%s\n\n", buf)
buf, _ = json.MarshalIndent(h2hMatches, "", " ")
fmt.Printf("h2hMatches:\n%s\n\n", buf)
}
func TestParseAllGames(t *testing.T) {
// flashscore format
buf, err := os.ReadFile("for_parse/allGamesFootball.fsf")
//buf, err := os.ReadFile("for_parse/allGamesHandball.fsf")
if err != nil {
t.Fatal(err)
}
str := string(buf)
/*
list, err := ParseFlashscoreEncoding(str)
if err != nil {
t.Fatal(err)
}
for _,m := range list {
for k, v := range m {
fmt.Printf("%s: %s\n", k, v)
}
fmt.Println()
}
*/
matches, err := parseFlashscoreEncodedAllGames(str, Football)
//matches, err := parseFlashscoreEncodedAllGames(str, Handball)
if err != nil {
t.Fatal(err)
}
buf, _ = json.MarshalIndent(matches, "", " ")
fmt.Printf("%s\n\n", buf)
}
func TestParseChampNameOnChampPage(t *testing.T) {
buf := []byte(`<h2 class="tournament"><div class="sportIcon sportIcon--soccer"></div><a href="/football/">Football</a> <span class="breadcrumb__arrow"></span><span class="flag fl_81"></span><a href="/football/germany/">Germany</a></h2>
<div class="container__fsbody" id="fsbody"> <div id="fscountry">
<div id="fsi"></div>
<div id="fscon">
<div class="teamHeader">
<div class="teamHeader__info">
<div class="teamHeader__logo" style="background-image: url('/res/image/data/Q1BvWoB7-KfVhu7bI.png')"></div>
<div class="teamHeader__information">
<div class="teamHeader__name">Bundesliga
<div id="tomyleagues"></div>
</div>
<div class="teamHeader__name">2020/2021</div>
</div>
</div>
</div>`)
doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(buf))
if err != nil {
t.Fatal(err)
}
// First -
f := doc.Find("div.teamHeader__name").First() //.Children()
txt := f.Text()
txt = strings.TrimSpace(txt)
champID := "/football/germany/bundesliga"
parts := strings.Split(champID, "/")
zoneID := strings.Join(parts[:3], "/")
fmt.Printf("%q\n\n%q\n\n", txt, zoneID)
//f := doc.Find("h2.tournament").First()
}
func TestParseEnvironmentJsVarOnMatchPageInJsVar(t *testing.T) {
// flashscore format
buf, err := os.ReadFile("for_parse/environmentJsVarOnMatchPage.html")
if err != nil {
t.Fatal(err)
}
doc, err := goquery.NewDocumentFromReader(bytes.NewBuffer(buf))
if err != nil {
t.Fatal(err)
}
home, away, err := parseTeamsOnMatchPageInJsVar(doc)
if err != nil {
t.Fatal(err)
}
buf, _ = json.MarshalIndent(home, "", " ")
fmt.Printf("%s\n\n", buf)
buf, _ = json.MarshalIndent(away, "", " ")
fmt.Printf("%s\n\n", buf)
}