Python爬蟲——異步爬蟲闯割,兩百四十多萬字彻消,六百章的小說20秒爬完?

大家好宙拉!我是霖hero宾尚。

相信很多人喜歡在空閑的時間里看小說,甚至有小部分人為了追小說而熬夜看谢澈,那么問題來了央勒,喜歡看小說的小伙伴在評論區(qū)告訴我們?yōu)槭裁聪矚g看小說,今天我們手把手教你使用異步協(xié)程20秒爬完兩百四十多萬字澳化,六百章的小說崔步,讓你一次看個夠。

在爬取之前我們先來簡單了解一下什么是同步缎谷,什么是異步協(xié)程井濒?

同步與異步

同步

同步是有序,為了完成某個任務(wù)列林,在執(zhí)行的過程中瑞你,按照順序一步一步執(zhí)行下去,直到任務(wù)完成希痴。

爬蟲是IO密集型任務(wù)者甲,我們使用requests請求庫來爬取某個站點時,網(wǎng)絡(luò)順暢無阻塞的時候砌创,正常情況如下圖所示:



但在網(wǎng)絡(luò)請求返回數(shù)據(jù)之前虏缸,程序是處于阻塞狀態(tài)的,程序在等待某個操作完成期間嫩实,自身無法繼續(xù)干別的事情刽辙,如下圖所示:



當(dāng)然阻塞可以發(fā)生在站點響應(yīng)后的執(zhí)行程序那里,執(zhí)行程序可能是下載程序甲献,大家都知道下載是需要時間的宰缤。

當(dāng)站點沒響應(yīng)或者程序卡在下載程序的時候,CPU一直在等待而不去執(zhí)行其他程序,那么就白白浪費了CPU的資源慨灭,導(dǎo)致我們的爬蟲效率很低朦乏。

異步

異步是一種比多線程高效得多的并發(fā)模型,是無序的氧骤,為了完成某個任務(wù)呻疹,在執(zhí)行的過程中,不同程序單元之間過程中無需通信協(xié)調(diào)语淘,也能完成任務(wù)的方式诲宇,也就是說不相關(guān)的程序單元之間可以是異步的。如下圖所示:



當(dāng)請求程序發(fā)送網(wǎng)絡(luò)請求1并收到某個站點的響應(yīng)后惶翻,開始執(zhí)行程序中的下載程序姑蓝,由于下載需要時間或者其他原因使處于阻塞狀態(tài),請求程序和下載程序是不相關(guān)的程序單元吕粗,所以請求程序發(fā)送下一個網(wǎng)絡(luò)請求纺荧,也就是異步。

  • 微觀上異步協(xié)程是一個任務(wù)一個任務(wù)的進行切換颅筋,切換條件一般就是IO操作宙暇;
  • 宏觀上異步協(xié)程是多個任務(wù)一起在執(zhí)行;

注意:上面我們所講的一切都是在單線程的條件下實現(xiàn)议泵。

請求庫

我們發(fā)送網(wǎng)絡(luò)請求一定要用到請求庫占贫,在Python從多的HTTP客戶端中,最常用的請求庫莫過于requests先口、aiohttp型奥、httpx。

在不借助其他第三方庫的情況下碉京,requests只能發(fā)送同步請求厢汹;aiohttp只能發(fā)送異步請求;httpx既能發(fā)送同步請求谐宙,又能發(fā)送異步請求烫葬。

接下來我們將簡單講解這三個庫。

requests庫

相信大家對requests庫不陌生吧凡蜻,requests庫簡單搭综、易用,是python爬蟲使用最多的庫咽瓷。

在命令行中運行如下代碼设凹,即可完成requests庫的安裝:

pip install requests

使用requests發(fā)送網(wǎng)絡(luò)請求非常簡單,

在本例中茅姜,我們使用get網(wǎng)絡(luò)請求來獲取百度首頁的源代碼,具體代碼如下:

import requests
headers={
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36'
}
response=requests.get('https://baidu.com',headers=headers)
response.encoding='utf-8'
print(response.text)

運行部分結(jié)果如下圖:



首先我們導(dǎo)入requests庫,創(chuàng)建請求頭钻洒,請求頭中包含了User-Agent字段信息奋姿,也就是瀏覽器標識信息,如果不加這個素标,網(wǎng)站就可能禁止抓取称诗,然后調(diào)用get()方法發(fā)送get請求,傳入的參數(shù)為URL鏈接和請求頭头遭,這樣簡單的網(wǎng)絡(luò)請求就完成了寓免。

