[PYTHON] Your own Twitter client made with Django

I will try to make it

As was the case with bottles, I think that if you start by creating a Twitter client, you will deepen your basic understanding of Django. By the way, I will make something that allows you to see posts, replies, and home timelines on one screen.

スクリーンショット 2016-10-25 13.09.09.png

The result is an image like this.

  1. API limit display
  2. Display one timeline
  3. Prepare to reply by clicking the user name or image on the timeline
  4. You can tweet text

It will be a specification such as.

If you want to try it under similar conditions, please refer to the previous article. http://qiita.com/Gen6/items/735245423b65698428be

contents

.py

myapp/views.py


from requests_oauthlib import OAuth1Session
import time, calendar
import datetime
import json
import re
import os
import requests
import sys, codecs
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)

from django.http.response import HttpResponse
from django.shortcuts import render


def index(request):

    msg = request.GET.get('words')

    C_KEY = '********************'
    C_SECRET = '********************'
    A_KEY = '********************'
    A_SECRET = '********************'

    url = 'https://api.twitter.com/1.1/statuses/update.json'
    params = {'status': msg,'lang': 'ja'}
    tw = OAuth1Session(C_KEY,C_SECRET,A_KEY,A_SECRET)
    req = tw.post(url, params = params)


    url = 'https://api.twitter.com/1.1/statuses/home_timeline.json'
    params = {'count': 1}
    req = tw.get(url, params = params)

    if req.status_code == 200:
        timeline = json.loads(req.text)
        limit = req.headers['x-rate-limit-remaining']

        for tweet in timeline:
            Text = (tweet['text'])
            User = (tweet['user']['screen_name'])
            Name = (tweet['user']['name'])
            Img = (tweet['user']['profile_image_url'])
            Created_at = YmdHMS(tweet['created_at'])

            Message = {
                'Words': msg,
                'timeline': timeline,
                'API_limit': limit,
                'Text': Text,
                'User': User,
                'Name': Name,
                'Img': Img,
                'Created_at': Created_at,
            }

            return render(request, 'index.html', Message)

    else:
        Error = {
            'Error_message': 'API restricted',
        }
        return render(request, 'index.html', Error)


def YmdHMS(created_at):
    time_utc = time.strptime(created_at, '%a %b %d %H:%M:%S +0000 %Y')
    unix_time = calendar.timegm(time_utc)
    time_local = time.localtime(unix_time)
    return int(time.strftime('%Y%m%d%H%M%S', time_local))

YdmHMS.py


def YmdHMS(created_at):
    time_utc = time.strptime(created_at, '%a %b %d %H:%M:%S +0000 %Y')
    unix_time = calendar.timegm(time_utc)
    time_local = time.localtime(unix_time)
    return str(time.strftime('%Y'+'Year'+'%m'+'Month'+'%d'+'Day'+'%H'+'Time'+'%M'+'Minutes', time_local))

HTML and CSS

templates/base.html


<!DOCTYPE html>
<html lang="la">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="../static/css/bootstrap.min.css" rel="stylesheet">
    <link href="../static/css/custom.css" rel="stylesheet">
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <script src="../static/js/bootstrap.min.js"></script>
    <title></title>
  </head>
  <body>
    {% block body %}
    {% endblock %}
  </body>
</html>

The URL is solid, isn't it?

templates/index.html


{% extends "base.html" %}
{% block body %}
  <div class="container">
    <div class="row">
      <div class="col-md-6">
        <form action="" method="get" class="form-group">
          <label><input type="text" size="60" name="words" class="form-control" placeholder="Tweet"></label>
          <input type="submit" class="btn btn-primary" value="Send">
          <input type="button" class="btn btn-warning" value="Reload" onclick="location.reload();" />
        </form>

        {% if Words %}
        <p>「{{ Words }}I tweeted.</p>
        {% endif %}
        <p class="error-message">{{ Error_message }}</p>
        <p><span class="status">API-Limit:{{ API_limit }}</p>
      </div>
      <script type="text/javascript">
      $(function() {
      $(".reply").click(function() {
        var newText = '@{{ User }} ';
        $(':text[name="words"]').val(newText);
      });
      });
      </script>

      <div class="col-md-6">
        {% if API_limit %}
        <dl>
          <dt><span class="reply"><img class="users-img" src="{{ Img }}">{{ User }}/{{ Name }}</span><span class="d-time">{{ Created_at }}</span></dt>
          <dd>{{ Text }}</dd>
        </dl>
        <ul id="output"></ul>
        {% endif %}
      </div>

    </div>
  </div>

