Lately, I've been, I've been losing sleep
Dreaming about the things that we could be
But baby, I've been, I've been praying hard,
Said, no more counting dollars
We'll be counting stars, yeah we'll be counting stars
(One Republic - Counting Stars)
The 2nd Monitor is known to be a quite star-happy chat room, but exactly how many stars is there? And who are the most starred users? I decided to write a script to find out.
I chose to write this in Python because:
- I've never used Python before
- @200_success used it and it didn't look that hard
- I found Beautiful Soup which seemed powerful and easy to use
The script performs a number of HTTP requests to the list of starred messages, keeps track of the numbers in a dict
and also saves the pure HTML data to files (to make it easier to perform other calculations on the data in the future, and I had the opportunity to learn file I/O in Python).
To be safe, I included a little delay between some of the requests (Don't want to risk getting blocked by the system)
Unfortunately there's no way to see which user starred the most messages, but we all know who that is anyway...
The code:
from time import sleep
from bs4 import BeautifulSoup
from urllib import request
from collections import OrderedDict
import operator
room = 8595 # The 2nd Monitor (Code Review)
url = 'http://chat.stackexchange.com/rooms/info/{0}/the-2nd-monitor/?tab=stars&page={1}'
pages = 125
def write_files(filename, content):
with open(filename, 'w', encoding = 'utf-8') as f:
f.write(content)
def fetch_soup(room, page):
resource = request.urlopen(url.format(room, page))
content = resource.read().decode('utf-8')
mysoup = BeautifulSoup(content)
return mysoup
allstars = {}
def add_stars(message):
message_soup = BeautifulSoup(str(message))
stars = message_soup.select('.times').pop().string
who = message_soup.select(".username a").pop().string
# If there is only one star, the `.times` span item does not contain anything
if stars == None:
stars = 1
if who in allstars:
allstars[who] += int(stars)
else:
allstars[who] = int(stars)
for page in range(1, pages):
print("Fetching page {0}".format(page))
soup = fetch_soup(room, page)
all_messages = soup.find_all(attrs={'class': 'monologue'})
for message in all_messages:
add_stars(message)
write_files("{0}-page-{1}".format(room, page), soup.prettify())
if page % 5 == 0:
sleep(3)
# Create a sorted list from the dict with items sorted by value (number of stars)
sorted_stars = sorted(allstars.items(), key=lambda x:x[1])
for user in sorted_stars:
print(user)
The results, you ask? Well, here they are: (Spoiler warning!) (I'm only showing those who have \$> 50\$ stars here, to keep the list shorter)
('apieceoffruit', 73)
('ChrisW', 85)
('Edward', 86)
('Yuushi', 93)
('Marc-Andre', 98)
('nhgrif', 112)
('amon', 119)
('James Khoury', 126)
('Nobody', 148)
('Jerry Coffin', 150)
('BenVlodgi', 160)
('Donald.McLean', 174)
('konijn', 184)
('200_success', 209)
('Vogel612', 220)
('kleinfreund', 229)
('Corbin', 233)
('Morwenn', 253)
('skiwi', 407)
('lol.upvote', 416)
('syb0rg', 475)
('Malachi', 534)
('retailcoder', 749)
("Mat's Mug", 931)
('Simon André Forsberg', 1079)
('Jamal', 1170)
('The Mug with many names', 2096) (Mat's Mug, retailcoder and lol.upvote is the same user)
('rolfl', 2115)
It feels strange to do .pop()
to fetch data from the select soup, is there another approach available here? However, as this is the first time I'm using Python, any comments are welcome.