這里我們返回打印輸出的是百度的源代碼,大家可以根據(jù)需求返回輸出其他類型的數(shù)據(jù)计维。

需要注意的是:

百度源代碼的head部分的編碼為:utf-8袜香,如下圖所示:



我們利用requests庫的方法來查看默認的編碼類型是什么,具體代碼如下所示:

import requests
url = 'https://www.baidu.com'
response = requests.get(url)
print(response.encoding)  

運行結(jié)果為:ISO-8859-1

由于默認的編碼類型不同鲫惶,所以需要更改輸出的編碼類型蜈首,更改方式也很簡單,只需要在返回數(shù)據(jù)前根據(jù)head部分的編碼來添加以下代碼即可:

response.encoding='編碼類型'

除了使用get()方法實現(xiàn)get請求外欠母,還可以使用post()欢策、put()、delete()等方法來發(fā)送其他網(wǎng)絡(luò)請求赏淌,在這里就不一一演示了踩寇,關(guān)于更多的requests網(wǎng)絡(luò)請求庫用法可以到官方參考文檔進行查看,我們今天主要講解可以發(fā)送異步請求的aiohttp庫和httpx庫六水。

asyncio模塊

在講解異步請求aiohttp庫和httpx庫請求前俺孙,我們需要先了解一下協(xié)程。

協(xié)程是一種比線程更加輕量級的存在缩擂,讓單線程跑出了并發(fā)的效果鼠冕,對計算資源的利用率高,開銷小的系統(tǒng)調(diào)度機制胯盯。

Python中實現(xiàn)協(xié)程的模塊有很多懈费,我們主要來講解asyncio模塊,從asyncio模塊中直接獲取一個EventLoop的引用博脑,把需要執(zhí)行的協(xié)程放在EventLoop中執(zhí)行憎乙,這就實現(xiàn)了異步協(xié)程。

協(xié)程通過async語法進行聲明為異步協(xié)程方法叉趣,await語法進行聲明為異步協(xié)程可等待對象泞边,是編寫asyncio應(yīng)用的推薦方式,具體示例代碼如下:

import asyncio
import time
async def function1():
    print('I am SupermanA粕肌U笱琛蚕礼!')
    await asyncio.sleep(3)
    print('function1')

async def function2():
    print('I am Batman!I沂病奠蹬!')
    await asyncio.sleep(2)
    print('function2')

async def function3():
    print('I am iron man!N宋纭囤躁!')
    await asyncio.sleep(4)
    print('function3')
    
async def Main():
    tasks=[
        asyncio.create_task(function1()),
        asyncio.create_task(function2()),
        asyncio.create_task(function3()),
    ]
    await asyncio.wait(tasks)
    
if __name__ == '__main__':
    t1=time.time()
    asyncio.run(Main())
    t2=time.time()
    print(t2-t1)

運行結(jié)果為:

I am Superman!@蠖谩狸演!
I am Batman!F宵距!
I am iron man!V薪消玄!
function2
function1
function3
4.0091118812561035

首先我們用了async來聲明三個功能差不多的方法,分別為function1丢胚,function2翩瓜,function3,在方法中使用了await聲明為可等待對象携龟,并使用asyncio.sleep()方法使函數(shù)休眠一段時間兔跌。

再使用async來聲明Main()方法,通過調(diào)用asyncio.create_task()方法將方法封裝成一個任務(wù)峡蟋,并把這些任務(wù)存放在列表tasks中坟桅,這些任務(wù)會被自動調(diào)度執(zhí)行;

最后通過asyncio.run()運行協(xié)程程序蕊蝗。

注意:當(dāng)協(xié)程程序出現(xiàn)了同步操作的時候仅乓,異步協(xié)程就中斷了。

例如把上面的示例代碼中的await asyncio.sleep()換成time.time()蓬戚,運行結(jié)果為:

I am Superman?溟埂!子漩!
function1
I am BatmanTバ!幢泼!
function2
I am iron man=粝浴!缕棵!
function3
9.014737844467163

所以在協(xié)程程序中孵班,盡量不使用同步操作涉兽。

好了,asyncio模塊我們講解到這里重父,想要了解更多的可以進入asyncio官方文檔進行查看花椭。

aiohttp庫

aiohttp是基于asyncio實現(xiàn)的HTTP框架忽匈,用于HTTP服務(wù)器和客戶端房午。安裝方法如下:

pip install aiohttp