{% endblock %}

static/css/custom.css


.container {
  width: 100%;
}

.row {
  margin: 40px auto;
}

.status {
  background: #E2264D;
  border-radius: 4px;
  color: #fff;
  padding: 4px;
  width: 100px;
  text-align: center;
  margin-right: 10px;
}

.reply {
  cursor: pointer;
  font-weight: bold;
}

.btn-primary {
  margin-right: 2px;
}

.d-time {
  margin-left: 10px;
  border: 1px solid #d8d8d8;
  border-radius: 4px;
  padding: 4px;
  font-weight: normal;
}

ul li {
  list-style: none;
}

.users-img {
  margin-right: 10px;
  border-radius: 4px;
}

dt {
  margin-bottom: 10px;
}

.error-message {
  color: #ff3300;
}

If you want to save it in the database, please see the following article. http://qiita.com/Gen6/items/907d869cdf1d588a4751

Further customization

スクリーンショット 2016-10-26 13.06.43.png

Create with an image like the one above.

Check settings.py

I will write about line 107.

mysite/settings.py


LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'

USE_I18N = True

USE_L10N = True

USE_TZ = True

STATIC_URL = '/static/'

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, "static"),
)

Add views.py

It is a pattern that implements posting, mention, and search. Since it is written in one views file, it is not easy to maintain, but please refer to it.

You can put the KEY outside or write it more simply, but it is easy to understand. Try different things to keep your code clean.

The search result will be a pattern saved in the database.

myapp/views.py


from requests_oauthlib import OAuth1Session
import time, calendar
import datetime
import json
import re
import os
import requests
import sys, codecs
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)

from django.http.response import HttpResponse
from django.shortcuts import render
from myapp.models import Supermodel


def index(request):

    msg = request.GET.get('words')

    C_KEY = '********************'
    C_SECRET = '********************'
    A_KEY = '********************'
    A_SECRET = '********************'

    url = 'https://api.twitter.com/1.1/statuses/update.json'
    params = {'status': msg,'lang': 'ja'}
    tw = OAuth1Session(C_KEY,C_SECRET,A_KEY,A_SECRET)
    req = tw.post(url, params = params)


    url = 'https://api.twitter.com/1.1/statuses/home_timeline.json'
    params = {'count': 1}
    req = tw.get(url, params = params)

    if req.status_code == 200:
        timeline = json.loads(req.text)
        limit = req.headers['x-rate-limit-remaining']

        for tweet in timeline:
            Text = (tweet['text'])
            User = (tweet['user']['screen_name'])
            Name = (tweet['user']['name'])
            Img = (tweet['user']['profile_image_url'])
            Created_at = YmdHMS(tweet['created_at'])

            Message = {
                'Words': msg,
                'timeline': timeline,
                'API_limit': limit,
                'Text': Text,
                'User': User,
                'Name': Name,
                'Img': Img,
                'Created_at': Created_at,
            }

            return render(request, 'index.html', Message)

    else:
        Error = {
            'Error_message': 'API restricted',
        }
        return render(request, 'index.html', Error)


