Skip to content

Instantly share code, notes, and snippets.

@bradmontgomery
Last active March 6, 2024 20:23
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save bradmontgomery/9166407291eb697f2ef5825103d85be7 to your computer and use it in GitHub Desktop.
Save bradmontgomery/9166407291eb697f2ef5825103d85be7 to your computer and use it in GitHub Desktop.
Quick & dirty hack to list a user's gists, sorted by stars
#!/usr/bin/env python
"""
A quick & dirty hack to list all of your Gists sorted by stars.
This script lists your gists, the number of stars, the number of comments,
a truncated description, and the url for the gist.
NOTE: This only returns public gists. If you want to see (your own) private gists,
you can provide a personal access token.
"""
import click
import requests
import sys
from github3.github import GitHub
from html.parser import HTMLParser
from tabulate import tabulate
class StarParser(HTMLParser):
"""Parse the HTML document for a github gist looking for the number of
times a gist has been starred.
"""
stars = 0 # Numeric value of the "Star" button.
# Flags that need to be active before we can try to grab the "DATA" (stored in both title & content)
# <span class="Button-content">
# <span class="Button-visual">...</span>
# <span class="Button-label">Star</span> <----- Look for this.
# <span title="1234" class="Counter">1234</span> <----- Save this.
# </span>
in_button = False # Flag if the parser is looking _in_ a button
is_star = False # Was the body of the preceding span == "Star"?
def handle_starttag(self, tag, attrs):
if tag == "span" and ("class", "Button-label") in attrs: # We're _inside_ a button
self.in_button = True
if tag == "span" and "title" in [k for k, _ in attrs]:
self.has_title = True
if self.in_button and self.has_title:
self._tag = tag
self._attrs = attrs
if tag != "span":
self.in_button = False
self.has_title = False
def handle_data(self, data):
# We already found the "Star" button
if self.in_button and self.is_star and data and data.strip().isnumeric():
try:
self.stars = int(data.strip())
except ValueError:
pass
# Reset our flags
self.in_button = False
self.is_star = False
# Looking for the "Star" content...
if self.in_button and data and data.strip() == "Star":
self.is_star = True # next element is the star count.
def _trunc(description, chars=50):
"""Truncate a description to `chars` characters."""
if description:
return description[:chars]
return ""
def _get_stars(url):
"""Scrape the stars number from each gist url."""
parser = StarParser()
resp = requests.get(url)
stars = 0
if resp.status_code == 200:
parser.feed(resp.content.decode("utf8"))
return parser.stars
return stars
def _sorted_by_stars(username, token=None):
"""Sort the given user's public gists by the number of starts"""
gh = GitHub(token=token)
results = []
for gist in gh.gists_by(username):
results.append(
[
_get_stars(gist.html_url),
gist.comments_count,
_trunc(gist.description),
gist.html_url,
"Public" if gist.public else "Private",
]
)
results = sorted(results, reverse=True)
print(tabulate(results, headers=["Stars", "Comments", "Description", "Link", "Visibility"]))
@click.command
@click.argument("username")
@click.option("-t", "--token", default=None, help="Optional github personal access token")
def main(username, token):
print(f"Listing gists for {username}")
_sorted_by_stars(username, token)
if __name__ == "__main__":
main()
github3.py==4.0.1
requests==2.31.0
tabulate==0.9.0
click==8.1.7
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment