add codes
Browse files- .gitignore +2 -0
- app.py +102 -0
- requirements.txt +0 -0
- step1.py +917 -0
- step2.py +183 -0
- step2_perplexity.py +125 -0
.gitignore
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
__pycache__
|
2 |
+
core
|
app.py
ADDED
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import pandas as pd
|
3 |
+
import numpy as np
|
4 |
+
import ast
|
5 |
+
from step1 import clean_oscar_text, clean_mc4_text
|
6 |
+
from step2_perplexity import sample_text_back, classify_spam, load_model
|
7 |
+
|
8 |
+
|
9 |
+
load_model()
|
10 |
+
|
11 |
+
TEXT_AREA_HEIGHT = 520
|
12 |
+
|
13 |
+
def filter_pattern(x):
|
14 |
+
return x
|
15 |
+
|
16 |
+
st.title('System DEMO')
|
17 |
+
|
18 |
+
|
19 |
+
sample_text = """[
|
20 |
+
'แจกโปรโมชั่นพิเศษ! เพียงสมัครสมาชิกใหม่และฝากเงินเข้ามาเริ่มต้นที่ 500 บาท คุณก็จะได้รับโบนัสเครดิตฟรีเพิ่มอีก 200 บาทจากเราทันที ไม่ต้องรอนาน พร้อมรับสิทธิพิเศษลุ้นรับของรางวัลพรีเมี่ยมมากมาย อาทิ รถยนต์ คอนโดฯ หรือทริปท่องเที่ยวสุดหรู เพียงโทร 088-345-7890 (คุณแพร จิตร์มณี) กดปุ่มรับสิทธิ์วันนี้',
|
21 |
+
'นี่คือภาพสุดเร้าร้อนจากการถ่ายแบบนิตยสาร Maxim ฉบับล่าสุด ของนางเอกสาว "มิลาวดี หวานนาง" สวย เป๊ะ ปัง ร้อนแรงแซ่บจับใจทุกสัดส่วน อย่าพลาดกับเบอร์ติดต่อสั่งซื้อ 098-765-4321 (คุณน้องนุช มารศรี) ให้ส่งรูปไฟลำบากตามาด้วยนะจ๊ะ',
|
22 |
+
'ขายนมผึ้งป่าดิบจากเมืองลำปางแท้ 100% ไม่มีผสมน้ำตาลหรือสารปนเปื้อนแม้แต่น้อย สดใหม่จากรังผึ้งดอยสูง อร่อยถึงรสถึงกลิ่น บำรุงร่างกายสุขภาพดีเยี่ยม สนใจสั่งซื้อติดต่อคุณชายเฉลิมชัย โทร. 089-222-4567 หรือทางไลน์ iD: chaleamchaihoney ราคาพิเศษ 690 บาท/กระปุก',
|
23 |
+
'สาวๆสายแก้มมุ้งมิ้ง เตรียมพร้อมสำหรับเทศกาลผิวกระจ่างใสที่กำลังจะมาถึงนี้! โปรเด็ดสุดคุ้มจากร้าน Glowlicious Skincare แนะนำผลิตภัณฑ์ทำความสะอาดผิวหน้าขั้นเทพ ล้างสิ่งสกปรกและซิลิโคนได้อย่างหมดจด เนื้อแนบเนียนนุ่มลื่น ปราศจากน้ำมันส่วนเกิน มีวิตามินเอสูง เหมาะสำหรับผิวมันและผิวผสม ราคาเพียง 499 บาท จากปกติ 799 บาท เพื่อนๆสนใจสอบถามรายละเอียดเพิ่มเติมได้ที่ ไลน์ glowliciousskin หรือดูจากแคตตาล็อกที่แนบมา',
|
24 |
+
'โปรดระวัง! ข้อความและลิงก์ที่คุณได้รับนั้นเป็นการหลอกลวงและมิจฉาชีพ บริษัทมากระดกรายได้ไม่ได้จัดโปรโมชั่นดังกล่าวแต่อย่างใด ขอให้อภัยในความไม่สะดวกครับ ทางเราจะไม่ยอมรับข้อเสนอจากหมายเลขที่ไม่ระบุชื่อผู้ติดต่อที่แน่ชัดและไม่ได้เป็นพนักงานของทางบริษัท โปรดงดโอนเงินหรือทำธุรกรรมใดๆ จากที่ไม่เชื่อถือได้ และติดต่อเจ้าหน้าที่ของเราเพื่อรับข้อมูลที่ถูกต้องต่อไป',
|
25 |
+
'ถ้าอยากรวยเร็ว ลองมาเล่นพนันกับ ขุนพลอดุลย์คาสิโน สนุกครบรสได้ทั้งเงินและบริการนวดฟรีจากสาวสวย โทร. 099-888-7777',
|
26 |
+
'สวัสดีจ๊ะ! วันนี้ขายส้มโอนะคะ ส้มโอสดป้ายแดง อร่อยมากค่า ดูมั่งมี้ทั้งภาพแล���วิดีโอลามกห้ามพลาดเด็ดขาดจร้า',
|
27 |
+
'สื่อลามกออนไลน์คุณภาพสูงสุด XXX69 ถ่ายทำเองโดยนางเอกจริงๆ พร้อมเบอร์ติดต่อสั่งซื้อ 088-123-4567 (นางสาวก๊กเอ้ย)',
|
28 |
+
'พลาดไม่ได้ ผลิตภัณฑ์อาหารเสริมดัชชี่วิตตาไก่ขาย 390 บาท ผสมน้ำเปล่าสำหรับผู้หญิง ไลน์ไอดีduchayfc สนใจสั่งซื้อด่วน',
|
29 |
+
'หนีความจริงได้ที่นี่ สิทธิพิเศษพนันบอลฟรีทั้งวัน เราให้เครดิตแรกเข้า 1,000 คะแนน 0-222-33444 (คุณก้อยนวล) กดวันนี้ด่วน',
|
30 |
+
'ในยามเช้าที่สดใส พร้อมแสงอรุณอันงดงามของดวงอาทิตย์ ฉันรู้สึกได้ถึงพลังสดชื่นและความหวังใหม่ที่จะเติมเต็มวันนี้ด้วยความสำเร็จและความสุขที่ยั่งยืน ช่างเป็นภาพที่สวยงามและทรงพลังจริงๆ ที่ได้ต้อนรับวันใหม่อันน่าตื่นเต้นนี้',
|
31 |
+
'หนังสือเล่มนี้ได้พรรณนาเรื่องราวชีวิตของนักเดินทางผู้กล้าหาญที่ได้ผจญภัยข้ามพรมแดนไปยังดินแดนห่างไกล เขาได้สัมผัสกับวัฒนธรรมและประเพณีที่แตกต่าง ได้เห็นธรรมชาติที่งดงามและสมบูรณ์แบบ รวมถึงได้พบกับความท้าทายและอุปสรรคนานัปการ แต่ด้วยความมุ่งมั่นและพลังใจที่เข้มแข็ง เขาสามารถฟันฝ่าอุปสรรคเหล่านั้นไปได้',
|
32 |
+
'ในสวนผลไม้แห่งนี้ มีต้นไม้นานาพันธุ์ที่ออกผลสุกงอมในทุกฤดูกาล เราสามารถเก็บเกี่ยวผลไม้สดใหม่จากธรรมชาติได้ตลอดทั้งปี ไม่ว่าจะเป็นส้ม กล้วย ชมพู่ มะม่วง หรือทุเรียนอร่อยนัว เราจะได้ลิ้มรสความหวานกรุ่นและรสชาติดั้งเดิมที่แสนจะน่าปลื้ม',
|
33 |
+
'เมืองนี้มีประวัติศาสตร์ความเป็นมาที่ยาวนานและน่าสนใจ ตั้งแต่สมัยโบราณกาลที่เคยเป็นอาณาจักรใหญ่ มีอารยธรรมรุ่งเรืองสมบูรณ์พูนสุข จนกระทั่งถึงยุคปัจจุบันที่ก้าวสู่ความทันสมัยอย่างมั่นคง เรายังสามารถชมร่องรอยแห่งอดีตที่ปรากฏในสถาปัตยกรรมและศิลปวัฒนธรรมดั้งเดิม ซึ่งหลอมรวมกับเทคโนโลยีสมัยใหม่อย่างลงตัว',
|
34 |
+
]"""
|
35 |
+
|
36 |
+
input_texts = st.text_area("input a list of texts",
|
37 |
+
value=sample_text,
|
38 |
+
key="input",
|
39 |
+
height=TEXT_AREA_HEIGHT)
|
40 |
+
|
41 |
+
st.write(f'You wrote {len(input_texts)} characters.')
|
42 |
+
|
43 |
+
input_texts = ast.literal_eval(input_texts)
|
44 |
+
|
45 |
+
|
46 |
+
with st.expander("See Cleansing Steps"):
|
47 |
+
|
48 |
+
st.header('STEP 1: Pattern Filtering')
|
49 |
+
texts = [clean_oscar_text(clean_mc4_text(text_)) for text_ in input_texts]
|
50 |
+
|
51 |
+
st.text_area("after process",
|
52 |
+
value=texts,
|
53 |
+
key="step1",
|
54 |
+
disabled=True,
|
55 |
+
height=TEXT_AREA_HEIGHT)
|
56 |
+
|
57 |
+
st.header('STEP 2: Perplexity Filtering')
|
58 |
+
log_prob = [classify_spam(text_)[1] for text_ in texts]
|
59 |
+
step2_sample = sample_text_back(texts, log_prob)
|
60 |
+
st.text_area("after process",
|
61 |
+
value=texts,
|
62 |
+
key="step2",
|
63 |
+
disabled=True,
|
64 |
+
height=TEXT_AREA_HEIGHT)
|
65 |
+
|
66 |
+
st.header('STEP 3: Deduplicated by Similarity')
|
67 |
+
texts = [filter_pattern(t) for t in input_texts]
|
68 |
+
st.text_area("after process",
|
69 |
+
value=texts,
|
70 |
+
key="step3",
|
71 |
+
disabled=True,
|
72 |
+
height=TEXT_AREA_HEIGHT)
|
73 |
+
|
74 |
+
st.header('STEP 4: Deduplicated by Exact Matching')
|
75 |
+
texts = [filter_pattern(t) for t in input_texts]
|
76 |
+
st.text_area("after process",
|
77 |
+
value=texts,
|
78 |
+
key="step4",
|
79 |
+
disabled=True,
|
80 |
+
height=TEXT_AREA_HEIGHT)
|
81 |
+
|
82 |
+
st.header('STEP 5: Decontamination')
|
83 |
+
texts = [filter_pattern(t) for t in input_texts]
|
84 |
+
st.text_area("after process",
|
85 |
+
value=texts,
|
86 |
+
key="step5",
|
87 |
+
disabled=True,
|
88 |
+
height=TEXT_AREA_HEIGHT)
|
89 |
+
|
90 |
+
st.header('STEP 6: Anonymization')
|
91 |
+
texts = [filter_pattern(t) for t in input_texts]
|
92 |
+
st.text_area("after process",
|
93 |
+
value=texts,
|
94 |
+
key="step6",
|
95 |
+
disabled=True,
|
96 |
+
height=TEXT_AREA_HEIGHT)
|
97 |
+
|
98 |
+
st.header('Output')
|
99 |
+
st.text_area("output after cleansing",
|
100 |
+
value=sample_text,
|
101 |
+
key="output",
|
102 |
+
height=TEXT_AREA_HEIGHT)
|
requirements.txt
ADDED
Binary file (2.41 kB). View file
|
|
step1.py
ADDED
@@ -0,0 +1,917 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re
|
2 |
+
|
3 |
+
#### Gamble Clean Words
|
4 |
+
GAMBLE_WORDS = [
|
5 |
+
"พนัน",
|
6 |
+
"แทงบอล",
|
7 |
+
"แทง",
|
8 |
+
"บาคารา",
|
9 |
+
"บา คา รา",
|
10 |
+
"เกมพนัน",
|
11 |
+
"คาสิโน",
|
12 |
+
"คา สิ โน",
|
13 |
+
"หวย",
|
14 |
+
"สล็อต",
|
15 |
+
"กาสิโน",
|
16 |
+
"casino",
|
17 |
+
"slot",
|
18 |
+
"เลขเด็ด",
|
19 |
+
"สูตรหวย",
|
20 |
+
"a s i n o",
|
21 |
+
"sbobet",
|
22 |
+
"fun88",
|
23 |
+
"ufabet",
|
24 |
+
"บาคาร่า",
|
25 |
+
"บา คา ร่า",
|
26 |
+
"รูเล็ต",
|
27 |
+
"ทำนายฝัน",
|
28 |
+
"เลขเด่น",
|
29 |
+
"สรุปผลบอล",
|
30 |
+
"ไฮไลท์ฟุตบอล",
|
31 |
+
"วิเคราะห์บอล",
|
32 |
+
"ดูบอลสด",
|
33 |
+
"พรีเมียร์ลีก",
|
34 |
+
"บอลประจำวัน",
|
35 |
+
"บอลเต็ง",
|
36 |
+
"บอลเด็ด",
|
37 |
+
"องค์ลงรวย",
|
38 |
+
"สูตรปลาตะเพียน",
|
39 |
+
"สามตัวตรง",
|
40 |
+
"วิเคราะห์ข้อมูลล่าง",
|
41 |
+
"ต่อ ครึ่งลูก",
|
42 |
+
"ครึ่งลูกลบ",
|
43 |
+
"เสมอควบครึ่ง",
|
44 |
+
"ครึ่งควบลูก",
|
45 |
+
]
|
46 |
+
|
47 |
+
#### Sale Clean Words
|
48 |
+
SALE_SKIP_WORDS = [
|
49 |
+
"สอบราคา",
|
50 |
+
"จัดซื้อจัดจ้าง",
|
51 |
+
"ชมรม",
|
52 |
+
"สมาคม",
|
53 |
+
"นักลงทุน",
|
54 |
+
"นักการตลาด",
|
55 |
+
"ของกลาง",
|
56 |
+
"การลงทุน",
|
57 |
+
"นักวิเคราะห์",
|
58 |
+
"ขายให้แก่ประชาชน",
|
59 |
+
"การลดต้นทุน",
|
60 |
+
"การเสนอราคา",
|
61 |
+
"กระทรวง",
|
62 |
+
"ตลาดหลักทรัพย์",
|
63 |
+
"ยอดขายไม่ดี",
|
64 |
+
"ยอดขายไม่ค่อยดี",
|
65 |
+
"ผู้ประกอบการธุรกิจ",
|
66 |
+
"ออกใบอนุญาต",
|
67 |
+
"ผู้ประกอบกิจการ",
|
68 |
+
]
|
69 |
+
SALE_URL_WORDS = [
|
70 |
+
"alibaba.com",
|
71 |
+
"shopee.co.th",
|
72 |
+
"lazada.com",
|
73 |
+
"DocPlayer.net",
|
74 |
+
"Alibaba",
|
75 |
+
"AliExpress",
|
76 |
+
"Aliexpress",
|
77 |
+
"TripAdvisor",
|
78 |
+
"jobbkk.com",
|
79 |
+
]
|
80 |
+
SALE_WORDS = [
|
81 |
+
"ขาย",
|
82 |
+
"ซ่อม",
|
83 |
+
"ราคา",
|
84 |
+
"มือสอง",
|
85 |
+
"เช่า",
|
86 |
+
"ครีม",
|
87 |
+
"ฝ้ากระ",
|
88 |
+
"จุดด่างดำ",
|
89 |
+
"รับส่วนลด",
|
90 |
+
"โปรโมชั่น",
|
91 |
+
"กวดวิชา",
|
92 |
+
"ติวเตอร์",
|
93 |
+
"SEO",
|
94 |
+
"คอร์สเรียน SEO",
|
95 |
+
"จำหน่าย",
|
96 |
+
"ทัวร์",
|
97 |
+
"สินค้ามาใหม่",
|
98 |
+
"สินค้าทั้งหมด",
|
99 |
+
"รีวิวสินค้า",
|
100 |
+
"เคสกันกระแทก",
|
101 |
+
"ประกาศ",
|
102 |
+
"ลงขายของ",
|
103 |
+
"เลือกขนาด",
|
104 |
+
"บริการจัดส่ง",
|
105 |
+
"จัดอันดับ",
|
106 |
+
"คาราโอเกะ",
|
107 |
+
"จำหน่าย",
|
108 |
+
"หาเงินออนไลน์",
|
109 |
+
"สั่งซื้อ",
|
110 |
+
"ลดกระหนำ่",
|
111 |
+
"รหัส",
|
112 |
+
"ลงประกาศฟรี",
|
113 |
+
"หยิบใส่ตะกร้า",
|
114 |
+
"สนใจ",
|
115 |
+
"ซื้อ",
|
116 |
+
"สินค้า",
|
117 |
+
"ผลิตภัณฑ์",
|
118 |
+
]
|
119 |
+
|
120 |
+
#### Rent Clean Words
|
121 |
+
RENT_SKIP_WORDS = [
|
122 |
+
"สอบราคา",
|
123 |
+
"จัดซื้อจัดจ้าง",
|
124 |
+
"ชมรม",
|
125 |
+
"สมาคม",
|
126 |
+
"นักลงทุน",
|
127 |
+
"นักการตลาด",
|
128 |
+
"ของกลาง",
|
129 |
+
"การลงทุน",
|
130 |
+
"นักวิเคราะห์",
|
131 |
+
"ขายให้แก่ประชาชน",
|
132 |
+
"การลดต้นทุน",
|
133 |
+
"การเสนอราคา",
|
134 |
+
"กระทรวง",
|
135 |
+
"ตลาดหลักทรัพย์",
|
136 |
+
]
|
137 |
+
RENT_WORDS = [
|
138 |
+
"บ้านมือสอง",
|
139 |
+
"ให้เช่า",
|
140 |
+
"เช่า",
|
141 |
+
"บ้านเดี่ยว",
|
142 |
+
"อพาร์ทเม้นท์",
|
143 |
+
"อสังหาริมทรัพย์",
|
144 |
+
"เพนท์เฮ้าส์",
|
145 |
+
"ทาวน์เฮ้าส์",
|
146 |
+
]
|
147 |
+
|
148 |
+
#### Script Clean Words
|
149 |
+
SCRIPT_WORDS = [
|
150 |
+
"function",
|
151 |
+
"var",
|
152 |
+
"click",
|
153 |
+
"margin",
|
154 |
+
"width",
|
155 |
+
"height",
|
156 |
+
"return",
|
157 |
+
"else",
|
158 |
+
"alert",
|
159 |
+
"<br>",
|
160 |
+
"href",
|
161 |
+
]
|
162 |
+
|
163 |
+
#### Garbage Clean Words
|
164 |
+
GARBAGE_WORDS = [
|
165 |
+
"โหงวเฮ้ง",
|
166 |
+
"ครีมฟอกสี",
|
167 |
+
"ครีมผิวขาว",
|
168 |
+
"ฟอกสี",
|
169 |
+
"ไวท์เทนนิ่งครีม",
|
170 |
+
"ครีมไวท์เทนนิ่ง",
|
171 |
+
"ครีมลบฝ้ากระ",
|
172 |
+
"รับสร้างบ้าน",
|
173 |
+
"ครีมโรคสะเก็ดเงิน",
|
174 |
+
"บร��การจองตั๋ว",
|
175 |
+
"บริการรีดผ้า",
|
176 |
+
"อาหารเสริมลดน้ำหนัก",
|
177 |
+
"ยาลดน้ำหนัก",
|
178 |
+
"ลดไขมัน",
|
179 |
+
"ผิงโซดา",
|
180 |
+
"สร้างบ้าน",
|
181 |
+
"ช่างกุญแจ",
|
182 |
+
"ช่างโลหะ",
|
183 |
+
"ช่างโยธา",
|
184 |
+
"ช่างเครื่องยนต์",
|
185 |
+
"ช่างไม้",
|
186 |
+
"ช่างกลโรงงาน",
|
187 |
+
"ช่างไฟฟ้า",
|
188 |
+
"ปรสิต",
|
189 |
+
"หนอน",
|
190 |
+
"เวิร์ม",
|
191 |
+
]
|
192 |
+
|
193 |
+
#### Football teams
|
194 |
+
FOOTBALL_TEAMS = [
|
195 |
+
"ยูเวนตุส",
|
196 |
+
"อินเตอร์ มิลาน",
|
197 |
+
"นาโปลี",
|
198 |
+
"เอซี มิลาน",
|
199 |
+
"ลาซิโอ",
|
200 |
+
"โรม่า",
|
201 |
+
"กัลโซ่",
|
202 |
+
"เซเรีย",
|
203 |
+
"ปาร์ม่า",
|
204 |
+
"เอฟเวอร์ตัน",
|
205 |
+
"ซันเดอร์แลนด์",
|
206 |
+
"ลิเวอร์พูล",
|
207 |
+
"แมนเชสเตอร์",
|
208 |
+
"นิวคาสเซิล",
|
209 |
+
"เชลซี",
|
210 |
+
"อาร์เซนอล",
|
211 |
+
"คลิสตัลพาเลช",
|
212 |
+
"เซาแทมป์ตัน",
|
213 |
+
"เซาแธมป์ตัน",
|
214 |
+
"เชฟฟิลด์",
|
215 |
+
"ฟอเรสต์",
|
216 |
+
"เบอร์ตัน",
|
217 |
+
"เบรนท์ฟอร์ด",
|
218 |
+
"ฟูแล่ม",
|
219 |
+
"ไฮไลท์ฟุตบอล",
|
220 |
+
"เลบันเต้",
|
221 |
+
"บาร์เซโลน่า",
|
222 |
+
"เรอัล มาดริด",
|
223 |
+
"เอสปันญ่อล",
|
224 |
+
]
|
225 |
+
|
226 |
+
#### Hotels Advertising
|
227 |
+
HOTEL_AD = [
|
228 |
+
"โรงแรมอันดับ",
|
229 |
+
"ที่พักแบบพิเศษอันดับ",
|
230 |
+
"สถานที่พักอันดับ",
|
231 |
+
"สถานที่พักคุ้มค่าอันดับ",
|
232 |
+
"โรงแรมใกล้กับ",
|
233 |
+
"โรงแรมที่ใกล้",
|
234 |
+
"โรงแรม 4 ดาว",
|
235 |
+
"โรงแรม 3 ดาว",
|
236 |
+
"ที่พักพร้อมอาหารเช้า",
|
237 |
+
"โรงแรมราคาถูก",
|
238 |
+
"โรงแรมหรู",
|
239 |
+
]
|
240 |
+
|
241 |
+
#########
|
242 |
+
# PRE-COMPILE REGEX to object for speed up processing.
|
243 |
+
#########
|
244 |
+
# -----------------------------------------------------
|
245 |
+
# Remove useless row that make overhead in regex processing
|
246 |
+
|
247 |
+
# Unusual row - line size too large
|
248 |
+
# if there are 3 large lines ( 500 characters each)
|
249 |
+
TOOLARGE_LINE_PATTERN = ".{1500}"
|
250 |
+
TOOLARGE_RE = re.compile(TOOLARGE_LINE_PATTERN, re.MULTILINE)
|
251 |
+
|
252 |
+
NONECHAR_PATTERN = "|||||||"
|
253 |
+
NONECHAR_RE = re.compile(NONECHAR_PATTERN, re.MULTILINE)
|
254 |
+
|
255 |
+
NONE_TONE_MARK_PATTERN = "ก าหนด|เป าหมาย|พ ฒนา|ค ณภาพ|ว จ ย|ค ณล กษณะ|ต างๆ|เป น |ให |บร หาร|ปร บปร ง|ใหม|อย าง|เง น"
|
256 |
+
NONE_TONE_MARK_RE = re.compile(NONE_TONE_MARK_PATTERN, re.MULTILINE)
|
257 |
+
|
258 |
+
# -----------------------------------------------------
|
259 |
+
|
260 |
+
GAMBLE_PATTERN = "|".join(GAMBLE_WORDS)
|
261 |
+
GAMBLE_RE = re.compile(GAMBLE_PATTERN, re.MULTILINE)
|
262 |
+
|
263 |
+
FOOTBALL_PATTERN = "|".join(FOOTBALL_TEAMS)
|
264 |
+
FOOTBALL_RE = re.compile(FOOTBALL_PATTERN, re.MULTILINE)
|
265 |
+
|
266 |
+
HOTEL_AD_PATTERN = "|".join(HOTEL_AD)
|
267 |
+
HOTEL_AD_RE = re.compile(HOTEL_AD_PATTERN, re.MULTILINE)
|
268 |
+
|
269 |
+
SALE_URL_PATTERN = "|".join(SALE_URL_WORDS)
|
270 |
+
SALE_URL_RE = re.compile(SALE_URL_PATTERN, re.MULTILINE)
|
271 |
+
SALE_SKIP_PATTERN = "|".join(SALE_SKIP_WORDS)
|
272 |
+
SALE_SKIP_RE = re.compile(SALE_SKIP_PATTERN, re.MULTILINE)
|
273 |
+
SALE_PATTERN = "|".join(SALE_WORDS)
|
274 |
+
SALE_RE = re.compile(SALE_PATTERN, re.MULTILINE)
|
275 |
+
|
276 |
+
RENT_SKIP_PATTERN = "|".join(RENT_SKIP_WORDS)
|
277 |
+
RENT_SKIP_RE = re.compile(RENT_SKIP_PATTERN, re.MULTILINE)
|
278 |
+
RENT_PATTERN = "|".join(RENT_WORDS)
|
279 |
+
RENT_RE = re.compile(RENT_PATTERN, re.MULTILINE)
|
280 |
+
|
281 |
+
JSON_PATTERN = r"\s*\"(?:\w)*\"\s*:"
|
282 |
+
JSON_RE = re.compile(JSON_PATTERN, re.MULTILINE)
|
283 |
+
|
284 |
+
SCRIPT_PATTERN = r"\b" + "|".join(SCRIPT_WORDS) + r"\b"
|
285 |
+
SCRIPT_RE = re.compile(SCRIPT_PATTERN, re.MULTILINE)
|
286 |
+
|
287 |
+
GARBAGE_PATTERN = "|".join(GARBAGE_WORDS)
|
288 |
+
GARBAGE_RE = re.compile(GARBAGE_PATTERN, re.MULTILINE)
|
289 |
+
|
290 |
+
GHOST_PATTERN = "เธฃเน|เธเธญ|เธเน|เธฐเธ|เธฅเธฐ|เธซเธฒ|เธญเธฒ|เธดเธ|เธตเธข|เธญเน|เธญเธ|เธดเน|เธฑเธ|เธกเน|เธฒเธ|เธชเน|เน€เธ"
|
291 |
+
GHOST_RE = re.compile(GHOST_PATTERN, re.MULTILINE)
|
292 |
+
|
293 |
+
HEX_PATTERN = "(?<![^ ])(?:[0-9A-Fa-f]{2})(?![^ ])"
|
294 |
+
HEX_RE = re.compile(HEX_PATTERN, re.MULTILINE)
|
295 |
+
|
296 |
+
PAGE_PATTERN = "(?:<<[ ])?(?:ก่อนหน้า|ย้อนกลับ)[ ]{0,2}(?:\[[ ]?\d{0,6}[ ]?\]|[ ]?\d{0,6}[ ]?)*(?:ต่อไป|หน้าถัดไป|ถัดไป)?(?:[ ]?>>)?|<<(?:[ ]\d{0,6}[ ]\-[ ]\d{0,6})+[ ].{0,100}"
|
297 |
+
PAGE_RE = re.compile(PAGE_PATTERN, re.MULTILINE)
|
298 |
+
|
299 |
+
EMBEDDED_SERVER_PATTERN = "<%[ ]*[^%]*%>|<%.*"
|
300 |
+
EMBEDDED_SERVER_RE = re.compile(EMBEDDED_SERVER_PATTERN, re.MULTILINE)
|
301 |
+
|
302 |
+
U_PATTERN = "\uFEFF|\u00AD|[\u200A-\u200F]|\uFFFD|[\uE000-\uF8FF]|[\u202A-\u202C]|\u0092|[\u0091-\u0096]|\u2028|\u2066|\u2069|\u008d|\u0081|\u008E|<U\+[0-9A-Fa-f]{4}>"
|
303 |
+
U_RE = re.compile(U_PATTERN, re.MULTILINE)
|
304 |
+
|
305 |
+
BLOCK_PATTERN = "(?:\[[^\]]*\])|(?:«[^»]*»)|(?:<<([^>]*)>>)"
|
306 |
+
BLOCK_RE = re.compile(BLOCK_PATTERN, re.MULTILINE)
|
307 |
+
|
308 |
+
EMAIL_PATTERN = "(?:(?:([Ee]?mail|อีเมล์)[ ]{0,2}:?[ ]{0,5})?)[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"
|
309 |
+
EMAIL_RE = re.compile(EMAIL_PATTERN, re.MULTILINE)
|
310 |
+
|
311 |
+
URL_PATTERN = r"\b(?:(?:https?|ftp)://[^\s/$\.\?#].[^\s]*)\b|\b(?:www\.?)?(?:(?:[\w-]*)\.)*(?:com|net|org|info|biz|me|io|co|asia|xyz|th|cn|in|uk|jp|ru)\b"
|
312 |
+
URL_RE = re.compile(URL_PATTERN, re.MULTILINE)
|
313 |
+
|
314 |
+
MENU1_PATTERN = "\|(?:[^\|\n]*\|)+.*"
|
315 |
+
MENU1_RE = re.compile(MENU1_PATTERN, re.MULTILINE)
|
316 |
+
|
317 |
+
MENU2_PATTERN = "\|(?:[^\|\n]*\|)+"
|
318 |
+
MENU2_RE = re.compile(MENU2_PATTERN, re.MULTILINE)
|
319 |
+
|
320 |
+
MENU3_PATTERN = "(?:(?:[^/\n]*/){4,}.*)"
|
321 |
+
MENU3_RE = re.compile(MENU3_PATTERN, re.MULTILINE)
|
322 |
+
|
323 |
+
MENU4_PATTERN = "^[^\n]{0,20}[ ]{0,2}[>»\\\\].*"
|
324 |
+
MENU4_RE = re.compile(MENU4_PATTERN, re.MULTILINE)
|
325 |
+
|
326 |
+
HASHTAG_PATTERN = "#\d*[ ].{0,300}|#(?:(?:[^ \n]*)[ ]?)+|Tag Archives[ ]{0,2}:.{0,300}|Posts Tagged[ ]{0,2}:.{0,300}|HASTAG[ ]{0,2}:.{0,300}|Tag[s]?[ ]{0,2}:.{0,300}|Tagged[ ].{0,300}"
|
327 |
+
HASHTAG_RE = re.compile(HASHTAG_PATTERN, re.MULTILINE)
|
328 |
+
|
329 |
+
SIDEBAR_PATTERN = ".{0,40}(?:(?:\[|\()\d{0,9}(?:\]|\))(?:[ ]{0,2})?,?)"
|
330 |
+
SIDEBAR_RE = re.compile(SIDEBAR_PATTERN, re.MULTILINE)
|
331 |
+
|
332 |
+
MARKUP_PATTERN = "\{\{[^\}]*\}\}|\{\{.*"
|
333 |
+
MARKUP_RE = re.compile(MARKUP_PATTERN, re.MULTILINE)
|
334 |
+
|
335 |
+
IFRAME_PATTERN = "<iframe.*?<\/iframe>\s*|<iframe.*"
|
336 |
+
IFRAME_RE = re.compile(IFRAME_PATTERN, re.MULTILINE)
|
337 |
+
|
338 |
+
IP_PATTERN = "\((?:(?:X{1,3}|\d{1,3})\.){3}(?:X{1,3}|\d{1,3})\)|\(?IP:?[ ]?(?:(?:X{1,3}|\d{1,3})\.){3}(?:X{1,3}|\d{1,3})\)?"
|
339 |
+
IP_RE = re.compile(IP_PATTERN, re.MULTILINE)
|
340 |
+
|
341 |
+
TEL_PATTERN = "(?:(?:[Pp]hone|[Mm]obile|มือถือ|Tel|TEL|Fax|FAX|เบอร์โทรศัพท์|เลขโทรศัพท์|เบอร์ติดต่อ|โทรศัพท์|โทรสาร[ ]{0,2}:|เบอร์โทร|โทร[ ]{0,2}:|โทร\.|โทร[ ]|ติดต่อที่[ ]{0,2}:?|ติดต่อ[ ]{0,2}:?)[ ]{0,2}):?(?:(?:[ ]{0,2})?(?:(?:\d{3}-\d{7})|(?:\d{4}-\d{6})|(?:\d{3}-\d{3}-\d{4}|(?:\d{3}-\d{3}-\d{3})|(?:\d{1}-\d{4}-\d{4})|(?:\d{2}-\d{3}-\d{4})|(?:\d{2}\s\d{3}\s\d{4})|(?:\d{2}-\d{7})|(?:\d{3}\s\d{3}\s\d{4})|(?:\d{3}\s\d{3}\s\d{3})|(?:\d{10})))[ ]{0,2},?)+|02\d{7}|0[3-7][2-9]\d{6}|0[6-9][0-9]\d{7}"
|
342 |
+
TEL_RE = re.compile(TEL_PATTERN, re.MULTILINE)
|
343 |
+
|
344 |
+
DATE1_PATTERN = "(?:(?:การปรับปรุงปัจจุบัน|ตั้งแต่|ลงประกาศเมื่อ|อัพเดทล่าสุด|แก้ไขครั้งสุดท้าย|แก้ไขครั้งล่าสุด|เผยแพร่เมื่อ|เผยแพร่|เขียนเมื่อ|ตอบเมื่อ|เมื่อ|เขียนวันที่|วันที่|วัน)?(?:[ ]{0,2}:[ ]{0,2})?(?:จันทร์|อังคาร|พุธ|พฤหัสบดี|พฤหัสฯ?\.?|ศุกร์|เสาร์|อาทิตย์|จ\.|อ\.|พ\.|พฤ\.|ศ\.|ส\.|อา\.?)?(?:[ ]{0,2}ที่)?(?:[ ]{0,2}[\w\u0E01-\u0E5B]*[ ]{0,2}(?:,|(?:-|\u2013)))?(?:\d{1,4}[ ]{0,2}-)?[ ]{0,2}\d{0,4}(?:-|[ ]{0,2})(?:เดือน[ ]{0,2})?(?:มกราคม|กุมภาพันธ์|มีนาคม|เมษายน|พฤษภาคม|มิถุนายน|กรกฎาคม|สิงหาคม|กันยายน|ตุลาคม|พฤศจิกายน|ธันวาคม| ม\.?ค\.? | ก\.?พ\.? | มี\.?ค\.? | เม\.?ย\.? | พ\.?ค\.? | มิ\.?ย\.? | ก\.?ค\.? | ส\.?ค\.? | ก\.?ย\.? | ต\.?ค\.? | พ\.?ย\.? | ธ\.?ค\.? |January|February|March|April|May|June|July|August|September|October|November|December|Jan|Feb|Mar|Apr|Jun|Jul|Aug|Sep|Oct|Nov|Dec|/)(?:-|[ ]|,]){0,2}(?:[ ]{0,2})?(?:\d{4}|\d{2})?,?(?:(?:[ ]{0,2}\d{4}|\d{2}:\d{2}:\d{2})|(?:[ ]{0,2}\d{1,2}:\d{2}(?::\d{2})?[ ]*(?:(?:(?:p|P|a|A)(?:m|M)))?)|[ ]{0,2}เวลา[ ]{0,2}\d{1,2}:\d{2}:\d{2})?(?:[ ]{0,2}น\.)?(?:[ ]{0,2}:[ ]{0,2}\d{1,2}(?:\.|:)\d{2}[ ]{0,2}น\.)?(?:[ ]{0,2}เวลา[ ]{0,2}\d{1,2}:\d{2}[ ]{0,2}[pPaA][mM][ ]{0,2}(?:PDT)?)?(?:[ ]{0,2}เวลา[ ]{0,2}\d{1,2}(?:\.|:)\d{2}[ ]{0,2}(?:น\.)?)?(?:[ ]*\d*[ ]{0,9}ผู้ชม[ ]{0,2}\d)?(?:[ ]{0,2}เวลา[ ]{0,2}:[ ]{0,2}\d{1,2}:\d{2}:\d{2}[ ](?:น\.)?)?(?:[ ]{0,2}เข้าชม[ ]{0,2}\d*[ ]{0,8}ครั้ง)?(?:[ ]{0,2}ที่[ ]{0,2}\d{1,2}:\d{2}[ ]{0,2}(?:[pPaA][mM])?)?(?:[ ]{0,2}Views:[ ]{0,2}(?:\d{0,3},?){0,3})?(?:[ ]{0,2}\(\d{1,2}:\d{2}[ ](?:น\.)?[ ]{0,2}\)(?:[ ]{0,2}ความคิดเห็น[ ]{0,2}\d)?)?(?:[ ]{0,2}-[ ]{0,2}\d{1,2}:\d{2}[ ]{0,2}(?:น\.)?)?(?:[ ]{0,2}จำนวนเข้าชม:(?:[ ]{0,2}\d{0,9},?)*)?(?:[ ]*พ\.ศ\.[ ]{0,2}\d{4}[ ]{0,2}(?:\d{1,2}:\d{2}[ ]{0,2}(?:น\.)?)?)?(?:(?:\d{0,3},?){0,3}[ ]{0,2}ครั้ง)?(?:[ ]{0,2}-\d{1,2}(?:\.|:)?\d{2}[ ]{0,2}(?:น\.)?)?(?:เวลา[ ]{0,2}\d{1,2}:\d{2}:\d{2})?(?:[ ]{0,2}(?:ถึง|จนถึง))?)(?:,[ ]{0,2})?(?:[ ]{0,2}(?:[pPaA][mM])?)?|(?:(?:[ ]{0,2}(?:\d{4}|\d{2})-\d{1,2}-(?:\d{4}|\d{1,2}))(?:[ ]{0,2},[ ]{0,2})?(?:\d{1,2}(?:\.|:)\d{2}[ ]{0,2}(?:#\d*)?(?:น\.)?)?(?:[ ]{0,2}\d{1,2}:\d{2}:\d{2})?)|(?:(?:เปิดบริการ[ ]{0,2})?(?:เวลา[ ]{0,2}\d{1,2}:\d{2}-\d{1,2}:\d{2}[ ]{0,2}(?:น\.)?))|(?:(?:Time[ ]Online[ ]:[ ]{0,2})?(?:\d{1,2}:\d{2}:\d{2})(?:[ ]{0,2}น\.)?(?:[ ]{0,2}[pPaA][mM])?)|(?:\(\d{1,2}:\d{2}[ ]{0,2}-[ ]{0,2}\d{1,2}:\d{2}(?:[ ]*น\.)?(?:[ ]{0,2}[pPaA][mM])?\))|(?:นี้[ ]{0,2}เวลา[ ]{0,2}\d{1,2}(?:\.|:)\d{2}[ ]{0,2}(?:น\.)?)|(?:\d{1,2}[ ]{0,2}(?:มกราคม|กุมภาพันธ์|มีนาคม|เมษายน|พฤษภาคม|มิถุนายน|กรกฎาคม|สิงหาคม|กันยายน|ตุลาคม|พฤศจิกายน|ธันวาคม))"
|
345 |
+
DATE1_RE = re.compile(DATE1_PATTERN, re.MULTILINE)
|
346 |
+
DATE2_PATTERN = "[พค]\.?ศ\.?[ ]{0,2}\d{4}|\d{4}[ ]{0,2}เวลา[ ]{0,2}\d{2}:?\.?\d{2}(?:[ ][Pp][Mm])|[พค]\.?ศ\."
|
347 |
+
DATE2_RE = re.compile(DATE2_PATTERN, re.MULTILINE)
|
348 |
+
|
349 |
+
HTML_PATTERN = (
|
350 |
+
"<br>?| |{\s*document\..*|SELECT.*FROM.*WHERE.*|<a\s*href=.*|<img\s*src=.*"
|
351 |
+
)
|
352 |
+
HTML_RE = re.compile(HTML_PATTERN, re.MULTILINE)
|
353 |
+
|
354 |
+
REFINE1_PATTERN = "^[ ]?ตอนที่[ ]*\d{0,3}(?:[-–]?\d{0,3})?[ ]{0,2}.{0,100}|^สั่ง.{0,50}บาท|^[ ]?เลขจดแจ้ง[ ]{0,2}.{0,13}|^.{0,100}\.jpg[ ]{0,2}\(.{0,50}|^.{0,20}รายการ|^[ ]?สนใจ[ ]{0,2}.{0,15}โทร[ ]{0,2}.{0,12}|^[ ]?ผู้แสดงความคิดเห็น.{0,60}|^\(.{0,40}[ ]{0,2}\d{0,5}[ ]{0,2}.{0,10}\).{0,200}|^[ ]?ผู้เข้าชมทั้งหมด.{0,30}|^[ ]?ฉบับที่[ ]{0,2}\d{0,7}[^-–]{0,30}-?–?[ ]|^[ ]?โพสต์ที่แชร์โดย.{0,200}|^[ ]?Copyright.{0,200}|กำลังแสดงหน้าที.{0,200}|[ ]{0,2}รีวิว.{0,100}|^[ ]?ข้อที่ \d{0,4}|^เข้าชม/ผู้ติดตาม.{0,13}"
|
355 |
+
REFINE1_RE = re.compile(REFINE1_PATTERN, re.MULTILINE)
|
356 |
+
|
357 |
+
REFINE2_PATTERN = "Submitted[ ]by.{0,100}|^เขียนโดย.{0,100}|^Poste?d?[ ]{0,2}(?:by|on){0,2}.{0,100}|^เมื่อวาน[ ]{0,2}\d.{0,100}|^อาทิตย์นี้[ ]{0,2}\d.{0,100}|^อาทิตย์ที่แล้ว[ ]{0,2}\d.{0,100}|^เดือนนี้[ ]{0,2}\d.{0,100}|^เดือนที่แล้ว[ ]{0,2}\d.{0,100}|^รวมผู้เยี่ยมชม[ ]{0,2}\d.{0,100}|^จำนวนผู้ชมโดยประมาณ[ ]{0,2}\d.{0,100}|^รหัสสินค้า[ ]{0,2}\d.{0,100}|^บาร์โค้ด[ ]{0,2}\d.{0,100}|^[ ]โดย[ ]{0,2}.{0,100}|^เข้าชม[ ]{0,2}\d.{0,100}|^โหวต[ ]{0,2}\d.{0,100}|^มุมมอง[ ]{0,2}\d.{0,100}"
|
358 |
+
REFINE2_RE = re.compile(REFINE2_PATTERN, re.MULTILINE)
|
359 |
+
|
360 |
+
REFINE3_PATTERN = "^[^@\n]{0,30}@\d{0,10}.{0,30}|.{0,100}[-]$|\d*[ ]*x[ ]\d*[^ ][ ]?|^ดูหนัง[ ]?(?:ออนไลน์)?[ ].{0,60}|^คุ้มค่าที่สุดอันดับ[ ]{0,2}\d{0,2}.{0,80}|^เปิด[^\d\n]+.{0,10}"
|
361 |
+
REFINE3_RE = re.compile(REFINE3_PATTERN, re.MULTILINE)
|
362 |
+
|
363 |
+
REFINE4_PATTERN = "^[^\n]{0,50}คลิก\)|[Ff]acebook[ ]{0,2}(?:\d{0,3},?\d{0,3})[ ]{0,2}เข้าชม|^[ ]{0,2}[^ ]{0,20}[ ]{0,2}\d{0,9}[ ]{0,2}ความเห็น|\[url=.{0,100}|^ผู้ชม[ ]{0,2}(?:\d{0,3},?)+|\([ ]?\)"
|
364 |
+
REFINE4_RE = re.compile(REFINE4_PATTERN, re.MULTILINE)
|
365 |
+
|
366 |
+
REFINE5_PATTERN = "^[^\d]{0,30}\d{0,10}[ ]{0,2}views.{0,100}|^Prev.{0,100}Next|^สินค้าติดต่อที่.{0,100}|^อ่านต่อคลิก.{0,100}|^สินค้าโปรโมชั่น.{0,200}|^US[ ]?\$\d{0,3},?\d{0,3}.?\d{0,3}.{0,50}"
|
367 |
+
REFINE5_RE = re.compile(REFINE5_PATTERN, re.MULTILINE)
|
368 |
+
|
369 |
+
REFINE6_PATTERN = "^เจ้าหน้าที่ฝ่ายขาย:\n.{0,80}|^(?:\*+[ ]{0,2}[^\*\n]{0,50}[ ]{0,2}\*+)[ ]{0,2}[\+]?(?:(?:\d{0,3},?)+)?|[\*\+]{2,5}|^(?:[^:\n]{0,30}:).{0,200}"
|
370 |
+
REFINE6_RE = re.compile(REFINE6_PATTERN, re.MULTILINE)
|
371 |
+
|
372 |
+
REFINE7_PATTERN = "\(?อ่าน[ ]{0,2}\d{0,3},?\d{0,3}[ ]{0,2}(?:ครั้ง[ ]{0,2})?\)?|โพสต์[ ].{0,100}|Read[ ]{0,2}\d{0,9}[ ]{0,2}times|[^ \n]{0,20}[ ]{0,2}pantip|^Previous (?:Post|article).{0,150}|^Next (?:Post|article).{0,150}|^ตอบกลับ[ ]{0,2}.{0,200}"
|
373 |
+
REFINE7_RE = re.compile(REFINE7_PATTERN, re.MULTILINE)
|
374 |
+
|
375 |
+
REFINE8_PATTERN = "^[ ]?(?:[Pp]ostby|[Pp]osted[ ](?:by|on)).*|^[ ]?เข้าชม/ผู้ติดตาม.*|^[ ]?จำนวนผู้ชมโดยประมาณ[ :]?.*|^[ ]?ลงประกาศฟรี[ ].*|^\|[ ]|^[ ]?จาก[ ].*|^[ ]?By.*|^[ ]{0,2}?โดย[ ]{0,2}?.*"
|
376 |
+
REFINE8_RE = re.compile(REFINE8_PATTERN, re.MULTILINE)
|
377 |
+
|
378 |
+
REFINE9_PATTERN = "^[^\n\.]{0,60}\.{3}$|^[^\n]{0,30}ฉบับที่[ ].*|^Home[ ]/[ ].{100}|^[^\n\|]{0,60}\|.{0,60}"
|
379 |
+
REFINE9_RE = re.compile(REFINE9_PATTERN, re.MULTILINE)
|
380 |
+
|
381 |
+
REFINE10_PATTERN = "^[ ]?(?:\)|↑|►|←|«)[ ]?|^[-_]+"
|
382 |
+
REFINE10_RE = re.compile(REFINE10_PATTERN, re.MULTILINE)
|
383 |
+
|
384 |
+
REFINE11_PATTERN = "^สถิติ(?:วันนี้|สัปดาห์นี้|เมื่อวาน(?:นี้)?|เดือนนี้)[ ]{0,2}.{0,50}|Online[ ]สถิติ.{0,50}"
|
385 |
+
REFINE11_RE = re.compile(REFINE11_PATTERN, re.MULTILINE)
|
386 |
+
|
387 |
+
REFINE12_PATTERN = (
|
388 |
+
"^[^\n\(]{0,80}\(รายละเอียด\)[ ]\(แจ้งลิงก์เสีย\)|^ด[\. ][ชญ][\. ].*|\.{5,}"
|
389 |
+
)
|
390 |
+
REFINE12_RE = re.compile(REFINE12_PATTERN, re.MULTILINE)
|
391 |
+
|
392 |
+
REFINE13_PATTERN = "^[ ]?(?:เรื่องย่อ[ ].{0,100}|คุ้มค่าที่สุดอันดับ.{0,100}|คุ้[ ]่าที่สุดอันดับ.{0,100}|\(?ลงโฆษณาฟรี[ ].{0,200}|\(free[ ]online[ ].{0,100}|\(คลิกเพื่อดูต้นฉบับ\)[ ].{0,100}|แก้ไขครั้งสุดท้ายโดย[ ].{0,100})|^[^\d\n]{0,30}[ ]\d{0,3},?\d{0,3}[ ]ครั้ง.{0,50}"
|
393 |
+
REFINE13_RE = re.compile(REFINE13_PATTERN, re.MULTILINE)
|
394 |
+
|
395 |
+
REFINE14_PATTERN = "^(?:[฿$]?\d{0,9}\.?,?\d{0,9}-?–?:?/?(?:[ ]{0,2}x[ ]{0,2}\d{0,8})?(?:\\bกม\\b\.?)?(?:\\bน\\b\.)?(?:ล้าน|แสน|หมื่น|พัน|ร้อย|สิบ|บาท|[ ])?){0,5}"
|
396 |
+
REFINE14_RE = re.compile(REFINE14_PATTERN, re.MULTILINE)
|
397 |
+
|
398 |
+
from datetime import datetime
|
399 |
+
from typing import List, Dict
|
400 |
+
import re
|
401 |
+
|
402 |
+
|
403 |
+
def clean_with_remove_document(text: str) -> bool:
|
404 |
+
# ---- Clean too large unused lines
|
405 |
+
# Limit matches list to 2 items only, enough
|
406 |
+
matches = TOOLARGE_RE.findall(text)[:2]
|
407 |
+
# Classify as toolarge row if number of matches = 2
|
408 |
+
if len(matches) == 2:
|
409 |
+
return True
|
410 |
+
|
411 |
+
# ---- Clean none characters row
|
412 |
+
# Limit matches list to 25 items
|
413 |
+
matches = NONECHAR_RE.findall(text)[:25]
|
414 |
+
# Classify as none character row if number of matches = 25
|
415 |
+
if len(matches) == 25:
|
416 |
+
return True
|
417 |
+
|
418 |
+
# ---- Clean none tone mark row
|
419 |
+
# Limit matches list to 25 items
|
420 |
+
matches = NONE_TONE_MARK_RE.findall(text)[:25]
|
421 |
+
# Classify as none tone mark row if number of matches = 25
|
422 |
+
if len(matches) == 25:
|
423 |
+
return True
|
424 |
+
|
425 |
+
# ---- Clean Gamble ~ 9.2% of mC4 data
|
426 |
+
# if found gamble word 2 times in a row, classify as gamble row
|
427 |
+
# remove the row
|
428 |
+
# Limit matches list to 2 items only, enough
|
429 |
+
matches = GAMBLE_RE.findall(text)[:2]
|
430 |
+
# Classify as gamble if number of matches = 2
|
431 |
+
if len(matches) == 2:
|
432 |
+
return True
|
433 |
+
|
434 |
+
# ---- Clean Football data
|
435 |
+
# if found gamble word 4 times in a row, classify as football data
|
436 |
+
# remove the row
|
437 |
+
# Limit matches list to 4 items only
|
438 |
+
matches = FOOTBALL_RE.findall(text)[:4]
|
439 |
+
if len(matches) == 4:
|
440 |
+
return True
|
441 |
+
|
442 |
+
# ---- Clean Hotel Advertising
|
443 |
+
# if found hotel word 4 times in a row, classify as Hotel Ad. data
|
444 |
+
# remove the row
|
445 |
+
# Limit matches list to 4 items only, enough
|
446 |
+
matches = HOTEL_AD_RE.findall(text)[:4]
|
447 |
+
if len(matches) == 4:
|
448 |
+
return True
|
449 |
+
|
450 |
+
# ---- Clean Sale ~26% of mC4 data
|
451 |
+
# Sale row data is diverse,
|
452 |
+
# so the regex is not used in this case.
|
453 |
+
# Rules:
|
454 |
+
# 1. Remove row if it contains common specific Sale's URL
|
455 |
+
# 2. Skip to next clean rule if it contains specific keywords, eg. "สอบราคา", "จัดซื้อจัดจ้าง, etc."
|
456 |
+
# 3. If not found keywords in (2) then scan the row with sale keywords, if there are at leat 3 sale kewords found then remove the row.
|
457 |
+
|
458 |
+
if SALE_URL_RE.search(text):
|
459 |
+
return True
|
460 |
+
|
461 |
+
if not SALE_SKIP_RE.search(text):
|
462 |
+
# Classify as Sale data ( 3 matches, can be adjusted)
|
463 |
+
matches = SALE_RE.findall(text)[:3]
|
464 |
+
if len(matches) == 3:
|
465 |
+
return True
|
466 |
+
|
467 |
+
# ---- Clean Rent (พวกเช่า ~2% of mC4 data)
|
468 |
+
# Rent use another rules
|
469 |
+
# 1. find skip words in the row. If found, skip to next rule (not remove)
|
470 |
+
# 2. if found rent word 2 times in a row, classify as rent row
|
471 |
+
# remove the row
|
472 |
+
|
473 |
+
if not RENT_SKIP_RE.search(text):
|
474 |
+
# Limit matches list to 2 items only, enough
|
475 |
+
matches = RENT_RE.findall(text)[:2]
|
476 |
+
if len(matches) == 2:
|
477 |
+
return True
|
478 |
+
|
479 |
+
# ---- Clean pattern (json like -> "abc": ~.5-1% )
|
480 |
+
# 99% can classify as gabage: so remove them
|
481 |
+
# match n items to make sure they are garbages n=20, can change
|
482 |
+
matches = JSON_RE.findall(text)[:20]
|
483 |
+
# if match only 20+, classify as garbage
|
484 |
+
if len(matches) == 20:
|
485 |
+
return True
|
486 |
+
|
487 |
+
# ---- Clean script (Javascript, etc. ~.5% )
|
488 |
+
# 99% can classify as gabage: so remove them
|
489 |
+
matches = SCRIPT_RE.findall(text)[:10]
|
490 |
+
# Classify as script if number of matches = 10
|
491 |
+
if len(matches) == 10:
|
492 |
+
return True
|
493 |
+
|
494 |
+
# ---- Clean garbage (useless or not necessary ~.45%)
|
495 |
+
# classify as gabage: so remove them
|
496 |
+
matches = GARBAGE_RE.findall(text)[:4]
|
497 |
+
# Classify as garbage if number of matches = 4
|
498 |
+
if len(matches) == 4:
|
499 |
+
return True
|
500 |
+
|
501 |
+
# ---- Clean ghost language (~0.008% can cancel this clean)
|
502 |
+
# classify as ghost : so remove them
|
503 |
+
matches = GHOST_RE.findall(text)[:4]
|
504 |
+
# Classify as ghost if number of matches = 4
|
505 |
+
if len(matches) == 4:
|
506 |
+
return True
|
507 |
+
|
508 |
+
# ---- Clean HEX code
|
509 |
+
# classify as HEX : so remove them
|
510 |
+
matches = HEX_RE.findall(text)[:25]
|
511 |
+
# Classify as HEX if number of matches = 25
|
512 |
+
if len(matches) == 25:
|
513 |
+
return True
|
514 |
+
|
515 |
+
return False
|
516 |
+
|
517 |
+
|
518 |
+
def clean_mc4_text(text: str) -> str:
|
519 |
+
text = PAGE_RE.sub(" ", text)
|
520 |
+
text = EMBEDDED_SERVER_RE.sub(" ", text)
|
521 |
+
text = U_RE.sub(" ", text)
|
522 |
+
text = EMAIL_RE.sub(" ", text)
|
523 |
+
text = URL_RE.sub(" ", text)
|
524 |
+
text = MENU1_RE.sub(" ", text)
|
525 |
+
text = MENU2_RE.sub(" ", text)
|
526 |
+
text = MENU3_RE.sub(" ", text)
|
527 |
+
text = MENU4_RE.sub(" ", text)
|
528 |
+
text = SIDEBAR_RE.sub(" ", text)
|
529 |
+
text = BLOCK_RE.sub(" ", text)
|
530 |
+
text = HASHTAG_RE.sub(" ", text)
|
531 |
+
text = MARKUP_RE.sub(" ", text)
|
532 |
+
text = IFRAME_RE.sub(" ", text)
|
533 |
+
text = IP_RE.sub(" ", text)
|
534 |
+
text = TEL_RE.sub(" ", text)
|
535 |
+
text = DATE1_RE.sub(" ", text)
|
536 |
+
text = DATE2_RE.sub(" ", text)
|
537 |
+
text = HTML_RE.sub(" ", text)
|
538 |
+
|
539 |
+
# --- Refinements (in sequence)
|
540 |
+
text = REFINE1_RE.sub(" ", text)
|
541 |
+
text = REFINE2_RE.sub(" ", text)
|
542 |
+
text = REFINE3_RE.sub(" ", text)
|
543 |
+
text = REFINE4_RE.sub(" ", text)
|
544 |
+
text = REFINE5_RE.sub(" ", text)
|
545 |
+
text = REFINE6_RE.sub(" ", text)
|
546 |
+
text = REFINE7_RE.sub(" ", text)
|
547 |
+
text = REFINE8_RE.sub(" ", text)
|
548 |
+
text = REFINE9_RE.sub(" ", text)
|
549 |
+
text = REFINE10_RE.sub(" ", text)
|
550 |
+
text = REFINE11_RE.sub(" ", text)
|
551 |
+
text = REFINE12_RE.sub(" ", text)
|
552 |
+
text = REFINE13_RE.sub(" ", text)
|
553 |
+
text = REFINE14_RE.sub(" ", text)
|
554 |
+
|
555 |
+
# Split the text into lines and remove any empty lines
|
556 |
+
lines = [line for line in text.split("\n") if line]
|
557 |
+
|
558 |
+
# Initialize the list with the first line
|
559 |
+
deduplicated_list = [lines[0]]
|
560 |
+
|
561 |
+
# Iterate over the rest of the lines
|
562 |
+
for i in range(1, len(lines)):
|
563 |
+
# Find the common prefix between this line and the previous line
|
564 |
+
common_prefix = ""
|
565 |
+
for char1, char2 in zip(lines[i], lines[i - 1]):
|
566 |
+
if char1 == char2:
|
567 |
+
common_prefix += char1
|
568 |
+
else:
|
569 |
+
break
|
570 |
+
|
571 |
+
# Remove the common prefix from this line and add it to the list
|
572 |
+
deduplicated_list.append(lines[i][len(common_prefix) :])
|
573 |
+
|
574 |
+
text = "\n".join(deduplicated_list)
|
575 |
+
|
576 |
+
# Clean short lines
|
577 |
+
# ( len(line) <= 30 characters , cut this line off)
|
578 |
+
text = "\n".join(line for line in text.split("\n") if len(line) > 30)
|
579 |
+
|
580 |
+
# ---- The scan row that passes all filter is written to disk
|
581 |
+
# before write to disk, get rid of spaces by change them to single space (' ').
|
582 |
+
|
583 |
+
text = re.sub("[ ]+", " ", text, 0, re.MULTILINE)
|
584 |
+
text = re.sub("^[ ]", "", text, 0, re.MULTILINE)
|
585 |
+
text = re.sub(r"\n\s*", "\n", text, 0, re.MULTILINE)
|
586 |
+
|
587 |
+
return text
|
588 |
+
|
589 |
+
|
590 |
+
def clean_dataset(dataset: List[Dict[str, str]]) -> List[Dict[str, str]]:
|
591 |
+
"""
|
592 |
+
Description : Call function clean_text to process the whole dataset.
|
593 |
+
Input text : An input dataset having each element as a document in the dataset.
|
594 |
+
Output : A clean dataset.
|
595 |
+
"""
|
596 |
+
|
597 |
+
for i, data_point in enumerate(dataset):
|
598 |
+
cleaned_text = clean_text(data_point["text"])
|
599 |
+
if cleaned_text != dataset[i]["text"]:
|
600 |
+
dataset[i]["text"] = cleaned_text
|
601 |
+
dataset[i]["updated_date"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
602 |
+
|
603 |
+
return [data_point for data_point in dataset if data_point["text"] != ""]
|
604 |
+
|
605 |
+
def sorted_by_len(keyword_list):
|
606 |
+
len_keywords = [[len(kw), kw] for kw in keyword_list]
|
607 |
+
|
608 |
+
return [kw for _, kw in sorted(len_keywords)][::-1]
|
609 |
+
|
610 |
+
|
611 |
+
PORN_KEYWORDS = [
|
612 |
+
"คลิปหลุด",
|
613 |
+
"กระเจี๊ยว",
|
614 |
+
"คลิปโป๊",
|
615 |
+
"หนังโป๊",
|
616 |
+
"หนังโป้",
|
617 |
+
"หนังโป็",
|
618 |
+
"เรื่องเสียว",
|
619 |
+
"ซอยหี",
|
620 |
+
"ชักว่าว",
|
621 |
+
"ท่าหมา",
|
622 |
+
"ขย่มควย",
|
623 |
+
"เล่นเสียว",
|
624 |
+
"ควยใหญ่",
|
625 |
+
"หนังเอ็กซ์",
|
626 |
+
"แหกหี",
|
627 |
+
"เย็ด",
|
628 |
+
]
|
629 |
+
GAMBLE_KEYWORDS = [
|
630 |
+
"ufabet",
|
631 |
+
"UFABET",
|
632 |
+
"ล้มโต๊ะ",
|
633 |
+
"เซียนสเต็ป",
|
634 |
+
"บอลเต็ง",
|
635 |
+
"แทงบอล",
|
636 |
+
"คาสิโน",
|
637 |
+
"บาคาร่า",
|
638 |
+
"เว็บสล็อต",
|
639 |
+
"เกมสล็อต",
|
640 |
+
"สล็อตออนไลน์",
|
641 |
+
"คาสิโนออนไลน์",
|
642 |
+
"หวยมาเลย์",
|
643 |
+
"หวยฮานอย",
|
644 |
+
"น้ำเต้าปูปลา",
|
645 |
+
"หวยออนไลน์",
|
646 |
+
"แทงหวย",
|
647 |
+
"หวยหุ้น",
|
648 |
+
"ยิง��ลาออนไลน์",
|
649 |
+
"ได้เงินจริง",
|
650 |
+
"บา คา ร่า",
|
651 |
+
]
|
652 |
+
SPAM_MOVIE_KEYWORDS = [
|
653 |
+
"ดูหนังออนไลน์",
|
654 |
+
"หนังออนไลน์",
|
655 |
+
"เว็บดูหนัง",
|
656 |
+
"หนังชนโรง",
|
657 |
+
"หนังใหม่ชนโรง",
|
658 |
+
"เสียงไทย",
|
659 |
+
"เสียงญี่ปุ่น",
|
660 |
+
"เสียงอังกฤษ",
|
661 |
+
]
|
662 |
+
SPAM_LIKE_KEYWORDS = [
|
663 |
+
"ปั้มไลค์",
|
664 |
+
"รับจ้างกดไลค์",
|
665 |
+
"จ้างไลค์",
|
666 |
+
"ปั๊มไลค์",
|
667 |
+
"ปั่นไลค์",
|
668 |
+
"เพิ่มไลค์",
|
669 |
+
"ซื้อไลค์",
|
670 |
+
]
|
671 |
+
CODE_KEYWORDS = [
|
672 |
+
"padding:",
|
673 |
+
"display:",
|
674 |
+
"S3=n8",
|
675 |
+
"phpBB Debug",
|
676 |
+
"getElementById",
|
677 |
+
"innerHTML",
|
678 |
+
"parseInt",
|
679 |
+
"addEventListener",
|
680 |
+
"console\.log",
|
681 |
+
"checkCookieForTarget",
|
682 |
+
"setAttribute",
|
683 |
+
"getItem",
|
684 |
+
"if \(",
|
685 |
+
"else {",
|
686 |
+
"JSON\.stringify",
|
687 |
+
"onclick",
|
688 |
+
]
|
689 |
+
|
690 |
+
WEBBOARD_KEYWORDS = [
|
691 |
+
"คุณกำลังใช้งานแบบปิดการใช้ Javascript",
|
692 |
+
"Longdo Dictionary",
|
693 |
+
"นโยบายการคุ้มครองข้อมูลส่วนบุคคล",
|
694 |
+
"เงื่อนไขการให้บริการเว็บไซต์",
|
695 |
+
"นโยบายความปลอดภัย",
|
696 |
+
"นโยบายเว็บไซต์และการปฏิเสธความรับผิด",
|
697 |
+
"คุณอาจจะยังไม่ได้เข้าสู่ระบบหรือยังไม่ได้ลงทะเบียน",
|
698 |
+
"คุณไม่ได้เข้าสู่ระบบหรือคุณไม่มีสิทธิ์เข้าหน้านี้",
|
699 |
+
]
|
700 |
+
|
701 |
+
PORN_KEYWORDS += [" ".join(list(kw)) for kw in PORN_KEYWORDS]
|
702 |
+
GAMBLE_KEYWORDS += [" ".join(list(kw)) for kw in GAMBLE_KEYWORDS]
|
703 |
+
SPAM_MOVIE_KEYWORDS += [" ".join(list(kw)) for kw in SPAM_MOVIE_KEYWORDS]
|
704 |
+
|
705 |
+
DOCUMENT_REMOVAL_KEYWORDS = (
|
706 |
+
PORN_KEYWORDS
|
707 |
+
+ GAMBLE_KEYWORDS
|
708 |
+
+ SPAM_MOVIE_KEYWORDS
|
709 |
+
+ SPAM_LIKE_KEYWORDS
|
710 |
+
+ CODE_KEYWORDS
|
711 |
+
+ WEBBOARD_KEYWORDS
|
712 |
+
)
|
713 |
+
|
714 |
+
PARTIAL_REMOVAL_KEYWORDS = [
|
715 |
+
"Posted on",
|
716 |
+
"Posted by",
|
717 |
+
"Posted by:",
|
718 |
+
"Posted By:",
|
719 |
+
"สมาชิกหมายเลข [0-9,]+",
|
720 |
+
"อ่าน [0-9,]+ ครั้ง",
|
721 |
+
"เปิดดู [0-9,]+ ครั้ง",
|
722 |
+
"ดู [0-9,]+ ครั้ง",
|
723 |
+
"คะแนนสะสม: [0-9,]+ แต้ม",
|
724 |
+
"ความคิดเห็น: [0-9,]+",
|
725 |
+
"[0-9,]+ บุคคลทั่วไป กำลังดูบอร์ดนี้",
|
726 |
+
"หน้าที่แล้ว ต่อไป",
|
727 |
+
"ความคิดเห็นที่ [0-9,]+",
|
728 |
+
"[0-9,]+ สมาชิก และ [0-9,]+ บุคคลทั่วไป",
|
729 |
+
"กำลังดูหัวข้อนี้",
|
730 |
+
"เข้าสู่ระบบด้วยชื่อผู้ใช้",
|
731 |
+
"แสดงกระทู้จาก:",
|
732 |
+
"กระทู้: [0-9,]+",
|
733 |
+
"เว็บไซต์เรามีการใช้คุกกี้และเก็บข้อมูลผู้ใช้งาน โปรดศึกษาและยอมรับ นโยบายคุ้มครองข้อมูลส่วนบุคคล ก่อนใช้งาน",
|
734 |
+
"Privacy & Cookies: This site uses cookies. By continuing to use this website, you agree to their use\.",
|
735 |
+
"Previous\t\nNext\nLeave a Reply Cancel reply\nYou must be logged in to post a comment.\nSearch for:\nFeatured Post\n",
|
736 |
+
"Click to read more\nYou must be logged in to view or write comments\.",
|
737 |
+
"[0-9,]+ Views",
|
738 |
+
"Skip to content",
|
739 |
+
"Last Modified Posts",
|
740 |
+
"Last Updated:",
|
741 |
+
"\(อ่าน [0-9,]+ ครั้ง\)",
|
742 |
+
"Recent Comments",
|
743 |
+
"«.*?»",
|
744 |
+
"< --แสดงทั้งหมด-- >",
|
745 |
+
"นโยบายความเป็นส่วนตัว",
|
746 |
+
"เงื่อนไขการใช้เว็บไซต์",
|
747 |
+
"ตั้งค่าคุกกี้",
|
748 |
+
"ท่านยอมรับให้เว็บไซต์นี้จัดเก็บคุกกี้เพื่อประสบการณ์การใช้งานเว็บไซต์ที่ดียิ่งขึ้น",
|
749 |
+
"รวมถึงช่วยให้ท่านมีโอกาสได้รับข้อเสนอหรือเนื้อหาที่ตรงตามความสนใจของท่าน",
|
750 |
+
"ท่านสามารถดู Privacy Notice ของเว็บไซต์เรา ได้ที่นี่",
|
751 |
+
"You may be trying to access this site from a secured browser on the server. Please enable scripts and reload this page.",
|
752 |
+
"เผยแพร่: \d\d [ก-๙]+ \d\d\d\d \d\d:\d\d น\.",
|
753 |
+
"Last updated: \d\d [ก-๙]+\.[ก-๙]+\. \d\d\d\d \d\d:\d\d น\.",
|
754 |
+
"Lorem ipsum dolor sit amet, consectetur adipiscing elit\.",
|
755 |
+
"Search for:",
|
756 |
+
"Save my name, email, and website in this browser for the next time I comment",
|
757 |
+
"Your email address will not be published. Required fields are marked",
|
758 |
+
"Leave a Reply Cancel reply",
|
759 |
+
"((?:หน้าหลัก|เข้าสู่ระบบ|หน้าแรก) \|(?: [^\s]+(?:(?: \|)|$|\s))+)",
|
760 |
+
"กลับหน้าแรก",
|
761 |
+
"ติดต่อเรา",
|
762 |
+
"Contact Us",
|
763 |
+
"#\w+",
|
764 |
+
"ติดต่อผู้ดูแลเว็บไซต์",
|
765 |
+
"หากท่านพบว่ามีข้อมูลใดๆที่ละเมิดทรัพย์สินทางปัญญาปรากฏอยู่ในเว็บไซต์โปรดแจ้งให้ทราบ",
|
766 |
+
"No related posts",
|
767 |
+
"Posted in",
|
768 |
+
"((?:Tags:|Tagged|Tag) (?:.{1,40}(?:,|\n|$))+)",
|
769 |
+
"ตอบ:",
|
770 |
+
"Sort by:",
|
771 |
+
"All rights reserved",
|
772 |
+
"ความยาวอย่างน้อย",
|
773 |
+
"ระบบได้ดำเนินการส่ง OTP",
|
774 |
+
"เป็นสมาชิกอยู่แล้ว\?",
|
775 |
+
"We use cookies",
|
776 |
+
"Cookie Settings",
|
777 |
+
"Homeหน้าหลัก",
|
778 |
+
"Home หน้าหลัก",
|
779 |
+
"ข่าวสารล่าสุด",
|
780 |
+
"ปัญหา การใช้งาน",
|
781 |
+
"ปัญหาการใช้งาน" "ผู้เขียน",
|
782 |
+
"หัวข้อ:",
|
783 |
+
"\*\* พร้อมส่ง \*\*",
|
784 |
+
]
|
785 |
+
|
786 |
+
TH_MONTHS = [
|
787 |
+
"มกราคม",
|
788 |
+
"กุมภาพันธ์",
|
789 |
+
"มีนาคม",
|
790 |
+
"เมษายน",
|
791 |
+
"พฤษภาคม",
|
792 |
+
"มิถุนายน",
|
793 |
+
"กรกฎาคม",
|
794 |
+
"สิงหาคม",
|
795 |
+
"กันยายน",
|
796 |
+
"ตุลาคม",
|
797 |
+
"พฤศจิกายน",
|
798 |
+
"ธันวาคม",
|
799 |
+
"ม\.ค\.",
|
800 |
+
"ก\.พ\.",
|
801 |
+
"มี\.ค\.",
|
802 |
+
"เม\.ย\.",
|
803 |
+
"พ\.ค\.",
|
804 |
+
"มิ\.ย\.",
|
805 |
+
"ก\.ค\.",
|
806 |
+
"ส\.ค\.",
|
807 |
+
"ก\.ย\.",
|
808 |
+
"ต\.ค\.",
|
809 |
+
"พ\.ย\.",
|
810 |
+
"ธ\.ค\.",
|
811 |
+
]
|
812 |
+
|
813 |
+
CODE_SPECIAL_CHARACTERS = ["\{", "\+", "\}", "/", ":"]
|
814 |
+
|
815 |
+
PARTIAL_REMOVAL_KEYWORDS = sorted_by_len(PARTIAL_REMOVAL_KEYWORDS)
|
816 |
+
|
817 |
+
import re
|
818 |
+
from pythainlp.util import countthai
|
819 |
+
from typing import List, Dict
|
820 |
+
from datetime import datetime
|
821 |
+
|
822 |
+
|
823 |
+
def contains_document_removal_keywords(text: str) -> bool:
|
824 |
+
"""
|
825 |
+
Description : Check if an input document contains any document removal keywords.
|
826 |
+
Input text : An input document.
|
827 |
+
Output : True if the document contains the keywords. Otherwise, False
|
828 |
+
"""
|
829 |
+
|
830 |
+
pattern = "|".join(DOCUMENT_REMOVAL_KEYWORDS)
|
831 |
+
|
832 |
+
return bool(re.search(pattern, text))
|
833 |
+
|
834 |
+
|
835 |
+
def check_ratio_bad_substring(text: str) -> bool:
|
836 |
+
"""
|
837 |
+
Description : Check if the ratio between number of keywords and length of a document
|
838 |
+
is exceeds the threshold for each groups.
|
839 |
+
|
840 |
+
Group #1 : Name of months in Thai including abbreviations.
|
841 |
+
Group #2 : Special char that usually found in the code section.
|
842 |
+
Group #3 : Space.
|
843 |
+
Group #4 : Commar.
|
844 |
+
|
845 |
+
Note : Thresholds of each group are from the experiment on oscar.
|
846 |
+
|
847 |
+
Input text : An input document.
|
848 |
+
Output : True if a ratio of at least 1 group is above . Otherwise, False
|
849 |
+
"""
|
850 |
+
|
851 |
+
n = len(text)
|
852 |
+
|
853 |
+
if len(re.findall("|".join(TH_MONTHS), text)) / n > 0.015:
|
854 |
+
return True
|
855 |
+
|
856 |
+
if len(re.findall("|".join(CODE_SPECIAL_CHARACTERS), text)) / n > 0.075:
|
857 |
+
return True
|
858 |
+
|
859 |
+
if len(re.findall(" ", text)) / n > 0.13:
|
860 |
+
return True
|
861 |
+
|
862 |
+
if len(re.findall(",", text)) / n > 0.05:
|
863 |
+
return True
|
864 |
+
return False
|
865 |
+
|
866 |
+
|
867 |
+
def remove_partial_keywords(text: str) -> str:
|
868 |
+
"""
|
869 |
+
Description : Remove partial removal keywords from the document.
|
870 |
+
|
871 |
+
Input text : An input document.
|
872 |
+
Output : A document after removed keywords.
|
873 |
+
"""
|
874 |
+
|
875 |
+
return re.sub("|".join(PARTIAL_REMOVAL_KEYWORDS), "", text)
|
876 |
+
|
877 |
+
|
878 |
+
def clean_oscar_text(text: str) -> str:
|
879 |
+
"""
|
880 |
+
Description : Clean an input document by these steps
|
881 |
+
|
882 |
+
1. Remove the whole document if
|
883 |
+
1.1. Contains any document removal keywords (ex. porn, gamble)
|
884 |
+
1.2. Contains too much TH months, code character, space and commar.
|
885 |
+
1.3. The percent of thai characters is less than 50%.
|
886 |
+
2. Remove partial removal keywords.
|
887 |
+
|
888 |
+
Input text : An input document.
|
889 |
+
Output : A clean document ("" if the whole document should be removed).
|
890 |
+
"""
|
891 |
+
|
892 |
+
if (
|
893 |
+
len(text) == 0
|
894 |
+
or contains_document_removal_keywords(text)
|
895 |
+
or check_ratio_bad_substring(text)
|
896 |
+
or countthai(text) < 50
|
897 |
+
):
|
898 |
+
return ""
|
899 |
+
|
900 |
+
text = remove_partial_keywords(text).strip()
|
901 |
+
|
902 |
+
return text
|
903 |
+
|
904 |
+
|
905 |
+
def clean_dataset(dataset: List[Dict[str, str]]) -> List[Dict[str, str]]:
|
906 |
+
"""
|
907 |
+
Description : Call function clean_text to process the whole dataset.
|
908 |
+
Input text : An input dataset having each element as a document in the dataset.
|
909 |
+
Output : A clean dataset.
|
910 |
+
"""
|
911 |
+
for i, data_point in enumerate(dataset):
|
912 |
+
cleaned_text = clean_text(data_point["text"])
|
913 |
+
if cleaned_text != dataset[i]["text"]:
|
914 |
+
dataset[i]["text"] = cleaned_text
|
915 |
+
dataset[i]["updated_date"] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
916 |
+
|
917 |
+
return [data_point for data_point in dataset if data_point["text"] != ""]
|
step2.py
ADDED
@@ -0,0 +1,183 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import re
|
2 |
+
import unicodedata
|
3 |
+
|
4 |
+
UNICODE_PUNCT = {
|
5 |
+
",": ",",
|
6 |
+
"。": ".",
|
7 |
+
"、": ",",
|
8 |
+
"„": '"',
|
9 |
+
"”": '"',
|
10 |
+
"“": '"',
|
11 |
+
"«": '"',
|
12 |
+
"»": '"',
|
13 |
+
"1": '"',
|
14 |
+
"」": '"',
|
15 |
+
"「": '"',
|
16 |
+
"《": '"',
|
17 |
+
"》": '"',
|
18 |
+
"´": "'",
|
19 |
+
"∶": ":",
|
20 |
+
":": ":",
|
21 |
+
"?": "?",
|
22 |
+
"!": "!",
|
23 |
+
"(": "(",
|
24 |
+
")": ")",
|
25 |
+
";": ";",
|
26 |
+
"–": "-",
|
27 |
+
"—": " - ",
|
28 |
+
".": ". ",
|
29 |
+
"~": "~",
|
30 |
+
"’": "'",
|
31 |
+
"…": "...",
|
32 |
+
"━": "-",
|
33 |
+
"〈": "<",
|
34 |
+
"〉": ">",
|
35 |
+
"【": "[",
|
36 |
+
"】": "]",
|
37 |
+
"%": "%",
|
38 |
+
"►": "-",
|
39 |
+
}
|
40 |
+
|
41 |
+
UNICODE_PUNCT_RE = re.compile(f"[{''.join(UNICODE_PUNCT.keys())}]")
|
42 |
+
|
43 |
+
|
44 |
+
def replace_unicode_punct(text: str) -> str:
|
45 |
+
return "".join((UNICODE_PUNCT.get(c, c) for c in text))
|
46 |
+
|
47 |
+
|
48 |
+
def remove_unicode_punct(text: str) -> str:
|
49 |
+
"""More aggressive version of replace_unicode_punct but also faster."""
|
50 |
+
return UNICODE_PUNCT_RE.sub("", text)
|
51 |
+
|
52 |
+
|
53 |
+
def strip_accents(line: str) -> str:
|
54 |
+
"""Strips accents from a piece of text."""
|
55 |
+
nfd = unicodedata.normalize("NFD", line)
|
56 |
+
output = [c for c in nfd if unicodedata.category(c) != "Mn"]
|
57 |
+
if len(output) == line:
|
58 |
+
return line
|
59 |
+
return "".join(output)
|
60 |
+
|
61 |
+
|
62 |
+
# Build a regex matching all control characters.
|
63 |
+
NON_PRINTING_CHARS_RE = re.compile(
|
64 |
+
f"[{''.join(map(chr, list(range(0,32)) + list(range(127,160))))}]"
|
65 |
+
)
|
66 |
+
DIGIT_RE = re.compile(r"\d")
|
67 |
+
PUNCT_OR_NON_PRINTING_CHARS_RE = re.compile(
|
68 |
+
(UNICODE_PUNCT_RE.pattern + NON_PRINTING_CHARS_RE.pattern).replace("][", "")
|
69 |
+
)
|
70 |
+
|
71 |
+
|
72 |
+
def remove_non_printing_char(text: str) -> str:
|
73 |
+
return NON_PRINTING_CHARS_RE.sub("", text)
|
74 |
+
|
75 |
+
|
76 |
+
def normalize_spacing_for_tok(text: str, language: str = "en") -> str:
|
77 |
+
res = (
|
78 |
+
text.replace("\r", "")
|
79 |
+
# remove extra spaces
|
80 |
+
.replace("(", " (")
|
81 |
+
.replace(")", ") ")
|
82 |
+
.replace(" +", " ")
|
83 |
+
)
|
84 |
+
res = re.sub(r"\) ([\.\!\:\?\;\,])", r"\)\1", res)
|
85 |
+
res = res.replace("( ", "(").replace(" )", ")")
|
86 |
+
res = re.sub(r"(\d) \%", r"\1\%", res)
|
87 |
+
res = res.replace(" :", ":").replace(" ;", ";")
|
88 |
+
res = res.replace("`", "'").replace("''", ' " ')
|
89 |
+
|
90 |
+
res = (
|
91 |
+
res.replace("„", '"')
|
92 |
+
.replace("“", '"')
|
93 |
+
.replace("”", '"')
|
94 |
+
.replace("–", "-")
|
95 |
+
.replace("—", " - ")
|
96 |
+
.replace(" +", " ")
|
97 |
+
.replace("´", "'")
|
98 |
+
.replace("([a-z])‘([a-z])", r"\1'\2/")
|
99 |
+
.replace("([a-z])’([a-z])", r"\1'\2/")
|
100 |
+
.replace("‘", '"')
|
101 |
+
.replace("‚", '"')
|
102 |
+
.replace("’", '"')
|
103 |
+
.replace("''", '"')
|
104 |
+
.replace("´´", '"')
|
105 |
+
.replace("…", "...")
|
106 |
+
# French quotes
|
107 |
+
.replace(" « ", ' "')
|
108 |
+
.replace("« ", '"')
|
109 |
+
.replace("«", '"')
|
110 |
+
.replace(" » ", '" ')
|
111 |
+
.replace(" »", '"')
|
112 |
+
.replace("»", '"')
|
113 |
+
# handle pseudo-spaces
|
114 |
+
.replace(" %", "%")
|
115 |
+
.replace("nº ", "nº ")
|
116 |
+
.replace(" :", ":")
|
117 |
+
.replace(" ºC", " ºC")
|
118 |
+
.replace(" cm", " cm")
|
119 |
+
.replace(" ?", "?")
|
120 |
+
.replace(" !", "!")
|
121 |
+
.replace(" ;", ";")
|
122 |
+
.replace(", ", ", ")
|
123 |
+
.replace(" +", " ")
|
124 |
+
.replace(".", ". ")
|
125 |
+
)
|
126 |
+
# English "quotation," followed by comma, style
|
127 |
+
if language == "en":
|
128 |
+
res = re.sub(r"\"([,\.]+)", r"\1\"", res)
|
129 |
+
# Czech is confused
|
130 |
+
elif language == "cs" or language == "cz":
|
131 |
+
pass
|
132 |
+
# German/Spanish/French "quotation", followed by comma, style
|
133 |
+
else:
|
134 |
+
res = res.replace(',"', '",')
|
135 |
+
res = re.sub(
|
136 |
+
r"(\.+)\"(\s*[^<])", r"\"\1\2", res
|
137 |
+
) # don't fix period at end of sentence
|
138 |
+
|
139 |
+
if (
|
140 |
+
language == "de"
|
141 |
+
or language == "es"
|
142 |
+
or language == "cz"
|
143 |
+
or language == "cs"
|
144 |
+
or language == "fr"
|
145 |
+
):
|
146 |
+
res = re.sub(r"(\d) (\d)", r"\1,\2", res)
|
147 |
+
else:
|
148 |
+
res = re.sub(r"(\d) (\d)", r"\1.\2", res)
|
149 |
+
return res
|
150 |
+
|
151 |
+
|
152 |
+
def normalize(line: str, accent=True, case=True, numbers=True, punct=1) -> str:
|
153 |
+
line = line.strip()
|
154 |
+
if not line:
|
155 |
+
return line
|
156 |
+
if case:
|
157 |
+
line = line.lower()
|
158 |
+
if accent:
|
159 |
+
line = strip_accents(line)
|
160 |
+
if numbers:
|
161 |
+
line = DIGIT_RE.sub("0", line)
|
162 |
+
if punct == 1:
|
163 |
+
line = replace_unicode_punct(line)
|
164 |
+
elif punct == 2:
|
165 |
+
line = remove_unicode_punct(line)
|
166 |
+
line = remove_non_printing_char(line)
|
167 |
+
return line
|
168 |
+
|
169 |
+
|
170 |
+
def slow_normalize_for_dedup(line: str) -> str:
|
171 |
+
return normalize(line, accent=False, case=True, numbers=True, punct=2)
|
172 |
+
|
173 |
+
|
174 |
+
def normalize_for_dedup(line: str) -> str:
|
175 |
+
line = line.strip()
|
176 |
+
if not line:
|
177 |
+
return line
|
178 |
+
# case
|
179 |
+
line = line.lower()
|
180 |
+
# numbers
|
181 |
+
line = DIGIT_RE.sub("0", line)
|
182 |
+
line = PUNCT_OR_NON_PRINTING_CHARS_RE.sub("", line)
|
183 |
+
return line
|
step2_perplexity.py
ADDED
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import kenlm
|
2 |
+
import math
|
3 |
+
import numpy as np
|
4 |
+
import pandas as pd
|
5 |
+
import pickle
|
6 |
+
import scipy
|
7 |
+
import sentencepiece # type: ignore
|
8 |
+
from typing import List
|
9 |
+
import warnings
|
10 |
+
from step2 import normalize
|
11 |
+
import os
|
12 |
+
warnings.simplefilter(action="ignore", category=FutureWarning)
|
13 |
+
|
14 |
+
|
15 |
+
def load_model():
|
16 |
+
os.system("gdown 1OBbo21v_-esL31rxtNtsMHrA8T1JYqAd")
|
17 |
+
os.system("unzip /content/core.zip")
|
18 |
+
os.system("!rm /content/core.zip")
|
19 |
+
|
20 |
+
class SentencesLM:
|
21 |
+
"""Returns the score of each individual paragraph."""
|
22 |
+
|
23 |
+
def __init__(self):
|
24 |
+
lm_config = kenlm.Config()
|
25 |
+
lm_config.load_method = 2
|
26 |
+
|
27 |
+
lm_model_filename = (
|
28 |
+
os.path.join("core", "th.arpa.bin")
|
29 |
+
)
|
30 |
+
self.lm = kenlm.Model(str(lm_model_filename), lm_config)
|
31 |
+
self.sp = sentencepiece.SentencePieceProcessor()
|
32 |
+
self.sp.load(
|
33 |
+
os.path.join("core", "th.sp.model")
|
34 |
+
)
|
35 |
+
|
36 |
+
def pp(self, log_score: float, length: int) -> float:
|
37 |
+
"""Compute perplexity score"""
|
38 |
+
power = min(30, -log_score / length)
|
39 |
+
|
40 |
+
return 10.0**power
|
41 |
+
|
42 |
+
def do(self, document: List[str]) -> float: # type: ignore
|
43 |
+
"""Compute perplexity for each line of document"""
|
44 |
+
total_pp = 0
|
45 |
+
total_length = 0
|
46 |
+
for line in document:
|
47 |
+
line = normalize(line, accent=False)
|
48 |
+
tokenized_line = " ".join(self.sp.encode_as_pieces(line))
|
49 |
+
log_score = self.lm.score(tokenized_line)
|
50 |
+
length = len(line.split()) + 1
|
51 |
+
|
52 |
+
total_length += length
|
53 |
+
total_pp += log_score
|
54 |
+
return round(self.pp(total_pp, total_length), 1)
|
55 |
+
|
56 |
+
|
57 |
+
classifier_filename = os.path.join("core", "decision_tree.sav")
|
58 |
+
classifier = pickle.load(open(classifier_filename, "rb"))
|
59 |
+
|
60 |
+
lm = SentencesLM()
|
61 |
+
|
62 |
+
|
63 |
+
def classify_spam(text: str):
|
64 |
+
"""Classify if text is spam using perplexity and decision tree as thresholder
|
65 |
+
Input : text -> a text to classify.
|
66 |
+
Output : prediction -> Prediction whether text is spam.
|
67 |
+
1 Represents spam and 0 represent non-spam.
|
68 |
+
Output : log_pp_score -> log of perplexity score.
|
69 |
+
"""
|
70 |
+
|
71 |
+
pp_score = lm.do(text.split("\n"))
|
72 |
+
|
73 |
+
log_pp_score = math.log(pp_score)
|
74 |
+
|
75 |
+
prediction = classifier.predict(pd.DataFrame({"log_score": [log_pp_score]}))
|
76 |
+
|
77 |
+
return prediction, log_pp_score
|
78 |
+
|
79 |
+
|
80 |
+
def sample_score(log_scores, n, percentage=0.1) -> np.ndarray:
|
81 |
+
np.random.seed(0)
|
82 |
+
|
83 |
+
lower_bound, upper_bound = min(log_scores), max(log_scores)
|
84 |
+
|
85 |
+
mean, std = np.mean(log_scores), np.std(log_scores)
|
86 |
+
|
87 |
+
sampled_scores = scipy.stats.truncnorm.rvs(
|
88 |
+
(lower_bound - mean) / std,
|
89 |
+
(upper_bound - mean) / std,
|
90 |
+
loc=mean,
|
91 |
+
scale=std,
|
92 |
+
size=int(percentage * n),
|
93 |
+
)
|
94 |
+
|
95 |
+
return sampled_scores
|
96 |
+
|
97 |
+
|
98 |
+
def sample_text_back(texts, log_scores, percentage=0.5, replace=True) -> List[str]:
|
99 |
+
"""Sample some spam text back in the dataset
|
100 |
+
using log score distribution of language model"""
|
101 |
+
|
102 |
+
sampled_scores = sample_score(log_scores, len(texts), percentage)
|
103 |
+
|
104 |
+
sampled_texts = []
|
105 |
+
|
106 |
+
selected_idx = set()
|
107 |
+
|
108 |
+
for samp_score in sampled_scores:
|
109 |
+
min_diff, min_idx = float("inf"), -1
|
110 |
+
|
111 |
+
for idx, s in enumerate(log_scores):
|
112 |
+
if idx in selected_idx:
|
113 |
+
continue
|
114 |
+
|
115 |
+
diff = (samp_score - s) ** 2
|
116 |
+
if diff < min_diff:
|
117 |
+
min_diff = diff
|
118 |
+
min_idx = idx
|
119 |
+
|
120 |
+
sampled_texts.append(texts[min_idx])
|
121 |
+
|
122 |
+
if not replace:
|
123 |
+
selected_idx.add(min_idx)
|
124 |
+
|
125 |
+
return sampled_texts
|