def search(request):

    search_words = 'hogehoge'
    search_words = request.GET.get('words')

    C_KEY = '********************'
    C_SECRET = '********************'
    A_KEY = '********************'
    A_SECRET = '********************'
    tw = OAuth1Session(C_KEY,C_SECRET,A_KEY,A_SECRET)

    url = 'https://api.twitter.com/1.1/search/tweets.json?'
    params = {
    			'q': (search_words, 'utf-8'),
                'lang': 'ja',
    			'count': '1'
    			}
    req = tw.get(url, params = params)

    if req.status_code == 200:
        timeline = json.loads(req.text)
        limit = req.headers['x-rate-limit-remaining']

        for tweet in timeline['statuses']:
            Text = (tweet['text'])
            User = (tweet['user']['screen_name'])
            Name = (tweet['user']['name'])
            Img = (tweet['user']['profile_image_url'])
            Created_at = YmdHMS(tweet['created_at'])


            data = Supermodel()
            data.user_id = User
            data.user_name = Name
            data.user_img = Img
            data.user_text = Text
            data.user_created_at = Created_at
            data.save()

            Message = {
                'Words': search_words,
                'timeline': timeline,
                'API_limit': limit,
                'Text': Text,
                'User': User,
                'Name': Name,
                'Img': Img,
                'Created_at': Created_at,
            }

            return render(request, 'search.html', Message)

    else:
        Error = {
            'Error_message': 'API restricted',
        }
        return render(request, 'search.html', Error)


def mentions(request):

    msg = request.GET.get('words')

    C_KEY = '********************'
    C_SECRET = '********************'
    A_KEY = '********************'
    A_SECRET = '********************'

    url = 'https://api.twitter.com/1.1/statuses/update.json'
    params = {'status': msg,'lang': 'ja'}
    tw = OAuth1Session(C_KEY,C_SECRET,A_KEY,A_SECRET)
    req = tw.post(url, params = params)


    url = 'https://api.twitter.com/1.1/statuses/mentions_timeline.json'
    params = {'count': 1}
    req = tw.get(url, params = params)

    if req.status_code == 200:
        timeline = json.loads(req.text)
        limit = req.headers['x-rate-limit-remaining']

        for tweet in timeline:
            Text = (tweet['text'])
            User = (tweet['user']['screen_name'])
            Name = (tweet['user']['name'])
            Img = (tweet['user']['profile_image_url'])
            Created_at = YmdHMS(tweet['created_at'])

            Message = {
                'Words': msg,
                'timeline': timeline,
                'API_limit': limit,
                'Text': Text,
                'User': User,
                'Name': Name,
                'Img': Img,
                'Created_at': Created_at,
            }

            return render(request, 'mentions.html', Message)

    else:
        Error = {
            'Error_message': 'API restricted',
        }
        return render(request, 'mentions.html', Error)


def YmdHMS(created_at):
    time_utc = time.strptime(created_at, '%a %b %d %H:%M:%S +0000 %Y')
    unix_time = calendar.timegm(time_utc)
    time_local = time.localtime(unix_time)
    return int(time.strftime('%Y%m%d%H%M%S', time_local))

The operation to save to the database is as follows. I think it's simple, easy to understand, and easy to understand what you're doing.

data = Supermodel()
data.user_id = User
data.user_name = Name
data.user_img = Img
data.user_text = Text
data.user_created_at = Created_at
data.save()

Write various HTML files

I'm using bootstrap.

templates/base.html


<!DOCTYPE html>
<html lang="la">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="../static/css/bootstrap.min.css" rel="stylesheet">
    <link href="../static/css/custom.css" rel="stylesheet">
    <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <script src="../static/js/bootstrap.min.js"></script>
    <title></title>
  </head>
  <body>
    {% block body %}
    {% endblock %}
  </body>
</html>

templates/index.html


{% extends "base.html" %}
{% block body %}
  <div class="container">
    <nav class="nav">
      <ul>
        <li><a href="{% url 'index' %}">POST</a></li>
        <li><a href="{% url 'search' %}">SEARCH</a></li>
        <li><a href="{% url 'mentions' %}">MENTIONS</a></li>
      </ul>
    </nav>
    <div class="row">
      <div class="col-md-6">
        <form action="" method="get" class="form-group">
          <label><input type="text" size="60" name="words" class="form-control" placeholder="Tweet"></label>
          <input type="submit" class="btn btn-primary" value="Send">
          <input type="button" class="btn btn-warning" value="Reload" onclick="location.reload();" />
        </form>

        {% if Words %}
        <p>「{{ Words }}I tweeted.</p>
        {% endif %}
        <p class="error-message">{{ Error_message }}</p>
        <p><span class="status">API-Limit:{{ API_limit }}</p>
      </div>
      <script type="text/javascript">
      $(function() {
      $(".reply").click(function() {
        var newText = '@{{ User }} ';
        $(':text[name="words"]').val(newText);
      });
      });
      </script>

      <div class="col-md-6">
        {% if API_limit %}
        <dl>
          <dt><span class="reply"><img class="users-img" src="{{ Img }}">{{ User }}/{{ Name }}</span><span class="d-time">{{ Created_at }}</span></dt>
          <dd>{{ Text }}</dd>
        </dl>
        {% endif %}
      </div>

    </div>
  </div>

