Анализ настроений помогает вам понять, что люди думают об определенной теме. Приложение для анализа настроений, которое мы собираемся создать, примет ключевое слово (слова) и получит соответствующие твиты из Twitter. Затем он будет запускать каждый твит через модуль узлов анализа настроений с поддержкой AFINN. Этот модуль оценит текст твита и, наконец, отобразит соответствующую статистику.
Прежде чем мы начнем, вы можете посмотреть демо здесь . Вы можете скачать полный исходный код здесь .
Технологический стек
Это приложение построено поверх Nodejs . Мы будем использовать Express в качестве нашей серверной инфраструктуры и jQuery для манипулирования DOM на стороне клиента. Чтобы упростить для нас задачу, мы будем использовать генератор Slush с именем slush-express (написанный мной) для создания нового приложения Express для нас. Для хранения результатов мы будем использовать легковесную дисковую БД с именем diskDB (также написанную мной).
Мы будем использовать модуль Twitter для взаимодействия с Twitter и модуль Sentiment для выполнения анализа.
Итак, начнем.
Настройте приложение
Создайте новую папку с именем sentimentAnalysisApp
и откройте новый терминал / приглашение здесь. Сначала мы собираемся установить генератор Gulp, Slush и Express с помощью следующей команды.
npm i -g gulp slush slush-express
После установки вышеуказанных модулей мы создадим новое приложение Express с помощью этой команды:
slush express
Slush попросит вас выбрать движок представления и движок таблиц стилей. Ответьте, как показано ниже.
[?] Select a View Engine: HTML [?] Select a Stylesheet Engine: CSS
Это займет пару минут, чтобы очистить приложение и установить зависимости. Полученная структура папок будет выглядеть следующим образом:
sentimentAnalysisApp ├── Gulpfile.js ├── app.js ├── bin │ └── www ├── bower.json ├── package.json ├── public │ └── stylesheets │ └── style.css ├── routes │ ├── index.js │ └── users.js └── views ├── error.html └── index.html
Вот краткое объяснение различных файлов и папок.
- bin / www — здесь указывается инициация сервера и
port
. - app.js — здесь настраиваются конфигурация сервера, маршруты и механизм просмотра.
- gulpFile.js — запуск задач для нашего проекта.
- / public — Состоит из статических файлов, отправляемых в пользовательский интерфейс.
- / маршруты — состоит из маршрутов приложения.
- / views — Состоит из представлений приложения.
Вы можете запустить приложение, выполнив команду gulp
. Это запустит сервер Express на порту 3000. Перейдите по http://localhost:3000
и вы увидите пример домашней страницы.
Разработка на стороне сервера
Сначала мы установим зависимости уровня приложения с помощью следующей команды:
npm i twitter sentiment --save
Затем создайте новую папку с именем logic
в корне проекта. Создайте два файла с именами twitterSearch.js
и sentimentAnalysis.js
. Эти файлы состоят из логики для получения твитов из Twitter и выполнения анализа, соответственно. Создайте другую папку с именем db
, в которой будут храниться данные.
Затем откройте routes/index.js
в вашем любимом редакторе. Мы добавим новый маршрут, POST /search
. Введенный пользователем текст поиска будет отправлен в эту конечную точку. Обновите routes/index.js
как показано ниже.
'use strict'; var express = require('express'); var router = express.Router(); var twitterSearch = require('../logic/twitterSearch'); /* GET home page. */ router.get('/', function(req, res) { res.render('index'); }); router.post('/search', function(req, res) { twitterSearch(req.body.search, function (data) { res.json(data); }); }); module.exports = router;
twitterSearch()
функция twitterSearch()
будет принимать условия поиска и получать соответствующие твиты из Twitter. Эти твиты затем будут переданы в модуль анализа настроений, а результаты будут возвращены как обратный вызов. Просто, правда?
Затем откройте logic/twitterSearch.js
и добавьте следующий код.
//includes var util = require('util'), twitter = require('twitter'), sentimentAnalysis = require('./sentimentAnalysis'), db = require('diskdb'); db = db.connect('db', ['sentiments']); //config var config = { consumer_key: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx', consumer_secret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx', access_token_key: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxx', access_token_secret: 'xxxxxxxxxxxxxxxxxxxxxxxxxxx' }; module.exports = function(text, callback) { var twitterClient = new twitter(config); var response = [], dbData = []; // to store the tweets and sentiment twitterClient.search(text, function(data) { for (var i = 0; i < data.statuses.length; i++) { var resp = {}; resp.tweet = data.statuses[i]; resp.sentiment = sentimentAnalysis(data.statuses[i].text); dbData.push({ tweet: resp.tweet.text, score: resp.sentiment.score }); response.push(resp); }; db.sentiments.save(dbData); callback(response); }); }
Чтобы получить твиты, нам нужно сначала зарегистрировать новое приложение в Twitter. Затем мы сгенерируем необходимые ключи и токены, которые будут использоваться при выполнении запроса.
Перейдите к приложениям Twitter и нажмите « Создать новое приложение» . Заполните форму на следующей странице. Как только приложение будет создано, мы сгенерируем необходимые токены. Нажмите на вкладку API Keys и прокрутите вниз до нижней части страницы. Там нажмите на Создать мой токен доступа . Как только это будет сделано, вы увидите сообщение, чтобы обновить страницу, продолжайте и сделайте это. Теперь вы можете увидеть ключ API, секрет API , токен доступа и секрет токена доступа и заполнить объект config
следующим образом:
var config = { consumer_key: 'API key', consumer_secret: 'API secret', access_token_key: 'Access token', access_token_secret: 'Access token secret' };
Если у вас возникли проблемы, обратитесь к этому обсуждению .
Затем откройте logic/sentimentAnalysis.js
и добавьте следующий код.
var sentiment = require('sentiment'); module.exports = function(text) { return sentiment(text); };
Логика очень проста. мы берем текст твита и возвращаем объект sentiment
. Это оборачивает наш сервер. Теперь давайте построим клиент.
Разработка на стороне клиента
Сначала создайте новую папку с именем scripts
внутри общей папки. Внутри scripts
создайте новый файл с именем app.js
и откройте его в своем любимом редакторе. app.js
отвечает за app.js
формы через Ajax в конечную точку /search
и отображение результатов на странице.
Мы собираемся использовать JavaScript- библиотеку Джона Резига для построения разметки на основе данных сервера ( если хотите, небольшого размера MV * ). Я изменил библиотеку шаблонов, чтобы мы могли использовать {{ }}
вместо <% %>
синтаксиса. Таким образом, мы можем использовать ejs
качестве шаблонов на стороне сервера для рендеринга страниц расширения HTML. Завершенный app.js
показан ниже.
$(document).ready(function() { // handle the form submit $('#searchText').on('keypress', function(e) { if (e.which == 13 || e.keyCode == 13) { if ($(this).val().trim().length > 0) { // initiate an Ajax call to send the data fireAJAX($(this).val().trim()); } } }); function fireAJAX(text) { $.ajax({ type: 'POST', url: '/search', data: { search: text }, beforeSend: function(xhr) { $('.tweet-results').html(''); $('.results').show(); enableState(); }, success: parseData, error: oops }); } function parseData(data) { disableState(); var html = ''; for (var i = 0; i < data.length; i++) { var s = data[i].sentiment, t = data[i].tweet; var _o = { imgSrc: t.user.profile_image_url, tweetLink: 'http://twitter.com/' + t.user.screen_name + '/status/' + t.id_str, tweet: t.text, score: s.score ? s.score : '--', comparative: s.comparative ? s.comparative : '--', favorited: t.favorite_count ? t.favorite_count : 0, retweet: t.retweet_count ? t.retweet_count : 0, wordsMatched: s.words && s.words.length ? s.words : '--', positiveWords: s.positive && s.positive.length ? s.positive : '--', negativeWords: s.negative && s.negative.length ? s.negative : '--' }; html += tmpl('tweet_tmpl', _o); }; $('.tweet-results').html(html); } function oops(data) { $('.error').show(); disableState(); } function disableState() { $('.loading').hide(); $('#searchText').prop('disabled', false); } function enableState() { $('.loading').show(); $('#searchText').prop('disabled', true); } }); // Simple JavaScript Templating // John Resig - http://ejohn.org/ - MIT Licensed (function() { var cache = {}; this.tmpl = function tmpl(str, data) { // Figure out if we're getting a template, or if we need to // load the template - and be sure to cache the result. var fn = !/\W/.test(str) ? cache[str] = cache[str] || tmpl(document.getElementById(str).innerHTML) : // Generate a reusable function that will serve as a template // generator (and which will be cached). new Function("obj", "var p=[],print=function(){p.push.apply(p,arguments);};" + // Introduce the data as local variables using with(){} "with(obj){p.push('" + // Convert the template into pure JavaScript str .replace(/[\r\t\n]/g, " ") .split("{{").join("\t") // modified .replace(/((^|\}\})[^\t]*)'/g, "$1\r") // modified .replace(/\t=(.*?)}}/g, "',$1,'") // modified .split("\t").join("');") .split("}}").join("p.push('") // modified .split("\r").join("\\'") + "');}return p.join('');"); // Provide some basic currying to the user return data ? fn(data) : fn; }; })();
.$(document).ready(function() { // handle the form submit $('#searchText').on('keypress', function(e) { if (e.which == 13 || e.keyCode == 13) { if ($(this).val().trim().length > 0) { // initiate an Ajax call to send the data fireAJAX($(this).val().trim()); } } }); function fireAJAX(text) { $.ajax({ type: 'POST', url: '/search', data: { search: text }, beforeSend: function(xhr) { $('.tweet-results').html(''); $('.results').show(); enableState(); }, success: parseData, error: oops }); } function parseData(data) { disableState(); var html = ''; for (var i = 0; i < data.length; i++) { var s = data[i].sentiment, t = data[i].tweet; var _o = { imgSrc: t.user.profile_image_url, tweetLink: 'http://twitter.com/' + t.user.screen_name + '/status/' + t.id_str, tweet: t.text, score: s.score ? s.score : '--', comparative: s.comparative ? s.comparative : '--', favorited: t.favorite_count ? t.favorite_count : 0, retweet: t.retweet_count ? t.retweet_count : 0, wordsMatched: s.words && s.words.length ? s.words : '--', positiveWords: s.positive && s.positive.length ? s.positive : '--', negativeWords: s.negative && s.negative.length ? s.negative : '--' }; html += tmpl('tweet_tmpl', _o); }; $('.tweet-results').html(html); } function oops(data) { $('.error').show(); disableState(); } function disableState() { $('.loading').hide(); $('#searchText').prop('disabled', false); } function enableState() { $('.loading').show(); $('#searchText').prop('disabled', true); } }); // Simple JavaScript Templating // John Resig - http://ejohn.org/ - MIT Licensed (function() { var cache = {}; this.tmpl = function tmpl(str, data) { // Figure out if we're getting a template, or if we need to // load the template - and be sure to cache the result. var fn = !/\W/.test(str) ? cache[str] = cache[str] || tmpl(document.getElementById(str).innerHTML) : // Generate a reusable function that will serve as a template // generator (and which will be cached). new Function("obj", "var p=[],print=function(){p.push.apply(p,arguments);};" + // Introduce the data as local variables using with(){} "with(obj){p.push('" + // Convert the template into pure JavaScript str .replace(/[\r\t\n]/g, " ") .split("{{").join("\t") // modified .replace(/((^|\}\})[^\t]*)'/g, "$1\r") // modified .replace(/\t=(.*?)}}/g, "',$1,'") // modified .split("\t").join("');") .split("}}").join("p.push('") // modified .split("\r").join("\\'") + "');}return p.join('');"); // Provide some basic currying to the user return data ? fn(data) : fn; }; })();
Затем откройте views/index.html
и добавьте следующий код.
<!DOCTYPE html> <html> <head> <title>Sentiment Analysis App</title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1>Sentiment Analysis App</h1> <hr/> <input type="text" id="searchText" placeholder="Enter the text you would like to see the analysis for and hit return"> <div class="results"> <h3>Results</h3> <lable class="loading">Loading.. Please wait</lable> <br/> <lable class="error">Oops.. Something went wrong</lable> <br/> </div> <section class="tweet-results"> </section> <script src="//code.jquery.com/jquery-1.11.0.min.js"></script> <script type="text/javascript" src="scripts/app.js"></script> <script type="text/html" id="tweet_tmpl"> <article> <div class="left"> <img src="{{=imgSrc}}"> <p>{{=tweet}} <a href="{{=tweetLink}}" target="_blank">Link</a></p> </div> <div class="right"> <table> <tr> <td>Score</td> <td>{{=score}}</td> </tr> <tr> <td>Comparative</td> <td>{{=comparative}}</td> </tr> <tr> <td>Favorited</td> <td>{{=favorited}}</td> </tr> <tr> <td>Retweeted</td> <td>{{=retweet}}</td> </tr> <tr> <td>Words Matched</td> <td>{{=wordsMatched}}</td> </tr> <tr> <td>Positive Words</td> <td>{{=positiveWords}}</td> </tr> <tr> <td>Negative Words</td> <td>{{=negativeWords}}</td> </tr> </table> </div> </article> </script> </body> </html>
В<!DOCTYPE html> <html> <head> <title>Sentiment Analysis App</title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1>Sentiment Analysis App</h1> <hr/> <input type="text" id="searchText" placeholder="Enter the text you would like to see the analysis for and hit return"> <div class="results"> <h3>Results</h3> <lable class="loading">Loading.. Please wait</lable> <br/> <lable class="error">Oops.. Something went wrong</lable> <br/> </div> <section class="tweet-results"> </section> <script src="//code.jquery.com/jquery-1.11.0.min.js"></script> <script type="text/javascript" src="scripts/app.js"></script> <script type="text/html" id="tweet_tmpl"> <article> <div class="left"> <img src="{{=imgSrc}}"> <p>{{=tweet}} <a href="{{=tweetLink}}" target="_blank">Link</a></p> </div> <div class="right"> <table> <tr> <td>Score</td> <td>{{=score}}</td> </tr> <tr> <td>Comparative</td> <td>{{=comparative}}</td> </tr> <tr> <td>Favorited</td> <td>{{=favorited}}</td> </tr> <tr> <td>Retweeted</td> <td>{{=retweet}}</td> </tr> <tr> <td>Words Matched</td> <td>{{=wordsMatched}}</td> </tr> <tr> <td>Positive Words</td> <td>{{=positiveWords}}</td> </tr> <tr> <td>Negative Words</td> <td>{{=negativeWords}}</td> </tr> </table> </div> </article> </script> </body> </html>
Здесь мы имеем в виду jQuery и app.js
Мы также создали шаблон ( tweet_tmpl
), который будет использоваться для отображения результатов. Наконец, откройте stylesheets/style.css
и добавьте следующие классы.
body { padding: 50px; font: 14px"Lucida Grande", Helvetica, Arial, sans-serif; background: #eee; } a { color: #00B7FF; } input { width: 98%; padding: 9px; font-size: 17px; } .results { display: none; } .error { color: red; display: none; } .tweet-results { width: 100%; overflow: hidden; padding-right: 18px; } .left { float: left; width: 39%; } .right { float: right; width: 55%; border-left: 1px dashed; padding-left: 21px; } article { background: #fff; display: block; padding: 18px; border: 1px solid #eee; margin-top: 21px; margin-bottom: 21px; overflow: hidden; box-shadow: 6px 4px 9px 1px rgba(119, 119, 119, 0.75); -moz-box-shadow: 6px 4px 9px 1px rgba(119, 119, 119, 0.75); -webkit-box-shadow: 6px 4px 9px 1px rgba(119, 119, 119, 0.75); } article img { width: 64px; float: left; margin:0 5px 0 0; } .right table { width: 100%; } .right table, .right table td { border: 1px solid; } .right table td { width: 50%; }
Вот и все, мы закончили с нашим развитием. Давайте запустим приложение и протестируем его. Вернувшись в терминал, запустите команду gulp
чтобы запустить сервер. Перейдите по http://localhost:3000/
и вы должны увидеть панель поиска. Введите «Это потрясающе» и нажмите «Return», и вы должны увидеть что-то вроде этого:
Здесь Score
— это сумма баллов для каждого слова, присутствующего в твите, соответствующего источнику AFINN . Comparative
равен score/total words
. Words Matched
показывают, сколько слов из твита соответствует словам AFINN во время обработки. Positive Words
— это совпадающие позитивные слова, а Negative Words
— это совпадающие негативные слова. Эти данные должны дать вам достаточно информации, чтобы принять решение и понять настроение.
Очистите панель поиска и введите sad broken
и нажмите возврат. Ваши результаты должны выглядеть примерно так:
Просто и легко, правда? Теперь вы можете найти несколько слов и посмотреть, как получится.
Тренировка ваших данных
Вы, должно быть, уже заметили, что не все твиты возвращают результаты. Это потому, что если ни одно из слов в твите не соответствует словам AFINN, оценка будет равна 0. Например:
Если вы хотите это исправить, вы можете обучить модуль настроений. Создайте новый файл в папке logic
именем training.js
и добавьте следующий код.
module.exports = { directives: 4, angular: 5, code: 3, scope: 3 };
Здесь мы обучаем модуль использовать вышеупомянутые оценки для упомянутых слов. Затем обновите logic/sentimentAnalysis.js
следующим образом:
var sentiment = require('sentiment'); var trainedData = require('./training.js'); module.exports = function(text) { return sentiment(text, trainedData); }
После тренировки результаты будут выглядеть так:
Это очень мощный метод, поэтому убедитесь, что вы присвоили ключевые слова правильные значения, иначе вы можете увидеть результаты, которые могут не иметь смысла. Обратитесь к AFINN для получения дополнительной информации.
Создание сервиса RESTful
Вы можете создать панель мониторинга в реальном времени, которая будет показывать твиты и результаты. Вы можете запустить асинхронное задание, которое будет время от времени обращаться к API Twitter, извлекать данные и сохранять их с помощью diskDB. Затем вы можете предоставить этот файл в качестве конечной точки RESTful. Добавьте следующий код в routes/index.js
.
router.get('/data', function(req, res) { res.json(require('diskdb') .connect('db', ['sentiments']) .sentiments.find()); });
Теперь, когда вы http://localhost:3000/data
к http://localhost:3000/data
вы можете видеть полные сохраненные данные. Живой пример доступен здесь .
Интегрировать социальные медиа
Подобно Twitter, вы можете интегрировать Facebook , Google+ и другие данные социальных сетей. Все, что вам нужно сделать, — это передать текст, который вы хотите проанализировать, в logic/sentimentAnalysis.js
, и вы сможете увидеть его оценку.
Вывод
Я надеюсь, что у вас есть основная идея о том, как выполнить анализ настроений с помощью этого приложения. Спасибо за прочтение! Прокомментируйте, пожалуйста.