阿里云安全算法挑戰(zhàn)賽網(wǎng)頁文本分類總結(jié)

第一次參加阿里天池的比賽沫屡,雖然總成績沒能進(jìn)前十,但是網(wǎng)頁題做的還不錯(最終排名第二撮珠,成績0.8312分)沮脖。在這里記錄一下網(wǎng)頁文本分類題的解題思路和賽后總結(jié):

出題背景是阿里云服務(wù)器上存在著很多非法網(wǎng)頁金矛,需要通過機(jī)器學(xué)習(xí)算法識別出網(wǎng)頁的類別,對非法網(wǎng)頁進(jìn)行打擊勺届。題目的詳細(xì)描述見賽題官方網(wǎng)址賽題二驶俊。原始數(shù)據(jù)是網(wǎng)頁靜態(tài)html代碼,但可轉(zhuǎn)換為文本分類問題免姿。訓(xùn)練集的標(biāo)簽已給出饼酿,共有4個類別:

  1. fake_card(證件卡票):提供偽造證件、發(fā)票胚膊、證明材料等制作服務(wù)故俐;
  2. gambling(賭博類):包括賭博交易、網(wǎng)絡(luò)博彩紊婉、賭博器具等药版;
  3. sexy(色情類):包括色情傳播、文圖視頻喻犁、網(wǎng)絡(luò)招嫖等槽片;
  4. normal(正常類):不包含以上內(nèi)容的網(wǎng)頁。
網(wǎng)頁分類

算法整體流程圖:

整體流程圖

關(guān)鍵步驟:

數(shù)據(jù)預(yù)處理

訓(xùn)練數(shù)據(jù)中肢础,id字段是經(jīng)過hash處理的还栓,會出現(xiàn)id重復(fù)的情況,需要根據(jù)id對數(shù)據(jù)進(jìn)行去重传轰;另外對各類型網(wǎng)頁html文本長度的統(tǒng)計結(jié)果如下所示:

網(wǎng)頁文本長度統(tǒng)計圖

網(wǎng)頁文本長度大于200000的不到網(wǎng)頁數(shù)據(jù)總量的1%剩盒,且基本不包含風(fēng)險網(wǎng)頁,所以對其進(jìn)行清除慨蛙,減少后續(xù)的計算量勃刨;觀察出字符長度小于200的網(wǎng)頁中大部分內(nèi)容為"404 ERROR","正在等待響應(yīng)...",'"排隊中..."等內(nèi)容,對模型的訓(xùn)練沒有用處股淡,也要進(jìn)行清除身隐。代碼在下方:

--計算id的出現(xiàn)次數(shù) 
select id, risk, html, count(id) over (partition by id) as count from ${t1} ;
--計算html文本的長度
select *, length(html) as html_length from ${t1} ;
--數(shù)據(jù)清理
select * from ${t1} where count = 1 and html_length > 200 and html_length < 200000 ;

網(wǎng)頁文本提取

html字段是網(wǎng)頁的源代碼,包含了結(jié)構(gòu)化的html標(biāo)簽唯灵,<style>贾铝、<script>等標(biāo)簽中的內(nèi)容不包含語義信息,參考文獻(xiàn)[5]中結(jié)論埠帕,實驗中只對title垢揩、keywords、description敛瓷、body標(biāo)簽中的文本進(jìn)行提取叁巨,另外去除文本中的特殊字符,僅保留英文呐籽、中文以及常見的標(biāo)點(diǎn)符號锋勺。html中文本內(nèi)容的提取通過在MapReduce的map函數(shù)中調(diào)用jsoup庫實現(xiàn)(因為ODPS有沙箱隔離的限制蚀瘸,實驗中抽取了jsoup的核心代碼放到自定義的包中打包上傳)

package cn.edu.whu.tianchi_htmlclassify;

import cn.edu.whu.tianchi_htmlclassify.nodes.Document;
import cn.edu.whu.tianchi_htmlclassify.nodes.Element;
import cn.edu.whu.tianchi_htmlclassify.select.Elements;

import com.aliyun.odps.OdpsException;
import com.aliyun.odps.data.Record;
import com.aliyun.odps.data.TableInfo;
import com.aliyun.odps.mapred.JobClient;
import com.aliyun.odps.mapred.MapperBase;
import com.aliyun.odps.mapred.conf.JobConf;
import com.aliyun.odps.mapred.utils.InputUtils;
import com.aliyun.odps.mapred.utils.OutputUtils;
import com.aliyun.odps.mapred.utils.SchemaUtils;

