Selasa, 20 Maret 2018

Sentimen Analisis pada Komentar Youtube Menggunakan Python

Anggota Kelompok:
1. Linda Dwi Novianti
2. Muhammat Amir Munajad
3. Rizka Yulia Sukma
4. Triswanto

Opinion Mining / Sentiment Analysis (sebagian besar researcher menganggap dua istilah ini sama/interchangeable) merupakan sebuah cabang penelitian di domain Text Mining yang mulai booming pada awal tahun 2002-an. Riset-nya mulai marak semenjak paper dari B.Pang dan L.Lee keluar. Secara umum, Sentiment analysis ini dibagi menjadi 2 kategori besar :
1. Coarse-grained sentiment analysis
2. Fined-grained sentiment analysis
Coarse-grained sentiment analysis - kita mencoba melakukan proses analysis pada level Dokumen. Singkatnya adalah kita mencoba mengklasifikasikan orientasi sebuah dokumen secara keseluruhan. Orientasi ini ada 3 jenih : Positif, Netral, Negatif. Akan tetapi, ada juga yang menjadikan nilai orientasi ini bersifat kontinu / tidak diskrit.
Fined-grained sentiment analysis - kategori kedua ini yang sedang Naik Daun sekarang. Maksudnya adalah para researcher sebagian besar fokus pada jenis ini. Obyek yang ingin diklasifikasi bukan berada pada level dokumen melainkan sebuah kalimat pada suatu dokumen.
Berikut dibawah ini merupakan tahapan dalam pembuatan sentiment analyst menggunakan Bahasa pemrograman python.

1. Pada pembuatan program kali ini memakai python versi 3.6.4
2. Berikut merupakan listing program nya.

import os
import sys
import time
import json
import requests
import argparse
import lxml.html
import re

from textblob import TextBlob
from lxml.cssselect import CSSSelector

YOUTUBE_COMMENTS_URL = 'https://www.youtube.com/watch?v={youtube_id}'
YOUTUBE_COMMENTS_AJAX_URL = 'https://www.youtube.com/comment_ajax'
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36'

np = 0
nn = 0
n = 0

def clean_text(text_sel):
    return ' '.join(re.sub("(@[A-Za-z0-9]+)|([^0-9A-Za-z \t])|(\w+:\/\/\S+)", " ", text_sel).split())


def find_value(html, key, num_chars=2):
    pos_begin = html.find(key) + len(key) + num_chars
    pos_end = html.find('"', pos_begin)
    return html[pos_begin: pos_end]


def extract_comments(html):
    global np, n, nn
    tree = lxml.html.fromstring(html)
    item_sel = CSSSelector('.comment-item')
    text_sel = CSSSelector('.comment-text-content')
    time_sel = CSSSelector('.time')
    author_sel = CSSSelector('.user-name')
    
    for item in item_sel(tree):
        analysis = TextBlob(clean_text(text_sel(item)[0].text_content()))
        # set sentiment
        if analysis.sentiment.polarity > 0:
            result = 'positive'
            np += 1
        elif analysis.sentiment.polarity == 0:
            result = 'neutral'
            n += 1
        else:
            result = 'negative'
            nn += 1

        yield {'cid': item.get('data-cid'),
               'text': text_sel(item)[0].text_content(),
               'time': time_sel(item)[0].text_content().strip(),
               'author': author_sel(item)[0].text_content(),
               'result': result}


def extract_reply_cids(html):
    tree = lxml.html.fromstring(html)
    sel = CSSSelector('.comment-replies-header > .load-comments')
    return [i.get('data-cid') for i in sel(tree)]


def ajax_request(session, url, params, data, retries=10, sleep=20):
    for _ in range(retries):
        response = session.post(url, params=params, data=data)
        if response.status_code == 200:
            response_dict = json.loads(response.text)
            return response_dict.get('page_token', None), response_dict['html_content']
        else:
            time.sleep(sleep)


