Skip to content

Instantly share code, notes, and snippets.

@thelabcat
Created June 13, 2026 20:53
Show Gist options
  • Select an option

  • Save thelabcat/80e06903410e73310324b2662d2d5171 to your computer and use it in GitHub Desktop.

Select an option

Save thelabcat/80e06903410e73310324b2662d2d5171 to your computer and use it in GitHub Desktop.
Gaming on Rumble viewers per stream calculator
#!/usr/bin/env python3
"""Gaming on Rumble viewers per stream calculator
Do some scraping, and calculate a viewer-per-stream stat for gaming on Rumble,
and format it to BewareTheMoon's X post style.
Relies on Chrome or a compatible browser being installed and configured for
Selenium puppeteering, plus the Python libraries `bs4` and `selenium` being
installed. Both modules are available from PYPI.
Copyright 2026 Wilbur Jaywright dba Marswide BGL
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
S.D.G.
"""
import bs4
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
RUMBLE_BASE_URI = "https://rumble.com"
CAT_SUB_URI = "/category/gaming" # The sub URI pointing at the category page
PAGE_PARAM = "?page=" # The parameter at the end of the category page URI that specifies with number page we are on
VIEW_MULTS = "KMGT" # Suffix multiples of 1000 views
def get_category_page(driver: webdriver.Chrome, pagenum: int = 1) -> bs4.BeautifulSoup:
"""
Load a numbered page of the category.
Args:
driver (webdriver.Chrome): A running Selenium driver.
pagenum (int): The page number to load.
Defaults to 1.
Returns:
page (bs4.BeautifulSoup): HTML soup of the page.
"""
url = RUMBLE_BASE_URI + CAT_SUB_URI + PAGE_PARAM + str(pagenum)
print(f"Loading {url} ...", end=" ")
driver.get(url)
print("Done.")
return bs4.BeautifulSoup(driver.page_source, features="html.parser")
def count_videos_on_page(page: bs4.BeautifulSoup) -> int:
"""Count the videos on a single category page"""
return len(page.find_all("rum-video-thumbnail"))
def get_page_ref(link_bttn: bs4.Tag) -> int:
"""For a number page button, find which page it is pointing at"""
assert link_bttn.get("href").startswith(CAT_SUB_URI), f"Expected page link button, got {link_bttn}"
# The button has no page parameter, so it will go to the first page
if "?page=" not in link_bttn["href"]:
return 1
# Parse the value of the page parameter
return int(link_bttn["href"].split(PAGE_PARAM)[1])
# Start the browser
print("Starting web browser driver...", end=" ")
chrome_options = Options()
chrome_options.add_argument("--headless=new")
driver = webdriver.Chrome(options=chrome_options)
print("Started.")
# Start with the first page of the category
first_page = get_category_page(driver)
# Find the highest valued numbered page button, and store what number it is
page_count = get_page_ref(
max(
# Use a CSS selector to find all links that point to this same category
# Most of them are the numbered page buttons at the bottom...
first_page.select(f"a[href^={repr(CAT_SUB_URI)}]"),
# ...then find the maximum page parameter among them
key=get_page_ref
)
)
print(f"There are {page_count} total pages of game streams.")
# The total number of videos on the first page is either the max there can be, or all there are.
# In the second case, the math later still works.
max_on_page = count_videos_on_page(first_page)
print("There are", max_on_page, "streams on the first page, so that's the most there will be on each.")
# If we have more than one page, the first page is full
if page_count != 1:
# Find the number of videos on the last page
last_page = get_category_page(driver, page_count)
on_last_page = count_videos_on_page(last_page)
print("There are", on_last_page, "streams on the last page.")
else:
# The first page is the only one, that's all the videos
on_last_page = max_on_page
# We are done requesting pages I think
print("Shutting down web browser driver.")
driver.quit()
# Even if the first page is the only one, that just means this equals on_last_page, which will be correct
total_streams = max_on_page * (page_count - 1) + on_last_page
print() # Value is calculated, but to fit tweet form, we want it later...
# Time to parse the viewer count!
viewcount_elem = first_page.select_one(".header__viewers")
viewcount_text = viewcount_elem.get_text().strip() # "6.7K Viewers"
assert viewcount_text.endswith(
"Viewers"
), f"Error finding view count element, found instead: {viewcount_elem}"
viewcount_numstr = viewcount_text.removesuffix("Viewers").strip() # "6.7K"
# Is the view count multiplied by a suffix?
if not viewcount_numstr[-1].isnumeric():
viewcount = int(
# Get the numeric half...
float(viewcount_numstr[:-1])
# ...then parse the multiplier suffix half and apply it
* (1000 ** (VIEW_MULTS.index(viewcount_numstr[-1]) + 1))
)
# The viewcount is direct, with no multiplier suffix
else:
viewcount = int(viewcount_numstr)
# Finally, print the tweet format
print(f"There are currently {viewcount_numstr} ppl viewing gaming on Rumble")
print(f"There are currently {total_streams} ppl streaming games on Rumble")
print(
f"Divided evenly, that would be {viewcount / total_streams:.2f} viewers per streamer"
)
input("\nPress enter to exit.")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment