質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Mecab

Mecabは、オープンソースの形態素解析エンジンです。 言語、辞書、コーパスに依存しない汎用的な設計を基本方針としています。 Mecabの由来は、開発者の好物である和布蕪(めかぶ)から名づけられました。

VPS

VPS(バーチャル・プライベート・サーバ)は、仮想化されたサーバをレンタルするサービスで、共有サーバでありながら専門サーバと同等の機能を果たします。物理的な専門サーバより安価で提供できるメリットがあります。

Q&A

0回答

1166閲覧

VPS上での文章生成:素材文によってBad Gatewayが生じることがある

bunks

総合スコア30

Django

DjangoはPythonで書かれた、オープンソースウェブアプリケーションのフレームワークです。複雑なデータベースを扱うウェブサイトを開発する際に必要な労力を減らす為にデザインされました。

Python 3.x

Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

Mecab

Mecabは、オープンソースの形態素解析エンジンです。 言語、辞書、コーパスに依存しない汎用的な設計を基本方針としています。 Mecabの由来は、開発者の好物である和布蕪(めかぶ)から名づけられました。

VPS

VPS(バーチャル・プライベート・サーバ)は、仮想化されたサーバをレンタルするサービスで、共有サーバでありながら専門サーバと同等の機能を果たします。物理的な専門サーバより安価で提供できるメリットがあります。

0グッド

0クリップ

投稿2018/10/31 12:06

編集2018/10/31 12:24

問題

VPS上でGoogleの検索結果のdescriptionを元に文章を作るプログラムを以下のようなコードで作成しているのですが,queryによってエラーが発生します。

Scraperでdescriptionを取得し,PrepareChainおよびGenerateTextで文章を作るのですが,
例えば,queryが
・1単語(ex. ”りんご”)→問題なし
・2単語(ex. ”りんご 製品”)→ほぼ問題なし
・3単語(ex. ”りんご 製品 良い”)→502 Bad Gateway多発
・サイト内検索(ex. ”site:yahoo.co.jp りんご”)→502 Bad Gateway多発
のようになります。

queryによってエラーの多寡が変わるので,views.pyのchain = PrepareChain(sample)より下,文章生成の部分をコメントアウトしてみたのですが,Scraperでurlやdescriptionをとるところまでは問題がないようでした。(どのqueryでもエラーなし)
これより,問題はPrepareChainやGenerateTextを実行するにあたって生じているのかと考えています。

いずれのqueryにおいても抽出されるdescriptionに大差はないと思うのですが,なぜqueryによってエラーが出たり出なかったりするのでしょうか?
対策と合わせて原因を教えていただきたいです。

なおローカルでrunserverする場合は,ほぼどのパターンでも問題なく,VPSより早く動きます。

環境

・さくらVPS
・Django
・MeCab
googleモジュール

コード

views

1def search(request): 2 if request.method == 'GET': 3 query = request.GET.get('your_name').encode('utf-8') 4 try: 5 page_num = request.GET.get('page_num').encode('utf-8') 6 page_num = int(page_num) 7 except: 8 page_num = 1 9 10 s = Scraper(query, page_num) 11 sample = s[0] 12 urls = s[1] 13 samples = s[2] 14 15 chain = PrepareChain(sample) 16 triplet_freqs = chain.make_triplet_freqs() 17 chain.save(triplet_freqs, True) 18 19 generator = GenerateText() 20 try: 21 sentence_num = request.GET.get('sentence_num').encode('utf-8') 22 generator.n = int(sentence_num) 23 except: 24 generator.n = 15 25 gen_txt = generator.generate() 26 27 data = { 28 'your_name': gen_txt, 29 'urls': urls, 30 'samples': samples, 31 } 32 33 return render(request, 'app/webapp.html', data) 34

models