{% endblock %}

templates/search.html


{% extends "base.html" %}
{% block body %}
  <div class="container">
    <nav class="nav">
      <ul>
        <li><a href="{% url 'index' %}">POST</a></li>
        <li><a href="{% url 'search' %}">SEARCH</a></li>
        <li><a href="{% url 'mentions' %}">MENTIONS</a></li>
      </ul>
    </nav>
    <div class="row">
      <div class="col-md-6">
        <form action="" method="get" class="form-group">
          <label><input type="text" size="60" name="words" class="form-control" placeholder="Search"></label>
          <input type="submit" class="btn btn-primary" value="Send">
          <input type="button" class="btn btn-warning" value="Reload" onclick="location.reload();" />
        </form>

        {% if Words %}
        <p>「{{ Words }}Was searched for.</p>
        {% endif %}
        <p class="error-message">{{ Error_message }}</p>
        <p><span class="status">API-Limit:{{ API_limit }}</p>
      </div>
      <script type="text/javascript">
      $(function() {
      $(".reply").click(function() {
        var newText = '@{{ User }} ';
        $(':text[name="words"]').val(newText);
      });
      });
      </script>

      <div class="col-md-6">
        {% if API_limit %}
        <dl>
          <dt><span class="reply"><img class="users-img" src="{{ Img }}">{{ User }}/{{ Name }}</span><span class="d-time">{{ Created_at }}</span></dt>
          <dd>{{ Text }}</dd>
        </dl>
        {% endif %}
      </div>

    </div>
  </div>

{% endblock %}

templates/mentions.html


{% extends "base.html" %}
{% block body %}
  <div class="container">
    <nav class="nav">
      <ul>
        <li><a href="{% url 'index' %}">POST</a></li>
        <li><a href="{% url 'search' %}">SEARCH</a></li>
        <li><a href="{% url 'mentions' %}">MENTIONS</a></li>
      </ul>
    </nav>
    <div class="row">
      <div class="col-md-6">
        <form action="" method="get" class="form-group">
          <label><input type="text" size="60" name="words" class="form-control" placeholder="Tweet"></label>
          <input type="submit" class="btn btn-primary" value="Send">
          <input type="button" class="btn btn-warning" value="Reload" onclick="location.reload();" />
        </form>

        {% if Words %}
        <p>「{{ Words }}I tweeted.</p>
        {% endif %}
        <p class="error-message">{{ Error_message }}</p>
        <p><span class="status">API-Limit:{{ API_limit }}</p>
      </div>
      <script type="text/javascript">
      $(function() {
      $(".reply").click(function() {
        var newText = '@{{ User }} ';
        $(':text[name="words"]').val(newText);
      });
      });
      </script>

      <div class="col-md-6">
        {% if API_limit %}
        <dl>
          <dt><span class="reply"><img class="users-img" src="{{ Img }}">{{ User }}/{{ Name }}</span><span class="d-time">{{ Created_at }}</span></dt>
          <dd>{{ Text }}</dd>
        </dl>
        {% endif %}
      </div>

    </div>
  </div>

{% endblock %}

CSS

static/css/custom.css


.container {
  width: 100%;
}

.nav {
  background: rgba(0,0,0,0.7);
  color: #fff;
  width: 100%;
  margin: 0 auto;
}

.nav ul li {
  list-style: none;
  margin: 10px auto;
}

.nav ul {
  margin-left: -20px;
}

.nav li {
  float: left;
  margin-right: 10px!important;
}

