YouTube statistics to Pushover using SocialBlade

Are you looking for a way to get notified whenever your YouTube stats are updated? Look no further!

SocialBladePushoverLast year I made a small script that would notify me whenever I got a new subscriber or the number of views increased significantly. I’ve now updated the script to get all the data from socialblade.com instead of YouTube. This way it doesn’t only support subscribers and view count, but also other ranking metrics that might or might not be of interest to you. This version also fixed a number of issues and (while still messy) is a lot cleaner than my previous code snippet.

Feel free to ask any questions you might have in the comment section below!

Requirements

The script currently requires you to purchase a Pushover license (7 day free trial available), but it’s easily modified to work with any other notification service you may prefer. You’ll obviously also need Python 2.7 and the modules defined in the script (I think of all of them are bundled with Python originally). Before using the script you should also make sure that your YouTube channel is indexed by SocialBlade. Normally it’s enough just to visit the site and search once for your YouTube channel. Please note that it may take some time before the statistics start to appear on the site. Signup shouldn’t be required.

Configuration

Replace PUSHOVER_USER with your Pushover User Key and modify all variables in YOUTUBE_CHANNELS with the correct values. The first variable is the name of your YouTube channel, the second variable is your YouTube Channel ID and the last one is your application specific Pushover API Key. You can also specify multiple YouTube channels like this:

YOUTUBE_CHANNELS = [["YouTube1", "CHANNEL_ID","PUSHOVER_API_KEY"], ["YouTube2", "CHANNEL_ID","PUSHOVER_API_KEY"]]

The script will notify you for every single subscriber or view. If you want to limit the number of notifications just set the threshold to whatever you want. If you for some reason want to disable certain metrics such as the number of views you can do so easily by adding a hashtag (#) before the current line such as # result.append({‘name’: ‘Views’….

The script can be executed similar to any other Python script. Just copy all the lines below and create a file called socialblade2pushover.py and run it using python socialblade2pushover.py.

If you want to run the script every 10 minutes on Linux you can add the following to your crontab:

*/10 * * * * /usr/bin/python /opt/socialblade2pushover.py

Script

#!/usr/bin/python

import sys
import re
import lxml.html
import urllib2, os, httplib, urllib, json

PUSHOVER_USER = "PUSHOVER_USER_KEY"
YOUTUBE_CHANNELS = [["IDmedia", "CHANNEL_ID","PUSHOVER_API_KEY"]]

def main():
    # Change to our directory
    abspath = os.path.abspath(__file__)
    dname = os.path.dirname(abspath)
    os.chdir(dname)

    for youtube_channel in YOUTUBE_CHANNELS:
       # Fetch data from SocialBlade
       online_data = json.loads(get_socialblade_data(youtube_channel[1]))
       local_data = None
       settings_file = 'data_%s.json' % youtube_channel[0].lower()

       # Create local file if necessary
       if not os.path.isfile(settings_file):       
           with open(settings_file, 'w') as f:
               json.dump(online_data, f)

       # Load local file
       with open(settings_file, 'r') as f:
           local_data = json.load(f)

       # Compare data and notify if changed
       for ok, lk in zip(online_data, local_data):
            ov = ok['value']
            lv = lk['value']

            # If a value has changed we want to send a notification
            if ov != lv:
                if ov.isdigit() and lv.isdigit():
                    # Skip if threshold is set and not met. Keep the old value
                    if abs(int(ov) - int(lv)) < int(ok['threshold']):
                        ok['value'] = lk['value']
                        continue

                    dv = ' ({0:,})'.format(int(ov) - int(lv))
                    ov = '{0:,}'.format(int(ov))
                    lv = '{0:,}'.format(int(lv))
                else:
                    dv = ''
                
                # Push notification through Pushover
                send_pushover(youtube_channel[2], youtube_channel[0], ok['name'], ov, lv, dv)

       # Update local stored cache
       with open(settings_file, 'w') as f:
           json.dump(online_data, f)

def get_socialblade_data(channel_id):
    headers = { 'User-Agent' : 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.97 Safari/537.36' }
    req = urllib2.Request('http://socialblade.com/youtube/channel/' + channel_id, None, headers)
    tree = lxml.html.parse(urllib2.urlopen(req))

    info = tree.xpath('//div[@class="YouTubeUserTopInfo"]/descendant::span[position() mod 2 = 0][string-length(text()) > 0]')
    rank = tree.xpath('//*[contains(text(),"TOTAL GRADE")]/ancestor::div[1]/ancestor::div[1]//p[1]')

    result = []
    result.append({'name': 'Subscribers', 'value': re.sub('[^0-9]', '', info[1].text_content().strip()), 'threshold': '1'})
    result.append({'name': 'Views', 'value': re.sub('[^0-9]', '', info[2].text_content().strip()), 'threshold': '1'})
    result.append({'name': 'Grade', 'value': rank[0].text_content().strip(), 'threshold': '0'})
    result.append({'name': 'Subscriber rank', 'value': re.sub('[^0-9]', '', rank[1].text_content().strip()), 'threshold': '1'})
    result.append({'name': 'Video rank', 'value': re.sub('[^0-9]', '', rank[2].text_content().strip()), 'threshold': '1'})
    result.append({'name': 'SocialBlade rank', 'value': re.sub('[^0-9]', '', rank[3].text_content().strip()), 'threshold': '1'})

    return json.dumps(result)

def send_pushover(pushover_token, name, key, new_value, old_value, diff_value):
    conn = httplib.HTTPSConnection("api.pushover.net:443")
    conn.request("POST", "/1/messages.json",
    urllib.urlencode({
    "token": pushover_token,
    "user": PUSHOVER_USER,
    "message": "%s for %s: %s\nPreviously: %s%s" % (key, name, new_value, old_value, diff_value),
    }), { "Content-type": "application/x-www-form-urlencoded" })
    conn.getresponse()

if __name__ == '__main__':
    main()

 

Share this:

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.