def download_comments(youtube_id, sleep=1):
    session = requests.Session()
    session.headers['User-Agent'] = USER_AGENT

    # Get Youtube page with initial comments
    response = session.get(YOUTUBE_COMMENTS_URL.format(youtube_id=youtube_id))
    html = response.text
    reply_cids = extract_reply_cids(html)

    ret_cids = []
    for comment in extract_comments(html):
        ret_cids.append(comment['cid'])
        yield comment

    page_token = find_value(html, 'data-token')
    session_token = find_value(html, 'XSRF_TOKEN', 4)

    first_iteration = True

    # Get remaining comments (the same as pressing the 'Show more' button)
    while page_token:
        data = {'video_id': youtube_id,
                'session_token': session_token}

        params = {'action_load_comments': 1,
                  'order_by_time': True,
                  'filter': youtube_id}

        if first_iteration:
            params['order_menu'] = True
        else:
            data['page_token'] = page_token

        response = ajax_request(session, YOUTUBE_COMMENTS_AJAX_URL, params, data)
        if not response:
            break

        page_token, html = response

        reply_cids += extract_reply_cids(html)
        for comment in extract_comments(html):
            if comment['cid'] not in ret_cids:
                ret_cids.append(comment['cid'])
                yield comment

        first_iteration = False
        time.sleep(sleep)

    # Get replies (the same as pressing the 'View all X replies' link)
    for cid in reply_cids:
        data = {'comment_id': cid,
                'video_id': youtube_id,
                'can_reply': 1,
                'session_token': session_token}

        params = {'action_load_replies': 1,
                  'order_by_time': True,
                  'filter': youtube_id,
                  'tab': 'inbox'}

        response = ajax_request(session, YOUTUBE_COMMENTS_AJAX_URL, params, data)
        if not response:
            break

        _, html = response

        for comment in extract_comments(html):
            if comment['cid'] not in ret_cids:
                ret_cids.append(comment['cid'])
                yield comment
        time.sleep(sleep)


def main(argv):
    global np, n, nn
    parser = argparse.ArgumentParser(add_help=False, description=('Download Youtube comments without using the Youtube API'))
    parser.add_argument('--help', '-h', action='help', default=argparse.SUPPRESS, help='Show this help message and exit')
    parser.add_argument('--youtubeid', '-y', help='ID of Youtube video for which to download the comments')
    parser.add_argument('--output', '-o', help='Output filename (output format is line delimited JSON)')
    parser.add_argument('--limit', '-l', type=int, help='Limit the number of comments')

    try:
        args = parser.parse_args(argv)

        youtube_id = args.youtubeid
        output = args.output
        limit = args.limit

        if not youtube_id or not output:
            parser.print_usage()
            raise ValueError('you need to specify a Youtube ID and an output filename')

        print('Downloading Youtube comments for video:', youtube_id)
        count = 0
        with open(output, 'w') as fp:
            for comment in download_comments(youtube_id):
                print(json.dumps(comment), file=fp)
                count += 1
                sys.stdout.write('Downloaded %d comment(s)\r' % count)
                sys.stdout.flush()
                if limit and count >= limit:
                    break

        print('\n\nPositive: ', float(np/(np+n+nn)*100), '%')
        print('Negative: ', float(nn/(np+n+nn)*100), '%')
        print('Neutral: ', float(n/(np+n+nn)*100), '%')
        print('\nDone!')


    except Exception as e:
        print('Error:', str(e))
        sys.exit(1)


if __name__ == "__main__":
    main(sys.argv[1:])

3. Pastikan sudah menginstall packages untuk program diatas.
a. pip install requests
b. pip install lxml
c. pip install cssselect
d. pip install textblob

4. Listing program dibawah ini digunakan untuk mendeklarasikan beberapa url yang akan diakses kedalam program dan beberapa variable untuk menampung nilai – nilai dalam program.
YOUTUBE_COMMENTS_URL = 'https://www.youtube.com/watch?v={youtube_id}'
YOUTUBE_COMMENTS_AJAX_URL = 'https://www.youtube.com/comment_ajax'
USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36'
np = 0 #persentase positif
nn = 0 #persentase negatif
n = 0 #persentase netral
5. def extract_comments(html), berfungsi untuk mengambil data-data komentar seperti penulis, waktu, dan komentar itu sendiri berdasarkan response (html). Pada fungsi ini juga ditentukan apkah komentar itu positif, nagatif, atau netral dengan TextBlob.
6. def extract_reply_cids(html), hampir sama dengan fungsi sebelumnya hanya mengambil id reply
7. def download_comments(youtube_id, sleep=1), berfungsi untuk membuat request session ke alamat URL youtube dan setelah mendapat response, response tersebut dikirim ke fungsi extract_comments
8. def main(argv), fungsi yang pertama kali dijalankan dengan beberapa argument, yaitu –youtubeid untuk meletakkan id youtube, --output untuk meletakkan komentar kedalam file, --limit untuk membatasi komentar yang didownload. Pada fungsi ini akan dijalankan fungsi download_comments yang nilai baliknya akan dimasukkan kedalam file yang telah didefinisikan pada saat menjalankan program ini (contoh: --output analisa.txt) , kemudian menghitung persentase komentar positif, negative, dan netral.

Hasil




Sumber:

Tidak ada komentar:

Posting Komentar