aiohttp只能發(fā)送異步請求,示例代碼如下所示:

import aiohttp
import asyncio
headers={
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.107 Safari/537.36'
}
async def Main():
    async with aiohttp.ClientSession() as session:
        async with session.get('https://www.baidu.com',headers=headers) as response:
            html = await response.text()
            print(html)
loop=asyncio.get_event_loop()
loop.run_until_complete(Main())

運行結(jié)果和前面介紹的requests網(wǎng)絡(luò)請求一樣丹允,如下圖所示:



大家可以對比requests網(wǎng)絡(luò)請求發(fā)現(xiàn)郭厌,其實aiohttp.ClientSession() as session相當(dāng)于將requests賦給session,也就是說session相當(dāng)于requests雕蔽,而發(fā)送網(wǎng)絡(luò)請求折柠、傳入的參數(shù)、返回響應(yīng)內(nèi)容都和requests請求庫大同小異批狐,只是aiohttp請求庫需要用async和await進行聲明扇售,然后調(diào)用asyncio.get_event_loop()方法進入事件循環(huán),再調(diào)用loop.run_until_complete(Main())方法運行事件循環(huán)嚣艇,直到Main方法運行結(jié)束承冰。

注意:在調(diào)用Main()方法時,不能使用下面這條語句:

asyncio.run(Main())

雖然會得到想要的響應(yīng)食零,但會報:RuntimeError: Event loop is closed錯誤困乒。

我們還可以在返回的內(nèi)容中指定解碼方式或編碼方式,例如:

await response.text(encoding='utf-8')

或者選擇不編碼贰谣,讀取圖像:

await resp.read()

好了aiohttp請求庫我們學(xué)到這里娜搂,想要了解更多的可以到pypi官網(wǎng)進行學(xué)習(xí)。

httpx請求庫

在前面我們簡單地講解了requests請求庫和aiohttp請求庫吱抚,requests只能發(fā)送同步請求百宇,aiohttp只能發(fā)送異步請求,而httpx請求庫既可以發(fā)送同步請求秘豹,又可以發(fā)送異步請求携御,而且比上面兩個效率更高。

安裝方法如下:

pip install httpx

httpx請求庫——同步請求

使用httpx發(fā)送同步網(wǎng)絡(luò)請求也很簡單憋肖,與requests代碼重合度99%因痛,只需要把requests改成httpx即可正常運行。

具體示例代碼如下:

import httpx
headers={
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36'
}
response=httpx.get('https://www.baidu.com',headers=headers)
print(response.text)

運行結(jié)果如下圖所示:



注意:httpx使用的默認utf-8進行編碼來解碼響應(yīng)岸更。

httpx請求庫——同步請求高級用法

當(dāng)發(fā)送請求時鸵膏,httpx必須為每個請求建立一個新連接(連接不會被重用),隨著對主機的 請求數(shù)量增加怎炊,網(wǎng)絡(luò)請求的效率就是變得很低谭企。

這時我們可以用Client實例來使用HTTP連接池廓译,這樣當(dāng)我們主機發(fā)送多個請求時,Client將重用底層的TCP連接债查,而不是為重新創(chuàng)建每個請求非区。

with塊用法如下:

with httpx.Client() as client:
    ...

我們把Client作為上下文管理器,并使用with塊盹廷,當(dāng)執(zhí)行完with語句時征绸,程序會自動清理連接。

當(dāng)然我們可以使用.close()顯式關(guān)閉連接池俄占,用法如下:

client = httpx.Client()
try:
    ...
finally:
    client.close()

為了我們的代碼更簡潔管怠,我們推薦使用with塊寫法,具體示例代碼如下:

import httpx
headers={
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36'
}
with httpx.Client(headers=headers)as client:
    response=client.get('https://www.baidu.com')
    print(response.text)

其中httpx.Client()as client相當(dāng)于把httpx的功能傳遞給client缸榄,也就是說示例中的client相當(dāng)于httpx渤弛,接著我們就可以使用client來調(diào)用get請求。

注意:我們傳遞的參數(shù)可以放在httpx.Client()里面甚带,也可以傳遞到get()方法里面她肯。

httpx請求庫——異步請求

要發(fā)送異步請求時,我們需要調(diào)用AsyncClient鹰贵,具體示例代碼如下:

import httpx
import asyncio
headers={
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36'
}
async def function():
    async with httpx.AsyncClient()as client:
        response=await client.get('https://www.baidu.com',headers=headers)
        print(response.text)
