mirror of
https://github.com/tiennm99/loldle-data.git
synced 2026-08-08 22:25:24 +00:00
fix: use MediaWiki API for wiki scraping to bypass Cloudflare
Direct wiki page requests return 403 due to Cloudflare JS challenge. Switch to MediaWiki parse API which returns rendered HTML without blocking. Also match img[alt='Official'] in lane detection.
This commit is contained in:
+511
-171
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,44 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
// wikiAPIResponse represents the MediaWiki parse API response.
|
||||
type wikiAPIResponse struct {
|
||||
Parse struct {
|
||||
Text struct {
|
||||
Content string `json:"*"`
|
||||
} `json:"text"`
|
||||
} `json:"parse"`
|
||||
}
|
||||
|
||||
// fetchWikiDoc fetches a Fandom wiki page via the MediaWiki parse API (bypasses Cloudflare)
|
||||
// and returns a goquery document of the rendered HTML.
|
||||
func fetchWikiDoc(pageName string) (*goquery.Document, error) {
|
||||
url := fmt.Sprintf(
|
||||
"https://leagueoflegends.fandom.com/api.php?action=parse&page=%s&prop=text&format=json",
|
||||
pageName,
|
||||
)
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fetching wiki API for %s: %w", pageName, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var apiResp wikiAPIResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&apiResp); err != nil {
|
||||
return nil, fmt.Errorf("decoding wiki API response: %w", err)
|
||||
}
|
||||
|
||||
return goquery.NewDocumentFromReader(strings.NewReader(apiResp.Parse.Text.Content))
|
||||
}
|
||||
|
||||
// buildNameIndex creates a map from champion name to slice index for O(1) lookups.
|
||||
func buildNameIndex(champions []ChampionResult) map[string]int {
|
||||
idx := make(map[string]int, len(champions))
|
||||
|
||||
+5
-13
@@ -2,14 +2,11 @@ package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
const wikiDraftPositionURL = "https://leagueoflegends.fandom.com/wiki/List_of_champions_by_draft_position"
|
||||
|
||||
// columnLaneMap maps 1-indexed column positions to lane names.
|
||||
var columnLaneMap = map[int]string{
|
||||
1: "top",
|
||||
@@ -19,18 +16,12 @@ var columnLaneMap = map[int]string{
|
||||
5: "support",
|
||||
}
|
||||
|
||||
// EnrichLanes scrapes the LoL Wiki draft position table to add lane data.
|
||||
// EnrichLanes fetches the LoL Wiki draft position table to add lane data.
|
||||
func EnrichLanes(champions []ChampionResult) ([]ChampionResult, error) {
|
||||
resp, err := http.Get(wikiDraftPositionURL)
|
||||
doc, err := fetchWikiDoc("List_of_champions_by_draft_position")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fetching wiki draft positions: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
doc, err := goquery.NewDocumentFromReader(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing wiki HTML: %w", err)
|
||||
}
|
||||
|
||||
nameIndex := buildNameIndex(champions)
|
||||
|
||||
@@ -42,8 +33,9 @@ func EnrichLanes(champions []ChampionResult) ([]ChampionResult, error) {
|
||||
}
|
||||
|
||||
cols.Each(func(colIdx int, col *goquery.Selection) {
|
||||
// Check if this column has a "Yes" indicator image.
|
||||
if col.Find("img[alt='Yes']").Length() == 0 {
|
||||
// Check if this column has a lane indicator image (alt="Yes" or alt="Official").
|
||||
hasIndicator := col.Find("img[alt='Yes'], img[alt='Official']").Length() > 0
|
||||
if !hasIndicator {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+15
-17
@@ -20,14 +20,23 @@ func TestDdragonAPI(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestWikiDraftPosition(t *testing.T) {
|
||||
resp, err := http.Get(wikiDraftPositionURL)
|
||||
func TestWikiAPIChampionList(t *testing.T) {
|
||||
doc, err := fetchWikiDoc("List_of_champions")
|
||||
if err != nil {
|
||||
t.Fatalf("request failed: %v", err)
|
||||
t.Fatalf("fetch failed: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", resp.StatusCode)
|
||||
if doc.Find("tbody").Length() == 0 {
|
||||
t.Fatal("expected at least one tbody in champion list")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWikiAPIDraftPosition(t *testing.T) {
|
||||
doc, err := fetchWikiDoc("List_of_champions_by_draft_position")
|
||||
if err != nil {
|
||||
t.Fatalf("fetch failed: %v", err)
|
||||
}
|
||||
if doc.Find("tbody").Length() < 2 {
|
||||
t.Fatal("expected at least two tbody in draft position page")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,14 +55,3 @@ func TestRegionAPI(t *testing.T) {
|
||||
t.Fatalf("expected application/json, got %s", ct)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWikiChampionList(t *testing.T) {
|
||||
resp, err := http.Get(wikiChampionListURL)
|
||||
if err != nil {
|
||||
t.Fatalf("request failed: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-11
@@ -2,27 +2,18 @@ package parser
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
)
|
||||
|
||||
const wikiChampionListURL = "https://leagueoflegends.fandom.com/wiki/List_of_champions"
|
||||
|
||||
// EnrichReleaseDates scrapes the LoL Wiki to add release year to each champion.
|
||||
// EnrichReleaseDates fetches the LoL Wiki to add release year to each champion.
|
||||
func EnrichReleaseDates(champions []ChampionResult) ([]ChampionResult, error) {
|
||||
resp, err := http.Get(wikiChampionListURL)
|
||||
doc, err := fetchWikiDoc("List_of_champions")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fetching wiki champion list: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
doc, err := goquery.NewDocumentFromReader(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parsing wiki HTML: %w", err)
|
||||
}
|
||||
|
||||
// Build a name-to-index map for O(1) lookups.
|
||||
nameIndex := buildNameIndex(champions)
|
||||
|
||||
Reference in New Issue
Block a user