import java.io.IOException;

public class HtmlExtractor {
    public static void main(String[] args) throws OdpsException {
        JobConf job = new JobConf();
        job.setMapperClass(MapperClass.class);
        job.setMapOutputKeySchema(SchemaUtils.fromString("id:string"));
        job.setMapOutputValueSchema(SchemaUtils.fromString(
                "title:string,keywords:string,description:string,bodytext:string"));
        job.setNumReduceTasks(0);

        InputUtils.addTable(TableInfo.builder().tableName(args[0]).build(), job);
        OutputUtils.addTable(TableInfo.builder().tableName(args[1]).build(), job);

        JobClient.runJob(job);
    }

    public static class MapperClass extends MapperBase {
        private Record output;

        @Override
        public void setup(TaskContext context) throws IOException {
            output = context.createOutputRecord();
        }

        @Override
        public void map(long key, Record record, TaskContext context)
            throws IOException {
            //id原樣設(shè)置
            output.set(0, record.get("id").toString());

            String html = record.get("html").toString();
            String bodyText = new String();
            String title = new String();
            String description = new String();
            String keywords = new String();

            Document doc = null;
            try {
                //使用Jsoup解析html
                doc = Jsoup.parse(html); 
                //去除隱藏的html標(biāo)簽
                doc.select("*[style*=display:none]").remove();   
                //獲取title文本
                Elements titles = doc.select("title");    
                for (Element element : titles) {
                    title = title + element.text();
                }
                //獲取keywords文本
                Elements metaKey = doc.select("meta[name=keywords]");    
                for (Element element : metaKey) {
                    keywords = keywords + element.attr("content");
                }
                //獲取description文本
                Elements metaDes = doc.select("meta[name=description]");    
                for (Element element : metaDes) {
                    description = description + element.attr("content");
                }
                //獲取body文本
                Elements body = doc.select("body");    
                for (Element element : body) {
                    bodyText = bodyText + element.text();
                }
            } catch (Exception e) {
                bodyText = "";
            } finally {
                //僅保留數(shù)字、字母庶橱、中文贮勃、常用的標(biāo)點(diǎn)符號
                output.set(1,title.replaceAll(
                          "[^0-9a-zA-Z\u4e00-\u9fa5.,苏章、,寂嘉。?“”]+", ""));
                output.set(2,keywords.replaceAll(
                          "[^0-9a-zA-Z\u4e00-\u9fa5.枫绅,泉孩、,。并淋?“”]+", ""));
                output.set(3,description.replaceAll(
                          "[^0-9a-zA-Z\u4e00-\u9fa5.棵譬,、,预伺。?“”]+", ""));
                output.set(4,bodyText.replaceAll(
                          "[^0-9a-zA-Z\u4e00-\u9fa5.曼尊,酬诀、,。骆撇?“”]+", ""));
                context.write(output);
            }
        }
    }
}

文本分詞

在中文文本分類中瞒御,常用的特征單元有字、詞神郊、字串(character n-gram)及其組合肴裙,中文中單個字的語義質(zhì)量不是很好,一般不作為特征單元涌乳,詞與二字串的效果相似蜻懦。在天池PAI平臺上,有基于AliWS(Alibaba Word Segmenter的簡稱)的分詞組件夕晓,若用戶指定了詞性標(biāo)注或語義標(biāo)注相關(guān)參數(shù)宛乃,則會將分詞結(jié)果、詞性標(biāo)注結(jié)果和語義標(biāo)注結(jié)果一同輸出蒸辆。在這里勾選詞性標(biāo)注征炼,在詞的初步過濾中會用到詞性。

文本分詞參數(shù)

詞初步過濾

  1. 停用詞過濾
    使用 https://github.com/JNU-MINT/TextBayesClassifier 中停用詞躬贡;
  2. 詞性過濾(僅保留動詞谆奥、名詞)
    參考[7]中實驗結(jié)果,僅適用動詞拂玻、名詞作為特征詞酸些。

詞頻統(tǒng)計

在對文章進(jìn)行分詞的基礎(chǔ)上宰译,按行保序輸出對應(yīng)文章ID列(docId)對應(yīng)文章的詞,統(tǒng)計指定文章ID列(docId)對應(yīng)文章內(nèi)容(docContent)的詞頻擂仍。
使用詞頻統(tǒng)計組件分別對title囤屹、keywords、description逢渔、bodytext進(jìn)行詞頻統(tǒng)計肋坚,得到文檔i中詞j出現(xiàn)在title、keywords肃廓、description智厌、bodytext中的個數(shù)

