神社 地域 にデジタルを
❝世界のニュースを少し知る❞のコーディング
Django4 python3.10 wsl bootstrap googletrans gtts feedparser pydub
googletransとgttsを使って実装しました。
どちらもインストールするだけで使えるのでお手軽です。ただし、googletransはバージョンによっては正しく動作しないので注意が必要です。
gttsは、日本語の発話が自然に感じられました。
ニュースソースは公開されているRSSからスクレイピングする方法を選択。
音声ファイル(mp.3)をDBには保存しない形で、サイトに表示させる方法や、ダウンロードできるようにする機能実装にかなり苦戦しました。
当初の意図の1/3程度ではありますが、それなりの動きになりました。
ただし動作はかなり遅いです。概要を記します。
事前準備(手順の概略)
<Poetryで仮想環境を準備(例)>事前にPoetryをインストールしておく
仮想環境をフォルダ直下にする
poetry config virtualenvs.in-project true
~$ mkdir o_poetry_dj
~$ cd o_poetry_dj/
~/o_poetry_dj$ poetry init
以下の設問にEnter、yes、noを入力
This command will guide you through creating your pyproject.toml config.
Package name [o_poetry_dj]:※Enterキー
Version [0.1.0]:※Enterキー
Description []:※Enterキー
Author [user , n to skip]:※Enterキー
License []:※Enterキー
Compatible Python versions [^3.10]:※Enterキー
Would you like to define your main dependencies interactively? (yes/no) [yes] no
Would you like to define your development dependencies interactively? (yes/no) [yes] no
Generated file
[tool.poetry]
name = "o_poetry_dj"
version = "0.1.0"
description = ""
authors = ["user "]
[tool.poetry.dependencies]
python = "^3.10"
[tool.poetry.dev-dependencies]
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
Do you confirm generation? (yes/no) [yes] yes
poetry shell
<venvの場合(例)>※venvがインストールされていることを確認
python3 -m venv app_text_process
source app_o_ytdl/bin/activate
以後、仮想環境で作業
必要なパッケージをインストール
<poetryの場合>
(仮想名) poetry add feedparser googletrans=="4.0.0-rc1" gtts pydub...
<pipの場合>
(仮想名) pip install googletrans=="4.0.0-rc1" gtts pydub...
Djangoプロジェクトにアプリを作成
python manage.py startapp text_process
スーパーユーザーを作成
python manage.py createsuperuser
googletransは、4.0.0-rc1で動作が安定した。
settings.py(config/settings.py)
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
"text_process",
...省略...
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / "templates"],
...省略...
STATIC_URL = 'static/'
STATICFILES_DIRS = [BASE_DIR / "static"]
MEDIA_URL = 'media/'
MEDIA_ROOT = BASE_DIR / 'media'
プロジェクトフォルダのsettings.pyに記述
urls.py(config/urls.py)
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path("text_process/", include("text_process.urls")),
...省略...
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
プロジェクトフォルダのurls.pyに記述
フォルダ構成
├── text_process/
│ ├── admin.py
│ ├── apps.py
│ ├── forms.py
│ ├── __init__.py
│ ├── migrations/
│ ├── models.py
│ ├── __pycache__/
│ ├── tests.py
│ ├── urls.py
│ ├── utils_news.py
│ ├── utils.py
│ └── views.py
スクレイピング、和訳、音声合成の記述をするutils_news.pyを配置します。
utils_news.py
import feedparser
from googletrans import Translator
from gtts import gTTS
from io import BytesIO
from pydub import AudioSegment
from pydub.playback import play
import base64
import urllib.parse
def get_feed(rss_url):
"""rssフィードからニュースをゲット"""
url = rss_url
feed = feedparser.parse(url)
t_list = []
ja_text = ""
for entry in feed.entries[:5]:
j_text = trans_sentence(str(entry.title))
ja_text += "。" + j_text
t_list.append([j_text, entry.title, entry.link])
return t_list, ja_text
def trans_sentence(text):
translator = Translator()
message = translator.translate(text, src="en", dest="ja")
return message.text
def speak_trans(text):
tts = gTTS(text=text, lang="ja", slow=False)
# convert to file-like object
fp = BytesIO()
tts.write_to_fp(fp)
fp.seek(0)
song = AudioSegment.from_file(fp, format="mp3")
# Assuming 'audio_segment' is your AudioSegment object
audio_segment = song
# Export AudioSegment to a byte array in WAV format
audio_data = audio_segment.export(format='mp3').read()
# Encode the byte array as base64
base64_audio = base64.b64encode(audio_data).decode('utf-8')
song_64 = "data:audio/mpeg;base64," + urllib.parse.quote_plus(base64_audio)
return song_64
- RSSのスクレイピング。14~26行目
- 和訳。29~32行目
- 音声合成。36~58行目
base64での合成音声を戻す(returnさせます)
以下のサイト参照しました。
参照:https://blog.furas.pl/python-how-to-play-mp3-from-gtts-as-bytes-without-saving-on-disk-gb.html
音声合成に関しては以下のサイトを参考にさせていただきました。
参照:https://self-development.info/%E3%80%90python%E3%81%A7%E9%9F%B3%E5%A3%B0%E5%90%88%E6%88%90%EF%BC%88%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E8%AA%AD%E3%81%BF%E4%B8%8A%E3%81%92%EF%BC%89%E3%80%91gtts%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9/
forms.py
from django import forms
class NewsChoiceForm(forms.Form):
DATA_CHOICE = (
("rss_bbc", "BBC"),
("rss_cnn", "CNN"),
("rss_nyt", "NewyorkTimes")
)
cho = forms.ChoiceField(label="選択中のメディア:ニュース5件", choices=DATA_CHOICE)
Choiceフィールドを持つシンプルなフォームを作ります。
views.py
from django.shortcuts import render
from .forms import NewsChoiceForm
from .utils_news import get_feed, speak_trans
def news_home(request):
form = NewsChoiceForm()
choice = request.GET.get("cho")
if choice == "rss_bbc":
choice_val = "http://feeds.bbci.co.uk/news/world/rss.xml"
elif choice == "rss_cnn":
choice_val = "http://rss.cnn.com/rss/money_news_international.rss"
elif choice == "rss_nyt":
choice_val = "https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml"
else:
choice_val = "http://feeds.bbci.co.uk/news/world/rss.xml"
feed_list, all = get_feed(choice_val)
sound = speak_trans(all)
context = {
"news": feed_list,
"all": all,
"sound": sound,
"form": form,
}
return render(request, "text_process/news.html", context)
templates/base.html
※雛形のイメージhtmlです 用途に合わせて手直してください
{% load static %}
<!doctype html>
<html lanh=ja>
<head>
<!-- Require meta tags -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0", shrink-to-fit=none>
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
<!-- custom css & js -->
<link rel="stylesheet" href="{% static 'style.css' %}">
<script src="{% static 'main.js' %}" defer></script>
<title>Spiner | {% block title %}{% endblock title %}</title>
</head>
<body>
<div class="container mt-3">
{% block content %}
{% endblock content %}
</div>
<!-- Optional Javascript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS-->
<script
src="https://code.jquery.com/jquery-3.6.3.min.js"
integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU="
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.9.2/dist/umd/popper.min.js" integrity="sha384-IQsoLXl5PILFhosVNubq5LC7Qb9DXgDA9i+tQ8Zj3iwWAwPtgFTxbJ8NT4GN1R8p" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.min.js" integrity="sha384-cVKIPhGWiC2Al4u+LWgxfKTRIcfu0JTxR+EQDz/bgldoEyl4H0zUF0QKbrJ0EcQF" crossorigin="anonymous"></script>
</body>
</html>
templates/text_process/news.htmll
JavascriptでGetパラメータを取り出す処理を記述しています。
{% extends 'base.html' %}
{% load static %}
{% block title %}世界のニュースを少し知る-読み上げ-{% endblock title %}
{% block contents %}
<div class="row">
<div class="col">
<h3 class="mb-3">世界のニュースを少し知る-読み上げ-</h3>
<form method="GET" action="{% url 'text_process:news' %}" name="news_form">
{{ form.as_p }}
{% comment %} {% for field in form %}
<div class="form-group">
{{ field.label_tag }}
{% render_field field class="form-control" style="border:none;" %}
{% if field.help_text %}
<small class="form-text text-muted">
{{ field.help_text }}
</small>
{% endif %}
</div>
{% endfor %} {% endcomment %}
<button class="btn btn-dark rounded-capsule mb-3" onclick="clickBtn1()">表示</button>
</form>
<p>gTTs 和訳を読み上げ</p>
<audio controls>
<source src="{{sound}}" type="audio/mpeg">
Your browser does not support audio
</audio>
<table class="table mt-3">
<thead>
<tr>
<th scope="col">和訳</th>
<th scope="col">Title</th>
<th scope="col">URL</th>
</tr>
</thead>
<tbody>
{% for value in news %}
<tr>
<td>{{value.0}}</td>
<td>{{value.1}}</td>
<td>{{value.2}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock contents %}
{% block end_scripts %}
<script>
// getパラメータからselectboxのテキストを抜き出す
// URLのクエリストリングを取得
const queryString = window.location.search;
// URLSearchParamsオブジェクトを作成
const urlParams = new URLSearchParams(queryString);
// selectboxパラメータの値を取得
const selectboxValue = urlParams.get('cho');
console.log(selectboxValue)
// selectboxのoption要素を取得
const selectbox = document.getElementById('id_cho');
// 選択肢と一致するものを選択
for (let i = 0; i < selectbox.options.length; i++) {
if (selectbox.options[i].value === selectboxValue) {
selectbox.options[i].selected = true;
const txt = selectbox.options[i].text;
console.log(txt)
break;
}
}
</script>
{% endblock end_scripts %}
投稿内容 ランダム表示
❝地図とグラフで見る大阪府データ❞の使い方
最終更新:2023年07月14日
❝世界のニュースを少し知る❞のコーディング
最終更新:2023年07月14日
❝地図とグラフで見る豊中市データ❞のコーディング
最終更新:2023年09月06日
豊中市MAPの基データの説明
最終更新:2023年07月14日
python3.10 Django4 wsl bootstrap javascript
カテゴリ
私の願い
私は神社の宮司です。神社や地域を担う次世代の人々に対し、何かを残してお役に立ててもらいたいとの願いが、強く芽生えました。個業としての神社や、小規模な地域社会に、恩恵が届くのが遅くなりそうな「デジタル」の分野。門外漢として奮闘した実体験から得た経験則を、わずかずつでも残し未来につなぎたいと願うばかりです。
最近の投稿
- 簡易で安価なカメラで防犯・外出対策を
最終更新:2023年09月08日
- 神社のオリジナルTシャツを作ってみた
最終更新:2023年08月07日
- HTMLメールを活用してみた
最終更新:2023年07月14日
- 豊中市Graphの基データの説明
最終更新:2023年07月14日