.nav li a:link, .nav li a:visited {
  color: #fff!important;
}

.row {
  margin: 40px auto;
}

.status {
  background: #E2264D;
  border-radius: 4px;
  color: #fff;
  padding: 4px;
  width: 100px;
  text-align: center;
  margin-right: 10px;
}

.reply {
  cursor: pointer;
  font-weight: bold;
}

.btn-primary {
  margin-right: 2px;
}

.d-time {
  margin-left: 10px;
  border: 1px solid #d8d8d8;
  border-radius: 4px;
  padding: 4px;
  font-weight: normal;
}

ul li {
  list-style: none;
}

.users-img {
  margin-right: 10px;
  border-radius: 4px;
}

dt {
  margin-bottom: 10px;
}

.error-message {
  color: #ff3300;
}

write urls.py

myapp/urls.py


from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.index, name='index'),
    url(r'^search/', views.search, name='search'),
    url(r'^mentions/', views.mentions, name='mentions'),
]

Write models.py and admin.py

If you do not save the tweet search results in the database, the following is not necessary, but just in case it is described in views.py. We will use SQLITE, so leave the basic settings.py as the default.

myapp/models.py


from django.db import models


class Supermodel(models.Model):
    user_name = models.CharField(max_length=140)
    user_id = models.CharField(max_length=140)
    user_img = models.CharField(max_length=140)
    user_text = models.TextField(null=True)
    user_created_at = models.CharField(max_length=140)

    def __str__(self):
        return self.user_name

myapp/admin.py


from django.contrib import admin
from myapp.models import Supermodel


class SupermodelAdmin(admin.ModelAdmin):
    list_display = ('id','user_id','user_name','user_img','user_text','user_created_at')

admin.site.register(Supermodel,SupermodelAdmin)

Final configuration

スクリーンショット 2016-10-26 13.20.58.png

It looks like this.

After that, please migrate and run server.

Recommended Posts

Your own Twitter client made with Django
Twitter posting application made with Django
Twitter search client made with bottle
Twitter OAuth with Django
Run it in your browser. Twitter client made with bottle (POST only
Create your own Django middleware
Twitter posting client made with Flask with simple login function
Solve your own maze with Q-learning
[Django] Create your own 403, 404, 500 error pages
Train UGATIT with your own dataset
Solve your own maze with DQN
A simple RSS reader made with Django
[Reinforcement learning] DQN with your own library
Simple Slack API client made with Python
Create your own DNS server with Twisted
Create your own Composite Value with SQLAlchemy
To import your own module with jupyter
Create your first app with Django startproject
I made CORS custom middleware with Django
Publish your own Python library with Homebrew
I made a WEB application with Django
Try to make your own AWS-SDK with bash
Until you CI what you made with Django with Jenkins
Argument implementation (with code) in your own language
Divide your data into project-like units with Django (3)
Internationalization with django
I made a Twitter fujoshi blocker with Python ①
Make your own module quickly with setuptools (python)
Divide your data into project-like units with Django
CRUD with Django
Train Stanford NER Tagger with your own data
Make your own music player with Bottle0.13 + jPlayer2.5!
Steps to install your own library with pip
Flow of creating your own package with setup.py with python
Memo to create your own Box with Pepper's Python
I made a Twitter BOT with GAE (python) (with a reference)
Call your own C library with Go using cgo
Write your own activation function with Pytorch (hard sigmoid)
Hit the Twitter API after Oauth authentication with Django
Let's call your own C ++ library with Python (Preferences)
Define your own distance function with k-means of scikit-learn
Web application made with Python3.4 + Django (Part.1 Environment construction)
Single sign-on to your Django application with AWS SSO
Deploy a Django app made with PTVS on Azure
Authenticate Google with Django
Django 1.11 started with Python3.6
Upload files with Django
Create your own exception
Development digest with Django
Output PDF with Django
Container-like # 1 made with C
Markdown output with Django
Use Gentelella with django
Initialize your Django app
Getting Started with Django 1
Container-like # 2 made with C
Send email with Django
File upload with django
Use LESS with Django
Pooling mechanize with Django
Use MySQL with Django