1from django.db import models 2import sqlite3 3import sys 4 5#PrepareChain 6import unittest 7import re 8import MeCab 9from collections import defaultdict 10 11#GenerateText 12import os.path 13import random 14 15#Scraper 16from bs4 import BeautifulSoup 17import urllib3 18import requests 19import chardet 20from urllib.parse import parse_qsl 21from urllib.parse import urlparse 22from google import google 23import ssl 24ssl._create_default_https_context = ssl._create_unverified_context 25 26 27class Text(models.Model): 28 query_text = models.CharField(max_length = 5000) 29 30 def __str__(self): 31 return self.query_text 32 33class Query(models.Model): 34 query_text = models.CharField(max_length = 40) 35 36 def __str__(self): 37 return self.query_text 38 39def Scraper(query, page_num): 40 #setup 41 urls = [] 42 samples = [] 43 44 query = query.decode('utf-8') 45 print(query) 46 search_results = google.search(query, page_num) 47 for result in search_results: 48 urls.append(result.link) 49 samples.append(result.description) 50 51 sample = ''.join(samples) 52 53 #Generating txt file 54 return [sample, urls, samples] 55 56class PrepareChain(object): 57 58 BEGIN = "__BEGIN_SENTENCE__" 59 END = "__END_SENTENCE__" 60 61 DB_PATH = "chain.db" 62 DB_SCHEMA_PATH = "schema.sql" 63 64 def __init__(self, text): 65 66 if isinstance(text, bytes): 67 text = text.decode('utf-8') 68 self.text = text 69 70 # 形態素解析用タガー 71 self.tagger = MeCab.Tagger('-Ochasen') 72 73 def make_triplet_freqs(self): 74 75 # 長い文章をセンテンス毎に分割 76 sentences = self._divide(self.text) 77 78 # 3つ組の出現回数 79 triplet_freqs = defaultdict(int) 80 81 # センテンス毎に3つ組にする 82 for sentence in sentences: 83 # 形態素解析 84 morphemes = self._morphological_analysis(sentence) 85 # 3つ組をつくる 86 triplets = self._make_triplet(morphemes) 87 # 出現回数を加算 88 for (triplet, n) in list(triplets.items()): 89 triplet_freqs[triplet] += n 90 91 return triplet_freqs 92 93 def _divide(self, text): 94 95 # 改行文字以外の分割文字(正規表現表記) 96 delimiter = "。|.|." 97 98 # 全ての分割文字を改行文字に置換(splitしたときに「。」などの情報を無くさないため) 99 text = re.sub(r"({0})".format(delimiter), r"\1\n", text) 100 101 # 改行文字で分割 102 sentences = text.splitlines() 103 104 # 前後の空白文字を削除 105 sentences = [sentence.strip() for sentence in sentences] 106 107 return sentences 108 109 def _morphological_analysis(self, sentence): 110 111 morphemes = [] 112 node = self.tagger.parseToNode(sentence) 113 while node: 114 if node.posid != 0: 115 try: 116 morpheme = node.surface 117 morphemes.append(morpheme) 118 except: 119 continue 120 node = node.next 121 return morphemes 122 123 def _make_triplet(self, morphemes): 124 125 # 3つ組をつくれない場合は終える 126 if len(morphemes) < 3: 127 return {} 128 129 # 出現回数の辞書 130 triplet_freqs = defaultdict(int) 131 132 # 繰り返し 133 for i in range(len(morphemes)-2): 134 triplet = tuple(morphemes[i:i+3]) 135 triplet_freqs[triplet] += 1 136 137 # beginを追加 138 triplet = (PrepareChain.BEGIN, morphemes[0], morphemes[1]) 139 triplet_freqs[triplet] = 1 140 141 # endを追加 142 triplet = (morphemes[-2], morphemes[-1], PrepareChain.END) 143 triplet_freqs[triplet] = 1 144 145 return triplet_freqs 146 147 def save(self, triplet_freqs, init=False): 148 149 # DBオープン 150 con = sqlite3.connect(PrepareChain.DB_PATH) 151 152 # 初期化から始める場合 153 if init: 154 # DBの初期化 155 with open(PrepareChain.DB_SCHEMA_PATH, "r") as f: 156 schema = f.read() 157 con.executescript(schema) 158 159 # データ整形 160 datas = [(triplet[0], triplet[1], triplet[2], freq) for (triplet, freq) in triplet_freqs.items()] 161 162 # データ挿入 163 p_statement = "insert into chain_freqs (prefix1, prefix2, suffix, freq) values (?, ?, ?, ?)" 164 con.executemany(p_statement, datas) 165 166 # コミットしてクローズ 167 con.commit() 168 con.close() 169 170 def show(self, triplet_freqs): 171 172 for triplet in triplet_freqs: 173 print("|".join(triplet), "\t", triplet_freqs[triplet]) 174 175 176class GenerateText(object): 177 """ 178 文章生成用クラス 179 """ 180 181 def __init__(self): 182 """ 183 初期化メソッド 184 @param n いくつの文章を生成するか 185 """ 186 self.n = 5 187 188 def generate(self): 189 """ 190 実際に生成する 191 @return 生成された文章 192 """ 193 # DBが存在しないときは例外をあげる 194 if not os.path.exists(PrepareChain.DB_PATH): 195 raise IOError("DBファイルが存在しません") 196 197 # DBオープン 198 con = sqlite3.connect(PrepareChain.DB_PATH) 199 con.row_factory = sqlite3.Row 200 201 # 最終的にできる文章 202 generated_text = "" 203 204 # 指定の数だけ作成する 205 for i in range(self.n): 206 text = self._generate_sentence(con) 207 generated_text += text 208 209 # DBクローズ 210 con.close() 211 212 return generated_text 213 214 def _generate_sentence(self, con): 215 """ 216 ランダムに一文を生成する 217 @param con DBコネクション 218 @return 生成された1つの文章 219 """ 220 # 生成文章のリスト 221 morphemes = [] 222 223 # はじまりを取得 224 first_triplet = self._get_first_triplet(con) 225 morphemes.append(first_triplet[1]) 226 morphemes.append(first_triplet[2]) 227 228 # 文章を紡いでいく 229 while morphemes[-1] != PrepareChain.END: 230 prefix1 = morphemes[-2] 231 prefix2 = morphemes[-1] 232 triplet = self._get_triplet(con, prefix1, prefix2) 233 morphemes.append(triplet[2]) 234 235 # 連結 236 result = "".join(morphemes[:-1]) 237 238 return result 239 240 def _get_chain_from_DB(self, con, prefixes): 241 """ 242 チェーンの情報をDBから取得する 243 @param con DBコネクション 244 @param prefixes チェーンを取得するprefixの条件 tupleかlist 245 @return チェーンの情報の配列 246 """ 247 # ベースとなるSQL 248 sql = "select prefix1, prefix2, suffix, freq from chain_freqs where prefix1 = ?" 249 250 # prefixが2つなら条件に加える 251 if len(prefixes) == 2: 252 sql += " and prefix2 = ?" 253 254 # 結果 255 result = [] 256 257 # DBから取得 258 cursor = con.execute(sql, prefixes) 259 for row in cursor: 260 result.append(dict(row)) 261 262 return result 263 264 def _get_first_triplet(self, con): 265 """ 266 文章のはじまりの3つ組をランダムに取得する 267 @param con DBコネクション 268 @return 文章のはじまりの3つ組のタプル 269 """ 270 # BEGINをprefix1としてチェーンを取得 271 prefixes = (PrepareChain.BEGIN,) 272 273 # チェーン情報を取得 274 chains = self._get_chain_from_DB(con, prefixes) 275 276 # 取得したチェーンから、確率的に1つ選ぶ 277 triplet = self._get_probable_triplet(chains) 278 279 return (triplet["prefix1"], triplet["prefix2"], triplet["suffix"]) 280 281 def _get_triplet(self, con, prefix1, prefix2): 282 """ 283 prefix1とprefix2からsuffixをランダムに取得する 284 @param con DBコネクション 285 @param prefix1 1つ目のprefix 286 @param prefix2 2つ目のprefix 287 @return 3つ組のタプル 288 """ 289 # BEGINをprefix1としてチェーンを取得 290 prefixes = (prefix1, prefix2) 291 292 # チェーン情報を取得 293 chains = self._get_chain_from_DB(con, prefixes) 294 295 # 取得したチェーンから、確率的に1つ選ぶ 296 triplet = self._get_probable_triplet(chains) 297 298 return (triplet["prefix1"], triplet["prefix2"], triplet["suffix"]) 299 300 def _get_probable_triplet(self, chains): 301 """ 302 チェーンの配列の中から確率的に1つを返す 303 @param chains チェーンの配列 304 @return 確率的に選んだ3つ組 305 """ 306 # 確率配列 307 probability = [] 308 309 # 確率に合うように、インデックスを入れる 310 for (index, chain) in enumerate(chains): 311 for j in range(chain["freq"]): 312 probability.append(index) 313 314 # ランダムに1つを選ぶ 315 chain_index = random.choice(probability) 316 317 return chains[chain_index] 318

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問