if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    loop.run_until_complete(function())

運行結(jié)果為:



首先我們導(dǎo)入了httpx庫和asyncio模塊晴氨,使用async來聲明function()方法并用來聲明with塊的客戶端打開和關(guān)閉,用await來聲明異步協(xié)程可等待對象response砾莱。接著我們調(diào)用asyncio.get_event_loop()方法進入事件循環(huán)瑞筐,再調(diào)用loop.run_until_complete(function())方法運行事件循環(huán),直到function運行結(jié)束腊瑟。

好了聚假,httpx請求庫講解到這里,想要了解更多的可以到httpx官方文檔進行學(xué)習(xí)闰非,接下來我們正式開始爬取小說膘格。

實戰(zhàn)演練

接下來我們將使用requests請求庫同步和httpx請求庫的異步,兩者結(jié)合爬取17k小說網(wǎng)里面的百萬字小說财松,利用XPath來做相應(yīng)的信息提取瘪贱。

Xpath小技巧

在使用Xpath之前,我們先來介紹使用Xpath的小技巧辆毡。

技巧一:快速獲取與內(nèi)容匹配的Xpath范圍菜秦。

我們可以將鼠標移動到我們想要獲取到內(nèi)容div的位置并右擊選擇copy,如下圖所示:



這樣我們就可以成功獲取到內(nèi)容匹配的Xpath范圍了舶掖。

技巧二:快速獲取Xpath范圍匹配的內(nèi)容球昨。

當(dāng)我們寫好Xpath匹配的范圍后,可以通過Chrome瀏覽器的小插件Xpath Helper眨攘,該插件的安裝方式很簡單主慰,在瀏覽器應(yīng)用商店中搜索Xpath Helper嚣州,點擊添加即可,如下圖所示:



使用方法也很簡單共螺,如下圖所示:



首先我們點擊剛剛添加的插件该肴,然后把已經(jīng)寫好的Xpath范圍寫到上圖2的方框里面,接著Xpath匹配的內(nèi)容將出現(xiàn)在上圖3方框里面藐不,接著被匹配內(nèi)容的背景色全部變成了金色匀哄,那么我們匹配內(nèi)容就一目了然了。

這樣我們就不需要每寫一個Xpath范圍就運行一次程序查看匹配內(nèi)容佳吞,大大提高了我們效率拱雏。

獲取小說章節(jié)名和鏈接

首先我們選取爬取的目標小說,并打開開發(fā)者工具底扳,如下圖所示:




我們通過上圖可以發(fā)現(xiàn),<div class="Main List"存放著我們所有小說章節(jié)名贡耽,點擊該章節(jié)就可以跳轉(zhuǎn)到對應(yīng)的章節(jié)頁面衷模,所以可以使用Xpath來通過這個div來獲取到我們想要的章節(jié)名和URL鏈接。

由于我們獲取的章節(jié)名和URL鏈接的網(wǎng)絡(luò)請求只有一個蒲赂,直接使用requests請求庫發(fā)送同步請求阱冶,主要代碼如下所示:

async def get_link(url):
    response=requests.get(url)
    response.encoding='utf-8'
    Xpath=parsel.Selector(response.text)
    dd=Xpath.xpath('/html/body/div[5]')
    for a in dd:
        #獲取每章節(jié)的url鏈接
        links=a.xpath('./dl/dd/a/@href').extract()
        linklist=['https://www.17k.com'+link for link in links]
        #獲取每章節(jié)的名字
        names=a.xpath('./dl/dd/a/span/text()').extract()
        namelist=[name.replace('\n','').replace('\t','') for name in names]
        #將名字和url鏈接合并成一個元組
        name_link_list=zip(namelist,linklist)

首先我們用async聲明定義的get_text()方法使用requests庫發(fā)送get請求并把解碼方式改成'utf-8',接著使用parsel.Selector()方法將文本構(gòu)成Xpath解析對象滥嘴,最后我們將獲取到的URL鏈接和章節(jié)名合并成一個元組木蹬。

獲取到URL鏈接和章節(jié)名后,需要構(gòu)造一個task任務(wù)列表來作為異步協(xié)程的可等待對象若皱,具體代碼如下所示:

task=[]
for name,link in name_link_list:
    task.append(get_text(name,link))
await asyncio.wait(task)

我們創(chuàng)建了一個空列表镊叁,用來存放get_text()方法,并使用await調(diào)用asyncio.wait()方法保存創(chuàng)建的task任務(wù)走触。