詞頻統(tǒng)計結(jié)果表

可以對title、keywords盲赊、description铣鹏、body設(shè)置不同的權(quán)重,測試了不同權(quán)重的組合哀蘑,當(dāng)四個標(biāo)簽權(quán)重都為1時分類精度最高诚卸,詞頻權(quán)重組合的方法如下所示:

select *, (title_count * 1 + key_count * 1 + des_count * 1 + body_count) as count from ${t1}

特征詞選擇

特征詞的選擇在很多博客與論文[1][6]中都有敘述,常見的特征提取方法有文檔詞頻(Document Frequency, DF)绘迁、信息增益(Information Gain, IG)合溺、互信息(Mutual Information, MI)、卡方統(tǒng)計(CHI)缀台、期望交叉熵(Expected Cross Entropy)等等棠赛, 由于時間限制,我們試驗了幾種通常情況表現(xiàn)好的方法進(jìn)行結(jié)合:文檔詞頻 + 信息增益 + 卡方增益膛腐,對比了幾組不同的閾值睛约,得到了較好的分類效果。

  • 文檔詞頻:在訓(xùn)練文本集中對每個詞計算其文檔頻數(shù)哲身,若該詞的DF值小于某個閾值或者大于某個閾值都要進(jìn)行去除辩涝,因為小于某個閾值代表了該詞“沒有代表性”,大于某個閾值代表了“沒有區(qū)分度”勘天。
  • 信息增益:信息增益是一種基于熵的評估方法膀值,定義為某個詞為分類能夠提供的信息量, 其值為不考慮任何詞的熵與考慮該詞后的熵的差值误辑。計算公式如下:
信息增益計算公式
  • 卡方統(tǒng)計:卡方統(tǒng)計體現(xiàn)特征詞與類別之間獨(dú)立性的缺乏程度沧踏,它同時考慮了特征詞存在與不存在的情況〗矶ぃ卡方統(tǒng)計值越大翘狱,獨(dú)立性越小,相關(guān)性越大砰苍。計算公式如下:
卡方統(tǒng)計計算公式
  • 計算步驟:
  1. 統(tǒng)計訓(xùn)練樣本的文檔的總數(shù)記為total_count潦匈;
  2. 統(tǒng)計每個類別的文檔總數(shù)分別為sexy_total_count阱高、gamble_total_count、fake_total_count茬缩、normal_total_count赤惊;
  3. 計算每個詞在各類別文檔中出現(xiàn)的總次數(shù)分別記為sexy_count、gamble_count凰锡、fake_count未舟、normal_count;
  4. 每個詞的文檔頻數(shù)doc_count = sexy_count + gamble_count + fake_count + normal_count掂为;
  5. 根據(jù)下圖中對各類型ABCD值的定義裕膀,求出sexyA、sexyB勇哗、sexyC昼扛、sexyD、gambleA欲诺、gambleB抄谐、gambleC、gambleD扰法、fakeA蛹含、fakeB、fakeC迹恐、fakeD、normalA卧斟、normalB殴边、normalC、normalD珍语;
ABCD定義
--計算特征詞在各類型中的ABCD值
select *,
sexy_count as sexyA,
doc_count - sexy_count as sexyB,
sexy_total_count - sexy_count as sexyC,
total_count - doc_count - sexy_total_count + sexy_count as sexyD,

gamble_count as gambleA,
doc_count - gamble_count as gambleB,
gamble_total_count - gamble_count as gambleC,
total_count - doc_count - gamble_total_count + gamble_count as gambleD,

fake_count as fakeA,
doc_count - fake_count as fakeB,
fake_total_count - fake_count as fakeC,
total_count - doc_count - fake_total_count + fake_count as fakeD,

normal_count as normalA,
doc_count - normal_count as normalB,
normal_total_count - normal_count as normalC,
total_count - doc_count - normal_total_count + normal_count as normalD

from ${t1}
  1. 根據(jù)卡方統(tǒng)計計算出各類型CHI值锤岸,再用下式計算詞t對于整個樣本的CHI值;
卡方統(tǒng)計值
--計算特征詞在各類型中的卡方統(tǒng)計
select *,
total_count * (sexyA * sexyD - sexyC * sexyB) * (sexyA * sexyD - sexyC * sexyB) / 
((sexyA + sexyC) * (sexyB + sexyD) * (sexyA + sexyB) * (sexyC + sexyD)) 
as sexy_x2,

