質問編集履歴

2

返り血の追記

2018/10/09 11:04

投稿

bunks
bunks

スコア30

test CHANGED
File without changes
test CHANGED
@@ -90,6 +90,8 @@
90
90
 
91
91
 
92
92
 
93
+ return render(request, 'app/home.html', data)
94
+
93
95
  ```
94
96
 
95
97
 
@@ -274,8 +276,6 @@
274
276
 
275
277
  """
276
278
 
277
- # 長い文章をセンテンス毎に分割
278
-
279
279
  sentences = self._divide(self.text)
280
280
 
281
281
 

1

コードの追記

2018/10/09 11:04

投稿

bunks
bunks

スコア30

test CHANGED
File without changes
test CHANGED
@@ -37,3 +37,757 @@
37
37
  ・Django
38
38
 
39
39
  ・MeCab
40
+
41
+
42
+
43
+ views.py
44
+
45
+ ```
46
+
47
+ def search(request):
48
+
49
+ if request.method == 'GET':
50
+
51
+ query = request.GET.get('your_name').encode('utf-8')
52
+
53
+ s = Scraper(query)
54
+
55
+ sample = s[0]
56
+
57
+ urls = s[1]
58
+
59
+ samples = s[2]
60
+
61
+
62
+
63
+ chain = PrepareChain(sample)
64
+
65
+ triplet_freqs = chain.make_triplet_freqs()
66
+
67
+ chain.save(triplet_freqs, True)
68
+
69
+
70
+
71
+ numb_sentence = int('3') #文章の数
72
+
73
+
74
+
75
+ generator = GenerateText()
76
+
77
+ gen_txt = generator.generate()
78
+
79
+
80
+
81
+ data = {
82
+
83
+ 'your_name': gen_txt,
84
+
85
+ 'urls': urls,
86
+
87
+ 'samples': samples,
88
+
89
+ }
90
+
91
+
92
+
93
+ ```
94
+
95
+
96
+
97
+
98
+
99
+ models.py
100
+
101
+ ```
102
+
103
+ from django.db import models
104
+
105
+ import sqlite3
106
+
107
+ import sys
108
+
109
+
110
+
111
+ #PrepareChain
112
+
113
+ import unittest
114
+
115
+ import re
116
+
117
+ import MeCab
118
+
119
+ from collections import defaultdict
120
+
121
+
122
+
123
+ #GenerateText
124
+
125
+ import os.path
126
+
127
+ import random
128
+
129
+
130
+
131
+ #Scraper
132
+
133
+ from bs4 import BeautifulSoup
134
+
135
+ import urllib3
136
+
137
+ import requests
138
+
139
+ import chardet
140
+
141
+ from urllib.parse import parse_qsl
142
+
143
+ from urllib.parse import urlparse
144
+
145
+ from google import google
146
+
147
+ import ssl
148
+
149
+ ssl._create_default_https_context = ssl._create_unverified_context
150
+
151
+
152
+
153
+
154
+
155
+
156
+
157
+
158
+
159
+ class Text(models.Model):
160
+
161
+ query_text = models.CharField(max_length = 5000)
162
+
163
+
164
+
165
+ def __str__(self):
166
+
167
+ return self.query_text
168
+
169
+
170
+
171
+ class Query(models.Model):
172
+
173
+ query_text = models.CharField(max_length = 40)
174
+
175
+
176
+
177
+ def __str__(self):
178
+
179
+ return self.query_text
180
+
181
+
182
+
183
+ def Scraper(x):
184
+
185
+ #setup
186
+
187
+ urls = []
188
+
189
+ samples = []
190
+
191
+
192
+
193
+ query = x
194
+
195
+ query = query.decode('utf-8')
196
+
197
+ num_page = 1
198
+
199
+ search_results = google.search(query, num_page)
200
+
201
+ for result in search_results:
202
+
203
+ urls.append(result.link)
204
+
205
+ samples.append(result.description)
206
+
207
+
208
+
209
+ print(samples)
210
+
211
+ sample = ''.join(samples)
212
+
213
+
214
+
215
+ #Generating txt file
216
+
217
+ return [sample, urls, samples]
218
+
219
+
220
+
221
+ class PrepareChain(object):
222
+
223
+ """
224
+
225
+ チェーンを作成してDBに保存するクラス
226
+
227
+ """
228
+
229
+
230
+
231
+ BEGIN = "__BEGIN_SENTENCE__"
232
+
233
+ END = "__END_SENTENCE__"
234
+
235
+
236
+
237
+ DB_PATH = "chain.db"
238
+
239
+ DB_SCHEMA_PATH = "schema.sql"
240
+
241
+
242
+
243
+ def __init__(self, text):
244
+
245
+ """
246
+
247
+ 初期化メソッド
248
+
249
+ @param text チェーンを生成するための文章
250
+
251
+ """
252
+
253
+ if isinstance(text, bytes):
254
+
255
+ text = text.decode('utf-8')
256
+
257
+ self.text = text
258
+
259
+
260
+
261
+ # 形態素解析用タガー
262
+
263
+ self.tagger = MeCab.Tagger('-Ochasen')
264
+
265
+
266
+
267
+ def make_triplet_freqs(self):
268
+
269
+ """
270
+
271
+ 形態素解析から3つ組の出現回数まで
272
+
273
+ @return 3つ組とその出現回数の辞書 key: 3つ組(タプル) val: 出現回数
274
+
275
+ """
276
+
277
+ # 長い文章をセンテンス毎に分割
278
+
279
+ sentences = self._divide(self.text)
280
+
281
+
282
+
283
+ # 3つ組の出現回数
284
+
285
+ triplet_freqs = defaultdict(int)
286
+
287
+
288
+
289
+ # センテンス毎に3つ組にする
290
+
291
+ for sentence in sentences:
292
+
293
+ # 形態素解析
294
+
295
+ morphemes = self._morphological_analysis(sentence)
296
+
297
+ # 3つ組をつくる
298
+
299
+ triplets = self._make_triplet(morphemes)
300
+
301
+ # 出現回数を加算
302
+
303
+ for (triplet, n) in list(triplets.items()):
304
+
305
+ triplet_freqs[triplet] += n
306
+
307
+
308
+
309
+ return triplet_freqs
310
+
311
+
312
+
313
+ def _divide(self, text):
314
+
315
+ """
316
+
317
+ 「。」や改行などで区切られた長い文章を一文ずつに分ける
318
+
319
+ @param text 分割前の文章
320
+
321
+ @return 一文ずつの配列
322
+
323
+ """
324
+
325
+ # 改行文字以外の分割文字(正規表現表記)
326
+
327
+ delimiter = "。|.|."
328
+
329
+
330
+
331
+ # 全ての分割文字を改行文字に置換(splitしたときに「。」などの情報を無くさないため)
332
+
333
+ text = re.sub(r"({0})".format(delimiter), r"\1\n", text)
334
+
335
+
336
+
337
+ # 改行文字で分割
338
+
339
+ sentences = text.splitlines()
340
+
341
+
342
+
343
+ # 前後の空白文字を削除
344
+
345
+ sentences = [sentence.strip() for sentence in sentences]
346
+
347
+
348
+
349
+ return sentences
350
+
351
+
352
+
353
+ def _morphological_analysis(self, sentence):
354
+
355
+ """
356
+
357
+ 一文を形態素解析する
358
+
359
+ @param sentence 一文
360
+
361
+ @return 形態素で分割された配列
362
+
363
+ """
364
+
365
+ morphemes = []
366
+
367
+ node = self.tagger.parseToNode(sentence)
368
+
369
+ while node:
370
+
371
+ if node.posid != 0:
372
+
373
+ morpheme = node.surface
374
+
375
+ morphemes.append(morpheme)
376
+
377
+ node = node.next
378
+
379
+ return morphemes
380
+
381
+
382
+
383
+ def _make_triplet(self, morphemes):
384
+
385
+ """
386
+
387
+ 形態素解析で分割された配列を、形態素毎に3つ組にしてその出現回数を数える
388
+
389
+ @param morphemes 形態素配列
390
+
391
+ @return 3つ組とその出現回数の辞書 key: 3つ組(タプル) val: 出現回数
392
+
393
+ """
394
+
395
+ # 3つ組をつくれない場合は終える
396
+
397
+ if len(morphemes) < 3:
398
+
399
+ return {}
400
+
401
+
402
+
403
+ # 出現回数の辞書
404
+
405
+ triplet_freqs = defaultdict(int)
406
+
407
+
408
+
409
+ # 繰り返し
410
+
411
+ for i in range(len(morphemes)-2):
412
+
413
+ triplet = tuple(morphemes[i:i+3])
414
+
415
+ triplet_freqs[triplet] += 1
416
+
417
+
418
+
419
+ # beginを追加
420
+
421
+ triplet = (PrepareChain.BEGIN, morphemes[0], morphemes[1])
422
+
423
+ triplet_freqs[triplet] = 1
424
+
425
+
426
+
427
+ # endを追加
428
+
429
+ triplet = (morphemes[-2], morphemes[-1], PrepareChain.END)
430
+
431
+ triplet_freqs[triplet] = 1
432
+
433
+
434
+
435
+ return triplet_freqs
436
+
437
+
438
+
439
+ def save(self, triplet_freqs, init=False):
440
+
441
+ """
442
+
443
+ 3つ組毎に出現回数をDBに保存
444
+
445
+ @param triplet_freqs 3つ組とその出現回数の辞書 key: 3つ組(タプル) val: 出現回数
446
+
447
+ """
448
+
449
+ # DBオープン
450
+
451
+ con = sqlite3.connect(PrepareChain.DB_PATH)
452
+
453
+
454
+
455
+ # 初期化から始める場合
456
+
457
+ if init:
458
+
459
+ # DBの初期化
460
+
461
+ with open(PrepareChain.DB_SCHEMA_PATH, "r") as f:
462
+
463
+ schema = f.read()
464
+
465
+ con.executescript(schema)
466
+
467
+
468
+
469
+ # データ整形
470
+
471
+ datas = [(triplet[0], triplet[1], triplet[2], freq) for (triplet, freq) in triplet_freqs.items()]
472
+
473
+
474
+
475
+ # データ挿入
476
+
477
+ p_statement = "insert into chain_freqs (prefix1, prefix2, suffix, freq) values (?, ?, ?, ?)"
478
+
479
+ con.executemany(p_statement, datas)
480
+
481
+
482
+
483
+ # コミットしてクローズ
484
+
485
+ con.commit()
486
+
487
+ con.close()
488
+
489
+
490
+
491
+ def show(self, triplet_freqs):
492
+
493
+ """
494
+
495
+ 3つ組毎の出現回数を出力する
496
+
497
+ @param triplet_freqs 3つ組とその出現回数の辞書 key: 3つ組(タプル) val: 出現回数
498
+
499
+ """
500
+
501
+ for triplet in triplet_freqs:
502
+
503
+ print("|".join(triplet), "\t", triplet_freqs[triplet])
504
+
505
+
506
+
507
+
508
+
509
+ class GenerateText(object):
510
+
511
+ """
512
+
513
+ 文章生成用クラス
514
+
515
+ """
516
+
517
+
518
+
519
+ def __init__(self, n=5):
520
+
521
+ """
522
+
523
+ 初期化メソッド
524
+
525
+ @param n いくつの文章を生成するか
526
+
527
+ """
528
+
529
+ self.n = n
530
+
531
+
532
+
533
+ def generate(self):
534
+
535
+ """
536
+
537
+ 実際に生成する
538
+
539
+ @return 生成された文章
540
+
541
+ """
542
+
543
+ # DBが存在しないときは例外をあげる
544
+
545
+ if not os.path.exists(PrepareChain.DB_PATH):
546
+
547
+ raise IOError("DBファイルが存在しません")
548
+
549
+
550
+
551
+ # DBオープン
552
+
553
+ con = sqlite3.connect(PrepareChain.DB_PATH)
554
+
555
+ con.row_factory = sqlite3.Row
556
+
557
+
558
+
559
+ # 最終的にできる文章
560
+
561
+ generated_text = ""
562
+
563
+
564
+
565
+ # 指定の数だけ作成する
566
+
567
+ for i in range(self.n):
568
+
569
+ text = self._generate_sentence(con)
570
+
571
+ generated_text += text
572
+
573
+
574
+
575
+ # DBクローズ
576
+
577
+ con.close()
578
+
579
+
580
+
581
+ return generated_text
582
+
583
+
584
+
585
+ def _generate_sentence(self, con):
586
+
587
+ """
588
+
589
+ ランダムに一文を生成する
590
+
591
+ @param con DBコネクション
592
+
593
+ @return 生成された1つの文章
594
+
595
+ """
596
+
597
+ # 生成文章のリスト
598
+
599
+ morphemes = []
600
+
601
+
602
+
603
+ # はじまりを取得
604
+
605
+ first_triplet = self._get_first_triplet(con)
606
+
607
+ morphemes.append(first_triplet[1])
608
+
609
+ morphemes.append(first_triplet[2])
610
+
611
+
612
+
613
+ # 文章を紡いでいく
614
+
615
+ while morphemes[-1] != PrepareChain.END:
616
+
617
+ prefix1 = morphemes[-2]
618
+
619
+ prefix2 = morphemes[-1]
620
+
621
+ triplet = self._get_triplet(con, prefix1, prefix2)
622
+
623
+ morphemes.append(triplet[2])
624
+
625
+
626
+
627
+ # 連結
628
+
629
+ result = "".join(morphemes[:-1])
630
+
631
+
632
+
633
+ return result
634
+
635
+
636
+
637
+ def _get_chain_from_DB(self, con, prefixes):
638
+
639
+ """
640
+
641
+ チェーンの情報をDBから取得する
642
+
643
+ @param con DBコネクション
644
+
645
+ @param prefixes チェーンを取得するprefixの条件 tupleかlist
646
+
647
+ @return チェーンの情報の配列
648
+
649
+ """
650
+
651
+ # ベースとなるSQL
652
+
653
+ sql = "select prefix1, prefix2, suffix, freq from chain_freqs where prefix1 = ?"
654
+
655
+
656
+
657
+ # prefixが2つなら条件に加える
658
+
659
+ if len(prefixes) == 2:
660
+
661
+ sql += " and prefix2 = ?"
662
+
663
+
664
+
665
+ # 結果
666
+
667
+ result = []
668
+
669
+
670
+
671
+ # DBから取得
672
+
673
+ cursor = con.execute(sql, prefixes)
674
+
675
+ for row in cursor:
676
+
677
+ result.append(dict(row))
678
+
679
+
680
+
681
+ return result
682
+
683
+
684
+
685
+ def _get_first_triplet(self, con):
686
+
687
+ """
688
+
689
+ 文章のはじまりの3つ組をランダムに取得する
690
+
691
+ @param con DBコネクション
692
+
693
+ @return 文章のはじまりの3つ組のタプル
694
+
695
+ """
696
+
697
+ # BEGINをprefix1としてチェーンを取得
698
+
699
+ prefixes = (PrepareChain.BEGIN,)
700
+
701
+
702
+
703
+ # チェーン情報を取得
704
+
705
+ chains = self._get_chain_from_DB(con, prefixes)
706
+
707
+
708
+
709
+ # 取得したチェーンから、確率的に1つ選ぶ
710
+
711
+ triplet = self._get_probable_triplet(chains)
712
+
713
+
714
+
715
+ return (triplet["prefix1"], triplet["prefix2"], triplet["suffix"])
716
+
717
+
718
+
719
+ def _get_triplet(self, con, prefix1, prefix2):
720
+
721
+ """
722
+
723
+ prefix1とprefix2からsuffixをランダムに取得する
724
+
725
+ @param con DBコネクション
726
+
727
+ @param prefix1 1つ目のprefix
728
+
729
+ @param prefix2 2つ目のprefix
730
+
731
+ @return 3つ組のタプル
732
+
733
+ """
734
+
735
+ # BEGINをprefix1としてチェーンを取得
736
+
737
+ prefixes = (prefix1, prefix2)
738
+
739
+
740
+
741
+ # チェーン情報を取得
742
+
743
+ chains = self._get_chain_from_DB(con, prefixes)
744
+
745
+
746
+
747
+ # 取得したチェーンから、確率的に1つ選ぶ
748
+
749
+ triplet = self._get_probable_triplet(chains)
750
+
751
+
752
+
753
+ return (triplet["prefix1"], triplet["prefix2"], triplet["suffix"])
754
+
755
+
756
+
757
+ def _get_probable_triplet(self, chains):
758
+
759
+ """
760
+
761
+ チェーンの配列の中から確率的に1つを返す
762
+
763
+ @param chains チェーンの配列
764
+
765
+ @return 確率的に選んだ3つ組
766
+
767
+ """
768
+
769
+ # 確率配列
770
+
771
+ probability = []
772
+
773
+
774
+
775
+ # 確率に合うように、インデックスを入れる
776
+
777
+ for (index, chain) in enumerate(chains):
778
+
779
+ for j in range(chain["freq"]):
780
+
781
+ probability.append(index)
782
+
783
+
784
+
785
+ # ランダムに1つを選ぶ
786
+
787
+ chain_index = random.choice(probability)
788
+
789
+
790
+
791
+ return chains[chain_index]
792
+
793
+ ```