vlr-stats-scraper 1.4.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +326 -170
- package/index.mjs +20 -17
- package/main.mjs +122 -47
- package/package.json +1 -1
- package/scripts/db.mjs +130 -102
- package/scripts/player-info-scraper.mjs +126 -127
- package/scripts/player-link-scraper.mjs +36 -9
|
@@ -1,128 +1,127 @@
|
|
|
1
|
-
import * as cheerio from 'cheerio'
|
|
2
|
-
import { Hyperbrowser } from "@hyperbrowser/sdk"
|
|
3
|
-
import { config } from "dotenv"
|
|
4
|
-
|
|
5
|
-
config()
|
|
6
|
-
|
|
7
|
-
let client;
|
|
8
|
-
function getClient() {
|
|
9
|
-
if (!client) {
|
|
10
|
-
client = new Hyperbrowser({
|
|
11
|
-
apiKey: process.env.HYPERBROWSER_API_KEY,
|
|
12
|
-
})
|
|
13
|
-
}
|
|
14
|
-
return client;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
async function scrapePlayer(url, region) {
|
|
18
|
-
// console.log('scrapePlayer ran')
|
|
19
|
-
try {
|
|
20
|
-
const scrapeData = await getClient().scrape.startAndWait({
|
|
21
|
-
url: url,
|
|
22
|
-
scrapeOptions: {
|
|
23
|
-
formats: ["html"]
|
|
24
|
-
}
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
const $ = cheerio.load(scrapeData.data.html)
|
|
28
|
-
const agentsPlayed = findAgents($)
|
|
29
|
-
|
|
30
|
-
return {
|
|
31
|
-
name: getPlayerName(url),
|
|
32
|
-
team: getCurTeam($),
|
|
33
|
-
past_teams: getPastTeam($),
|
|
34
|
-
winnings: getWinnings($),
|
|
35
|
-
most_played_agents: agentsPlayed.map(data => data.Agent),
|
|
36
|
-
avg_kda: agentsPlayed.reduce((acc, data) => acc + (parseFloat(data['K:D'])) / agentsPlayed.length, 0).toFixed(2),
|
|
37
|
-
nationality: getNationality($),
|
|
38
|
-
region: region,
|
|
39
|
-
stats: agentsPlayed
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
catch (error) {
|
|
43
|
-
console.error("Error scraping website:", error);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// testing
|
|
48
|
-
/*
|
|
49
|
-
node scripts/player-info-scraper.mjs
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
&& !ele.includes('
|
|
105
|
-
&& !ele.includes('
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
// console.log(
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
1
|
+
import * as cheerio from 'cheerio'
|
|
2
|
+
import { Hyperbrowser } from "@hyperbrowser/sdk"
|
|
3
|
+
import { config } from "dotenv"
|
|
4
|
+
|
|
5
|
+
config()
|
|
6
|
+
|
|
7
|
+
let client;
|
|
8
|
+
function getClient() {
|
|
9
|
+
if (!client) {
|
|
10
|
+
client = new Hyperbrowser({
|
|
11
|
+
apiKey: process.env.HYPERBROWSER_API_KEY,
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
return client;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function scrapePlayer(url, region) {
|
|
18
|
+
// console.log('scrapePlayer ran')
|
|
19
|
+
try {
|
|
20
|
+
const scrapeData = await getClient().scrape.startAndWait({
|
|
21
|
+
url: url,
|
|
22
|
+
scrapeOptions: {
|
|
23
|
+
formats: ["html"]
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const $ = cheerio.load(scrapeData.data.html)
|
|
28
|
+
const agentsPlayed = findAgents($)
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
name: getPlayerName(url),
|
|
32
|
+
team: getCurTeam($),
|
|
33
|
+
past_teams: getPastTeam($),
|
|
34
|
+
winnings: getWinnings($),
|
|
35
|
+
most_played_agents: agentsPlayed.map(data => data.Agent),
|
|
36
|
+
avg_kda: agentsPlayed.reduce((acc, data) => acc + (parseFloat(data['K:D'])) / agentsPlayed.length, 0).toFixed(2),
|
|
37
|
+
nationality: getNationality($),
|
|
38
|
+
region: region,
|
|
39
|
+
stats: agentsPlayed
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
console.error("Error scraping website:", error);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// testing
|
|
48
|
+
/*
|
|
49
|
+
node scripts/player-info-scraper.mjs
|
|
50
|
+
scrapePlayer("https://www.vlr.gg/player/939/mwzera/?timespan=all", "AMER")
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
function getPlayerName(link) {
|
|
55
|
+
let name = new URL(link).pathname.split('/').filter(ele => ele !== '').pop()
|
|
56
|
+
return name
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function findAgents($) {
|
|
60
|
+
// console.log('findAgents ran')
|
|
61
|
+
const header = $('.mod-agent-rows').first().children().first()
|
|
62
|
+
|
|
63
|
+
const statCategories = header.text().split('\n').map(ele => ele.trim()).filter(ele => ele !== '')
|
|
64
|
+
statCategories.unshift('Agent')
|
|
65
|
+
|
|
66
|
+
const agentStats = header.next().children()
|
|
67
|
+
const agents = agentStats.map((i, el) => {
|
|
68
|
+
el = $(el)
|
|
69
|
+
// console.log('agent found:', el.children().first().find('img').attr('alt'))?.trim()
|
|
70
|
+
return {
|
|
71
|
+
agent: el.children().first().find('img').attr('alt')?.trim(),
|
|
72
|
+
stats: el.text().split('\t').map(ele => ele.trim()).filter(ele => ele !== '')
|
|
73
|
+
}
|
|
74
|
+
}).get()
|
|
75
|
+
|
|
76
|
+
const agentData = agents.map(agent => {
|
|
77
|
+
return statCategories.reduce((acc, category, index) => {
|
|
78
|
+
if(index == 0) {
|
|
79
|
+
acc[category] = agent.agent
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
acc[category] = agent.stats[index-1]
|
|
83
|
+
}
|
|
84
|
+
return acc
|
|
85
|
+
}, {})
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
return agentData.length<3 ? agentData : agentData.slice(0,3)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function getCurTeam($) {
|
|
92
|
+
// console.log('getCurTeam ran')
|
|
93
|
+
const currentTeam = $('h2:contains("Current Teams")').first().next().text().trim()
|
|
94
|
+
const currentTeamName = currentTeam.split('\t').filter(ele => ele.trim() !== '')
|
|
95
|
+
// Free agents have no current team
|
|
96
|
+
return currentTeamName.length ? currentTeamName[0].trim() : null
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function getPastTeam($) {
|
|
100
|
+
// console.log('getPastTeam ran')
|
|
101
|
+
const pastTeams = $('h2:contains("Past Teams")').first().next().text().trim()
|
|
102
|
+
const pastTeamNames = pastTeams.split('\t').filter(ele => ele.trim() !== ''
|
|
103
|
+
&& !ele.includes('joined')
|
|
104
|
+
&& !ele.includes(' – ')
|
|
105
|
+
&& !ele.includes('left'))
|
|
106
|
+
.map(ele => ele.trim())
|
|
107
|
+
return pastTeamNames
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getWinnings($) {
|
|
111
|
+
// console.log('getWinnings ran')
|
|
112
|
+
const events = $('h2:contains("Event Placements")').first().next()
|
|
113
|
+
const winnings = events.children().first().text().trim()
|
|
114
|
+
// // console.log(winnings.split('\t').map(ele => ele.trim()).filter(ele => ele !== '')[1])
|
|
115
|
+
return winnings.split('\t').map(ele => ele.trim()).filter(ele => ele !== '')[1]
|
|
116
|
+
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function getNationality($) {
|
|
120
|
+
// console.log('getNationality ran')
|
|
121
|
+
const header = $('.flag').first()
|
|
122
|
+
// console.log('flags found:', $('.flag').length);
|
|
123
|
+
// console.log(header.parent().text().trim())
|
|
124
|
+
return header.parent().text().trim()
|
|
125
|
+
}
|
|
126
|
+
|
|
128
127
|
export default scrapePlayer
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as cheerio from 'cheerio'
|
|
1
2
|
import { Hyperbrowser } from "@hyperbrowser/sdk"
|
|
2
3
|
import { config } from "dotenv";
|
|
3
4
|
|
|
@@ -19,24 +20,50 @@ async function scrapeWebsite(url) {
|
|
|
19
20
|
url: url,
|
|
20
21
|
scrapeOptions: {
|
|
21
22
|
formats: ["html"],
|
|
22
|
-
includeTags: [".event-team-players-item"]
|
|
23
|
+
includeTags: [".event-header-main-title", ".event-team-players-item"]
|
|
23
24
|
},
|
|
24
25
|
})
|
|
26
|
+
const data = scrapeData.data.html
|
|
27
|
+
const $ = cheerio.load(data)
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
/*
|
|
30
|
+
<h1 class="event-header-main-title">
|
|
31
|
+
VCT 2027: America Kickoff - North America Open Qualifier
|
|
32
|
+
</h1>
|
|
33
|
+
*/
|
|
34
|
+
const playerLinks = $('.event-team-players-item')
|
|
35
|
+
.map((i, el) => $(el).attr('href'))
|
|
36
|
+
.get()
|
|
37
|
+
.filter(href => href.includes('/player/'))
|
|
38
|
+
.map(href => href + '/?timespan=all')
|
|
39
|
+
|
|
40
|
+
let region = $('.event-header-main-title').first().text().trim()
|
|
41
|
+
console.log(region)
|
|
42
|
+
|
|
43
|
+
if (region.includes('America')) {
|
|
44
|
+
region = 'AMER'
|
|
45
|
+
}
|
|
46
|
+
else if (region.includes('EMEA')) {
|
|
47
|
+
region = 'EMEA'
|
|
48
|
+
}
|
|
49
|
+
else if (region.includes('Pacific')) {
|
|
50
|
+
region = 'PAC'
|
|
51
|
+
}
|
|
52
|
+
else if (region.includes('China')) {
|
|
53
|
+
region = 'CN'
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
region = null
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return { playerLinks, region }
|
|
33
60
|
}
|
|
34
61
|
catch (error) {
|
|
35
62
|
console.error("Error scraping website:", error)
|
|
36
63
|
}
|
|
37
64
|
}
|
|
38
65
|
|
|
39
|
-
export { scrapeWebsite }
|
|
66
|
+
export { scrapeWebsite }
|
|
40
67
|
/*
|
|
41
68
|
node teamscraper.mjs
|
|
42
69
|
*/
|