total_count * (gambleA * gambleD - gambleC * gambleB) * (gambleA * gambleD - gambleC * gambleB) / 
((gambleA + gambleC) * (gambleB + gambleD) * (gambleA + gambleB) * (gambleC + gambleD)) 
as gamble_x2,

total_count * (fakeA * fakeD - fakeC * fakeB) * (fakeA * fakeD - fakeC * fakeB) / 
((fakeA + fakeC) * (fakeB + fakeD) * (fakeA + fakeB) * (fakeC + fakeD)) 
as fake_x2,

total_count * (normalA * normalD - normalC * normalB) * (normalA * normalD - normalC * normalB) / 
((normalA + normalC) * (normalB + normalD) * (normalA + normalB) * (normalC + normalD)) 
as normal_x2

from ${t1};

select *, GREATEST(sexy_x2,  gamble_x2, fake_x2, normal_x2) as x2 from ${t1};
  1. 代入信息增益公式計算出word_ig值板乙;

p(csexy) = sexy_total_count / total_count
p(t) = doc_count / total_count
p(csexy| t) = sexy_count / doc_count
p(t) = 1 - doc_count / total_count
p(csexy| t) = (sexy_total_count - sexy_count) / (total_count- doc_count)

select *,
- ((sexy_total_count / total_count * log(2, sexy_total_count / total_count )
 +  gamble_total_count / total_count * log(2, gamble_total_count / total_count )
 +  fake_total_count / total_count * log(2, fake_total_count / total_count ) 
 +  normal_total_count / total_count * log(2, normal_total_count / total_count ) ) 
+ doc_count / total_count * 
  (sexy_count / doc_count * log(2, sexy_count / doc_count + 0.01) + 
   gamble_count / doc_count * log(2, gamble_count / doc_count  + 0.01) + 
   fake_count / doc_count * log(2, fake_count / doc_count  + 0.01) + 
   normal_count / doc_count * log(2, normal_count / doc_count  + 0.01)) 
+ (1 - doc_count / total_count ) * 
  ((sexy_total_count- sexy_count) / (total_count- doc_count) * 
      log(2, (sexy_total_count - sexy_count) / (total_count- doc_count) + 0.01) + 
   (gamble_total_count - gamble_count) / (total_count - doc_count) * 
      log(2, (gamble_total_count - gamble_count) / (total_count- doc_count) + 0.01) + 
   (fake_total_count - fake_count) / (total_count - doc_count) * 
      log(2, (fake_total_count  - fake_count) / (total_count - doc_count) + 0.01) + 
   (normal_total_count- normal_count) / (total_count - doc_count) * 
      log(2, (normal_total_count - normal_count) / (total_count - doc_count) + 0.01))  
as word_ig from ${t1}
  1. 結(jié)合doc_count是偷,信息增益,卡方統(tǒng)計選擇特征詞(設(shè)置各自的閾值)募逞。

特征計算

特征計算常用tfidf蛋铆,它的形式為:

tfidf計算公式

其中tf(ti, dj)是詞在當(dāng)前文本中的局部因子,idf(ti)是只與ti相關(guān)的全局因子放接,在論文[2]中認(rèn)為idf不能充分反映詞的重要程度刺啦,總結(jié)了一些改進(jìn)的方法:
其他特征值計算方法

我們實驗了幾種在論文結(jié)論中表現(xiàn)較好的幾種方法:TFIDF、Probability based纠脾、tf * info gain玛瘸,感到驚訝的是傳統(tǒng)的TFIDF的特征計算方法表現(xiàn)最好(當(dāng)然實驗次數(shù)有限蜕青,不能充分證明)。tfidf的計算方法如下:

-- total_count 為總文檔數(shù)
-- doc_count 為特征詞在文檔中出現(xiàn)總次數(shù)
-- count 為在文檔j中特征詞i出現(xiàn)的頻數(shù)
select *, sum(tfidf_square) over ( partition by id) as tfidf_square_sum from 
(select *, count * log(2, total_count / doc_count + 0.01) * count * log(2, total_count / doc_count + 0.01) 
as tfidf_square from ${t1} ) c

select *, count * log(2, total_count / doc_count + 0.01) / sqrt(tfidf_square_sum) as tfidf from ${t1}

模型訓(xùn)練及預(yù)測

在訓(xùn)練樣本中糊渊,各類型的比值大約為50:5:3:3右核,需要考慮類別不平衡對模型訓(xùn)練的影響[4]。常見的解決方法有隨機(jī)采樣渺绒、SMOTE算法贺喝、代價矩陣等。我們采用的是隨機(jī)采樣算法芒篷,將各類型的比值降采樣到10:5:3:3時分類效果最好搜变。

總結(jié)

比賽之前對我們對特征工程、類別不平衡問題针炉、特征穿越這些基本問題一無所知挠他,都是在做題的過程中遇到了問題再去查找解決方法,加上比賽經(jīng)驗不足篡帕,過程上可謂是磕磕絆絆殖侵。給自己總結(jié)幾個經(jīng)驗和教訓(xùn):

  1. 經(jīng)典的算法適合入手,學(xué)習(xí)成本低镰烧,通過完善往往能夠得到不錯的結(jié)果拢军;
  2. 比賽的前期要不斷的嘗試不同的思路,關(guān)注賽題官方論壇怔鳖、查找相關(guān)文獻(xiàn)茉唉、和隊友交流思路、動手實驗要同步進(jìn)行结执;
  3. 比賽后期要考慮試錯的成本度陆,特別是最后關(guān)鍵時刻還是要采用最穩(wěn)妥的方法提高成績;
  4. 選擇好的隊友很關(guān)鍵献幔,要找和自己有同樣的激情和目標(biāo)的人懂傀,在這里感謝我的隊友們,一起努力走到最后蜡感。

比賽中和隊友一起進(jìn)步了很多蹬蚁,對學(xué)習(xí)機(jī)器學(xué)習(xí)的熱情也增加了不少,感謝阿里天池給我們提供了這么好的學(xué)習(xí)平臺郑兴。

參考文獻(xiàn)

  1. Zheng Z, Wu X, Srihari R. Feature selection for text categorization on imbalanced data[J]. Acm Sigkdd Explorations Newsletter, 2004, 6(1):80-89.
  2. Liu Y, Han T L, Sun A. Imbalanced text classification: A term weighting approach[J]. Expert Systems with Applications, 2009, 36(1):690-701.
  3. Shen D, Chen Z, Yang Q, et al. Web-page classification through summarization[C]// International ACM SIGIR Conference on Research and Development in Information Retrieval. ACM, 2004:242-249.
  4. He H, Garcia E A. Learning from Imbalanced Data[J]. IEEE Transactions on Knowledge & Data Engineering, 2009, 21(9):1263-1284.
  5. Golub K, Ard? A. Importance of HTML structural elements and metadata in automated subject classification[C]// European Conference on Research and Advanced Technology for Digital Libraries. Springer-Verlag, 2005:368-378.
  6. 王小青. 中文文本分類特征選擇方法研究[D]. 西南大學(xué), 2010.
  7. 段軍峰, 黃維通, 陸玉昌. 中文網(wǎng)頁分類研究與系統(tǒng)實現(xiàn)[J]. 計算機(jī)科學(xué), 2007, 34(6):210-213
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末犀斋,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子情连,更是在濱河造成了極大的恐慌闪水,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異球榆,居然都是意外死亡朽肥,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門持钉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衡招,“玉大人,你說我怎么就攤上這事每强∈继冢” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵空执,是天一觀的道長浪箭。 經(jīng)常有香客問我,道長辨绊,這世上最難降的妖魔是什么奶栖? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮门坷,結(jié)果婚禮上宣鄙,老公的妹妹穿的比我還像新娘。我一直安慰自己默蚌,他們只是感情好冻晤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绸吸,像睡著了一般鼻弧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锦茁,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天攘轩,我揣著相機(jī)與錄音,去河邊找鬼蜻势。 笑死撑刺,一個胖子當(dāng)著我的面吹牛鹉胖,可吹牛的內(nèi)容都是我干的握玛。 我是一名探鬼主播痹栖,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼祈远,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蟹但?” 一聲冷哼從身側(cè)響起寂诱,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤拂苹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后痰洒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瓢棒,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡浴韭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了脯宿。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片念颈。...
    茶點(diǎn)故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖连霉,靈堂內(nèi)的尸體忽然破棺而出榴芳,到底是詐尸還是另有隱情,我是刑警寧澤跺撼,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布窟感,位于F島的核電站,受9級特大地震影響歉井,放射性物質(zhì)發(fā)生泄漏柿祈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一酣难、第九天 我趴在偏房一處隱蔽的房頂上張望谍夭。 院中可真熱鬧,春花似錦憨募、人聲如沸紧索。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽珠漂。三九已至,卻和暖如春尾膊,著一層夾襖步出監(jiān)牢的瞬間媳危,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工冈敛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留待笑,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓抓谴,卻偏偏與公主長得像暮蹂,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子癌压,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容