獲取每章節(jié)的小說內(nèi)容

由于需要發(fā)送很多個章節(jié)的網(wǎng)絡(luò)請求晦譬,所以我們采用httpx請求庫來發(fā)送異步請求。

主要代碼如下所示:

async def get_text(name,link):
    async with httpx.AsyncClient() as client:
        response=await client.get(link)
        html=etree.HTML(response.text)
        text=html.xpath('//*[@id="readArea"]/div[1]/div[2]/p/text()')
        await save_data(name,text)

首先我們將上一步的獲取到的章節(jié)名和URL鏈接傳遞到用async聲明定義的get_text()方法互广,使用with塊調(diào)用httpx.AsyncClient()方法敛腌,并使用await來聲明client.get()是可等待對象,然后使用etree模塊來構(gòu)造一個XPath解析對象并自動修正HTML文本惫皱,將獲取到的小說內(nèi)容和章節(jié)名傳入到自定義方法save_data中像樊。

保存小說內(nèi)容到text文本中

好了,我們已經(jīng)把章節(jié)名和小說內(nèi)容獲取下來了旅敷,接下來就要把內(nèi)容保存在text文本中生棍,具體代碼如下所示:

async def save_data(name,text):
    f=open(f'小說/{name}.txt','w',encoding='utf-8')
    for texts in text:
        f.write(texts)
        f.write('\n')
        print(f'正在爬取{name}')

老規(guī)矩,首先用async來聲明save_data()協(xié)程方法save_data()扫皱,然后使用open()方法足绅,將text文本文件打開并調(diào)用write()方法把小說內(nèi)容寫入文本中捷绑。

最后調(diào)用asyncio.get_event_loop()方法進入事件循環(huán),再調(diào)用loop.run_until_complete(get_link())方法運行事件循環(huán)氢妈,直到function運行結(jié)束粹污。具體代碼如下所示:

url='https://www.17k.com/list/2536069.html'
loop = asyncio.get_event_loop()
loop.run_until_complete(get_link(url))

結(jié)果展示


image

好了,異步爬蟲爬取小說就講解到這里了首量,感謝觀看W撤浴!加缘!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鸭叙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子拣宏,更是在濱河造成了極大的恐慌沈贝,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件勋乾,死亡現(xiàn)場離奇詭異宋下,居然都是意外死亡,警方通過查閱死者的電腦和手機辑莫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評論 3 395
  • 文/潘曉璐 我一進店門学歧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人各吨,你說我怎么就攤上這事枝笨。” “怎么了揭蜒?”我有些...
    開封第一講書人閱讀 165,345評論 0 356
  • 文/不壞的土叔 我叫張陵横浑,是天一觀的道長。 經(jīng)常有香客問我忌锯,道長伪嫁,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評論 1 295
  • 正文 為了忘掉前任偶垮,我火速辦了婚禮张咳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘似舵。我一直安慰自己脚猾,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,868評論 6 392
  • 文/花漫 我一把揭開白布砚哗。 她就那樣靜靜地躺著龙助,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上提鸟,一...
    開封第一講書人閱讀 51,688評論 1 305
  • 那天军援,我揣著相機與錄音,去河邊找鬼称勋。 笑死胸哥,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的赡鲜。 我是一名探鬼主播空厌,決...
    沈念sama閱讀 40,414評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼银酬!你這毒婦竟也來了嘲更?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,319評論 0 276
  • 序言:老撾萬榮一對情侶失蹤揩瞪,失蹤者是張志新(化名)和其女友劉穎赋朦,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體壮韭,經(jīng)...
    沈念sama閱讀 45,775評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡北发,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了喷屋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,096評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡瞭恰,死狀恐怖屯曹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情惊畏,我是刑警寧澤恶耽,帶...
    沈念sama閱讀 35,789評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站颜启,受9級特大地震影響偷俭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缰盏,卻給世界環(huán)境...
    茶點故事閱讀 41,437評論 3 331
  • 文/蒙蒙 一涌萤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧口猜,春花似錦负溪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至须尚,卻和暖如春崖堤,著一層夾襖步出監(jiān)牢的瞬間侍咱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評論 1 271
  • 我被黑心中介騙來泰國打工密幔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留楔脯,地道東北人。 一個月前我還...
    沈念sama閱讀 48,308評論 3 372
  • 正文 我出身青樓老玛,卻偏偏與公主長得像淤年,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蜡豹,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,037評論 2 355

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