從零開始教你寫一個NPM包

前言

本文主要記錄我開發(fā)一個npm包:pixiv-login時的心得體會,其中穿插了一些日常開發(fā)的流程和技巧奔垦,希望對新手有所啟發(fā),大佬們看看就好_(:3」∠)

pixiv-login

pixiv-login的功能就是模擬用戶登錄網(wǎng)站pixiv尸疆,獲取cookie
源碼 npm

安裝:

npm install --save pixiv-login

使用:

const pixivLogin = require('pixiv-login');

pixivLogin({
    username: '你的用戶名',
    password: '你的密碼'
}).then((cookie) => {
    console.log(cookie);
}).catch((error) => {
    console.log(error);
})

開發(fā)工具

日常開發(fā)中椿猎,我常用的IDE是vscode+webstorm+sublime,其中vscode因為其啟動快寿弱,功能多犯眠,調(diào)試方便,受到大多數(shù)開發(fā)者的青睞症革。在接下來的教程中筐咧,我就以vscode進(jìn)行演示了。至于終端噪矛,由于是在windows平臺量蕊,所以我選擇了cmder代替原生cmd,畢竟cmder支持大多數(shù)linux命令艇挨。

初始化項目

mkdir pixiv-login
cd pixiv-login
npm init

一路回車就好

安裝依賴

要模擬登陸残炮,我們就需要一個http庫,這里我選擇了axios缩滨,同時獲取的html字符串我們需要解析势就,cheerio就是首選了

npm i axios cheerio --save

debug

畢業(yè)參加工作也有幾個月了,其中學(xué)到了很重要的一個技能就是debug脉漏。說出來也不怕大家笑苞冯,在大學(xué)時,debug就是console.log大法好侧巨,基本就不用斷點來追蹤舅锄,其實用好斷點,效率比console.log要高很多刃泡。還記得當(dāng)初看到同事花式debug時巧娱,心中不禁感慨:為什么你們會這么熟練啊烘贴!

使用vscode進(jìn)行node調(diào)試是非常方便的

首先新建一個index.js文件禁添,項目結(jié)構(gòu)如下(我本地的npm版本是5.x,所以會多一個package-lock.json文件桨踪,npm版本3.x的沒有該文件):


pic

然后點擊左側(cè)第4個圖標(biāo)老翘,添加配置


pic

配置文件如下:

{
    // Use IntelliSense to learn about possible Node.js debug attributes.
    // Hover to view descriptions of existing attributes.
    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
    "version": "0.2.0",
    "configurations": [
        {
            "type": "node",
            "request": "launch",
            "name": "Launch Program",
            "program": "${workspaceRoot}\\index.js"
        }
    ]
}

其中最重要的一句是:"program": "${workspaceRoot}\\index.js",它表示debug時,項目的啟動文件是index.js
至此铺峭,debug的配置就完成了墓怀。

現(xiàn)在編寫index.js文件

const axios = require('axios');
const cheerio = require('cheerio');

axios.get('https://www.pixiv.net')
    .then(function (response) {
        const $ = cheerio.load(response.data);
        const title = $('title').text();
        debugger;
        console.log(title);
    })
    .catch(function (error) {
        console.log(error);
    });

按下F5啟動調(diào)試模式,如果一切正常卫键,那么效果如下:


pic

可以看到傀履,程序卡在了第8行
如果你把鼠標(biāo)移到response變量上,可以發(fā)現(xiàn)莉炉,vscode會自動顯示該變量的值钓账,這比直接console.log(response)清晰簡潔多了

pic

pic

如果想繼續(xù)執(zhí)行程序,可以接著按下F5或者右上角的綠色箭頭


pic

程序執(zhí)行完成絮宁,控制臺打出了pixiv首頁的title值


pic

除了使用debugger語句打斷點梆暮,你也可以直接點擊代碼的行數(shù)打斷點

pic

比如上圖,我就在第8行處打了一個斷點绍昂,效果是一樣的

還有一個小技巧啦粹,在debug模式下,你可以隨意修改變量的值窘游,比如現(xiàn)在程序卡在了第8行唠椭,這時你在控制臺修改title的值


pic

按下回車,然后繼續(xù)執(zhí)行代碼张峰,這時控制臺輸出的title值就是'deepred'泪蔫,而不是真正的title值


pic

這個技巧,在平時開發(fā)過程中喘批,當(dāng)需要繞過某些驗證時,非常有用

正式開始

雖然我們最后是要寫一個npm包铣揉,但是首先饶深,我們先把獲取cookie的功能實現(xiàn)了,然后再思考怎么封裝為一個npm包逛拱,供其他人使用敌厘。

進(jìn)入登錄頁面 https://accounts.pixiv.net/login?lang=zh&source=pc&view_type=page&ref=wwwtop_accounts_index,我們先登錄一次朽合,看看前端向后臺發(fā)送了哪些數(shù)據(jù)

pic

這里需要特別注意俱两,我們要勾選preserve log,這樣曹步,即使頁面刷新跳轉(zhuǎn)了宪彩,http請求記錄仍然會記錄下來

pic

pic

pic

可以看到,post_key是登錄的關(guān)鍵點讲婚,P站使用了該值來防止CSRF
post_key怎么獲取呢尿孔?
經(jīng)過頁面分析,發(fā)現(xiàn)在登錄頁面,有個隱藏表單域(后來發(fā)現(xiàn)活合,其實在首頁就已經(jīng)寫出來了):

pic

可以清楚看到雏婶,post_key已經(jīng)寫出來了,我們只需要用cheerio解析出該input的值就ok了

const post_key = $('input[name="post_key"]').val();

獲取post_key

const axios = require('axios');
const cheerio = require('cheerio');

const LOGIN_URL = 'https://accounts.pixiv.net/login?lang=zh&source=pc&view_type=page&ref=wwwtop_accounts_index';
const USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36';
const LOGIN_API = 'https://accounts.pixiv.net/api/login?lang=zh';


const getKey = axios({
    method: 'get',
    url: LOGIN_URL,
    headers: {
        'User-Agent': USER_AGENT
    }
}).then((response) => {
    const $ = cheerio.load(response.data);
    const post_key = $('input[name="post_key"]').val();
    const cookie = response.headers['set-cookie'].join('; ');
    if (post_key && cookie) {
        return { post_key, cookie };
    }
    return Promise.reject("no post_key");
}).catch((error) => {
    console.log(error);
});


getKey.then(({ post_key, cookie }) => {
    debugger;
})

F5運行代碼


pic
pic

注意:打開注冊頁時白指,注冊頁會返回一些cookie留晚,這些cookie在登錄時也是需要隨密碼,用戶名一起發(fā)送過去的

獲取到了post_key, cookie告嘲,我們就可以愉快的把登錄數(shù)據(jù)發(fā)送給后臺接口了

const querystring = require('querystring');
getKey.then(({ post_key, cookie }) => {
    axios({
        method: 'post',
        url: LOGIN_API,
        headers: {
            'User-Agent': USER_AGENT,
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'Origin': 'https://accounts.pixiv.net',
            'Referer': 'https://accounts.pixiv.net/login?lang=zh&source=pc&view_type=page&ref=wwwtop_accounts_index',
            'X-Requested-With': 'XMLHttpRequest',
            'Cookie': cookie
        },
        data: querystring.stringify({
            pixiv_id: '你的用戶名',
            password: '你的密碼',
            captcha: '',
            g_recaptcha_response: '',
            post_key: post_key,
            source: 'pc',
            ref: 'wwwtop_accounts_index',
            return_to: 'http://www.pixiv.net/'
        })
    }).then((response) => {
        if (response.headers['set-cookie']) {
            const cookie = response.headers['set-cookie'].join(' ;');
            debugger;
        } else {
            return Promise.reject(new Error("no cookie"))
        }

    }).catch((error) => {
       console.log(error);
    });
});

注意其中這段代碼:

data: querystring.stringify({
        pixiv_id: '你的用戶名',
        password: '你的密碼',
        captcha: '',
        g_recaptcha_response: '',
        post_key: post_key,
        source: 'pc',
        ref: 'wwwtop_accounts_index',
        return_to: 'http://www.pixiv.net/'
    })

這里有個巨大的坑错维,axios默認(rèn)把數(shù)據(jù)轉(zhuǎn)成json格式,如果你想發(fā)送application/x-www-form-urlencoded的數(shù)據(jù)状蜗,就需要使用querystring模塊
詳情見: using-applicationx-www-form-urlencoded-format

如果一切正常需五,那么效果如下:


pic

其中的PHPSESSID和device_token就是服務(wù)器端返回的登錄標(biāo)識,說明我們登錄成功了

程序運行的同時轧坎,你也很可能收到P站的登錄郵件


pic

好了宏邮,目前為止,我們已經(jīng)成功獲取到了cookie缸血,實現(xiàn)了最基本的功能蜜氨。

特別注意
程序不要運行太多次,因為每次運行捎泻,你就登錄一次P站飒炎,如果被P站監(jiān)測到頻繁登錄,它會開啟驗證碼模式笆豁,這時郎汪,你除了需要發(fā)送用戶名和密碼,還需要向后臺發(fā)送驗證碼值

data: querystring.stringify({
            pixiv_id: '你的用戶名',
            password: '你的密碼',
            captcha: '你還需要填驗證碼',
            g_recaptcha_response: '',
            post_key: post_key,
            source: 'pc',
            ref: 'wwwtop_accounts_index',
            return_to: 'http://www.pixiv.net/'
        })

也就是闯狱,captcha字段不再是空值了煞赢!

基本功能的完整代碼

const axios = require('axios');
const cheerio = require('cheerio');
const querystring = require('querystring');

const LOGIN_URL = 'https://accounts.pixiv.net/login?lang=zh&source=pc&view_type=page&ref=wwwtop_accounts_index';
const USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36';
const LOGIN_API = 'https://accounts.pixiv.net/api/login?lang=zh';


const getKey = axios({
    method: 'get',
    url: LOGIN_URL,
    headers: {
        'User-Agent': USER_AGENT
    }
}).then((response) => {
    const $ = cheerio.load(response.data);
    const post_key = $('input[name="post_key"]').val();
    const cookie = response.headers['set-cookie'].join('; ');

    if (post_key && cookie) {
        return { post_key, cookie };
    }
    return Promise.reject("no post_key");
}).catch((error) => {
    console.log(error);
});


getKey.then(({ post_key, cookie }) => {
    axios({
        method: 'post',
        url: LOGIN_API,
        headers: {
            'User-Agent': USER_AGENT,
            'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
            'Origin': 'https://accounts.pixiv.net',
            'Referer': 'https://accounts.pixiv.net/login?lang=zh&source=pc&view_type=page&ref=wwwtop_accounts_index',
            'X-Requested-With': 'XMLHttpRequest',
            'Cookie': cookie
        },
        data: querystring.stringify({
            pixiv_id: '你的用戶名',
            password: '你的密碼',
            captcha: '',
            g_recaptcha_response: '',
            post_key: post_key,
            source: 'pc',
            ref: 'wwwtop_accounts_index',
            return_to: 'http://www.pixiv.net/'
        })
    }).then((response) => {
        if (response.headers['set-cookie']) {
            const cookie = response.headers['set-cookie'].join(' ;');
            console.log(cookie);
        } else {
            return Promise.reject(new Error("no cookie"));
        }

    }).catch((error) => {
        console.log(error);
    });
});

封裝成一個npm包

登錄P站獲取cookie這個功能,如果我們想讓其他開發(fā)者也能方便調(diào)用哄孤,就可以考慮將其封裝為一個npm包發(fā)布出去照筑,這也算是對開源社區(qū)做出自己的一份貢獻(xiàn)。

首先我們回想一下瘦陈,我們調(diào)用其他npm包時是怎么做的凝危?

const cheerio = require('cheerio');
const $ = cheerio.load(response.data);

同理,我們現(xiàn)在規(guī)定pixiv-login的用法:

const pixivLogin = require('pixiv-login');

pixivLogin({
    username: '你的用戶名',
    password: '你的密碼'
}).then((cookie) => {
    console.log(cookie);
}).catch((error) => {
    console.log(error);
})

pixiv-login對外暴露一個函數(shù)晨逝,該函數(shù)接受一個配置對象蛾默,里面記錄了用戶名和密碼

現(xiàn)在,我們來改造index.js

const pixivLogin = ({ username, password }) => {
    
};


module.exports = pixivLogin;

最基本的骨架就是定義一個函數(shù)咏花,然后把該函數(shù)導(dǎo)出

由于我們需要支持Promise寫法趴生,所以導(dǎo)出的pixivLogin本身要返回一個Promise

const pixivLogin = ({ username, password }) => {
    return new Promise((resolve, reject) => {
        
    })
};

之后阀趴,只要把原先的代碼套進(jìn)去就好了

完整代碼:

const axios = require('axios');
const cheerio = require('cheerio');
const querystring = require('querystring');

const LOGIN_URL = 'https://accounts.pixiv.net/login?lang=zh&source=pc&view_type=page&ref=wwwtop_accounts_index';
const USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36';
const LOGIN_API = 'https://accounts.pixiv.net/api/login?lang=zh';


const pixivLogin = ({ username, password }) => {
    return new Promise((resolve, reject) => {
        const getKey = axios({
            method: 'get',
            url: LOGIN_URL,
            headers: {
                'User-Agent': USER_AGENT
            }
        }).then((response) => {
            const $ = cheerio.load(response.data);
            const post_key = $('input[name="post_key"]').val();
            const cookie = response.headers['set-cookie'].join('; ');

            if (post_key && cookie) {
                return { post_key, cookie };
            }
            reject(new Error('no post_key'));
        }).catch((error) => {
            reject(error);
        });

        getKey.then(({ post_key, cookie }) => {
            axios({
                method: 'post',
                url: LOGIN_API,
                headers: {
                    'User-Agent': USER_AGENT,
                    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
                    'Origin': 'https://accounts.pixiv.net',
                    'Referer': 'https://accounts.pixiv.net/login?lang=zh&source=pc&view_type=page&ref=wwwtop_accounts_index',
                    'X-Requested-With': 'XMLHttpRequest',
                    'Cookie': cookie
                },
                data: querystring.stringify({
                    pixiv_id: username,
                    password: password,
                    captcha: '',
                    g_recaptcha_response: '',
                    post_key: post_key,
                    source: 'pc',
                    ref: 'wwwtop_accounts_index',
                    return_to: 'http://www.pixiv.net/'
                })
            }).then((response) => {
                if (response.headers['set-cookie']) {
                    const cookie = response.headers['set-cookie'].join(' ;');
                    resolve(cookie);
                } else {
                    reject(new Error('no cookie'));
                }
                
            }).catch((error) => {
                reject(error);
            });
        });
    })
}

module.exports = pixivLogin;

發(fā)布npm包

README

每個npm包,一般都需要配一段介紹文字苍匆,來告訴使用者如何安裝使用刘急,比如lodash的首頁

pic

新建一個README.md,填寫相關(guān)信息


pic

有時浸踩,我們會看到一些npm包有很漂亮的版本號圖標(biāo):


pic

這些圖標(biāo)叔汁,其實可以在https://shields.io/ 上制作
登錄該網(wǎng)站,下拉到最下面

pic

輸入你想要的文字检碗,版本號据块,顏色, 然后點擊按鈕
pic

就可以得到圖片的訪問地址了
pic

修改剛才的README.md折剃,加上我們的版本號吧!
pic

gitignore

我們現(xiàn)在的文件夾目錄應(yīng)該如下所示:


pic

其實node_modules以及.vscode是完全不用上傳的另假,所以為了防止發(fā)布時帶上這些文件夾,我們要新建一個.gitignore

.vscode/
node_modules/

注冊

https://www.npmjs.com/ 上注冊一個賬號
然后在終端輸入

npm adduser

輸入用戶名怕犁,密碼边篮,郵箱即可登入成功
這里還有一個坑!
如果你的npm使用的是淘寶鏡像奏甫,那么是無法登陸成功的
最簡單的解決方法:

npm i nrm -g
nrm use npm

nrm是個npm鏡像管理工具戈轿,可以很方便的切換鏡像源

登陸成功后,輸入

npm whoami
pic

如果出現(xiàn)了你的用戶名阵子,說明你已經(jīng)成功登陸了

發(fā)布

特別注意
因為pixiv-login這個名字已經(jīng)被我占用了思杯,所以你需要改成其他名字
修改pacakge.json文件的name字段

npm publish

即可發(fā)布成功啦!

下載

發(fā)布成功后挠进,我們就可以下載自己的包了

npm i pixiv-login

使用pixiv-login包

我們可以用pixiv-login做一些有趣(♂)的事
比如:

下載 R-18每周排行榜的圖片

沒登錄的用戶是無法訪問R18區(qū)的色乾,所以我們需要模擬登陸


const fs = require('fs');
const axios = require('axios');
const pixivLogin = require('pixiv-login');
const cheerio = require('cheerio');

const USER_AGENT = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36';


pixivLogin({
    username: '你的用戶名',
    password: '你的密碼'
}).then((cookie) => {
    // 把cookie寫入文件中,則下次無需再次獲取cookie领突,直接讀取文件即可
    fs.writeFileSync('cookie.txt', cookie);
}).then((response) => {
    const cookie = fs.readFileSync('cookie.txt', 'utf8');
    axios({
        method: 'get',
        url: 'https://www.pixiv.net/ranking.php?mode=weekly_r18',
        headers: {
            'User-Agent': USER_AGENT,
            'Referer': 'https://www.pixiv.net',
            'Cookie': cookie
        },
    })
        .then(function (response) {
            const $ = cheerio.load(response.data);
            const src = $('#1 img').data('src');
            return src;
        }).then(function (response) {
            axios({
                method: 'get',
                url: response,
                responseType: 'stream'
            })
                .then(function (response) {
                    const url = response.config.url;
                    const fileName = url.substring(url.lastIndexOf('/') + 1);
                    response.data.pipe(fs.createWriteStream(fileName)).on('close', function () {
                        console.log(`${fileName}下載完成`);
                    });;
                });
        })
})

同時杈湾,我們的pixiv-login是支持async await的!

const pixivStart = async () => {
    try {
        const cookie = await pixivLogin({
            username: '你的用戶名',
            password: '你的密碼'
        });

        fs.writeFileSync('cookie.txt', cookie);
        const data = fs.readFileSync('cookie.txt', 'utf8');

        const response = await axios({
            method: 'get',
            url: 'https://www.pixiv.net/ranking.php?mode=weekly_r18',
            headers: {
                'User-Agent': USER_AGENT,
                'Referer': 'https://www.pixiv.net',
                'Cookie': cookie
            },
        });

        const $ = cheerio.load(response.data);
        const src = $('#1 img').data('src');

        const pic = await axios({
            method: 'get',
            url: src,
            responseType: 'stream'
        });

        const fileName = pic.config.url.substring(pic.config.url.lastIndexOf('/') + 1);
        pic.data.pipe(fs.createWriteStream(fileName)).on('close', function () {
            console.log(`${fileName}下載完成`);
        });;

    } catch (err) {
        console.log(err)
    }
};

pixivStart();

參考

  1. 模擬登錄pixiv.net
  2. Python爬蟲入門:爬取pixiv
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末攘须,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子殴泰,更是在濱河造成了極大的恐慌于宙,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件悍汛,死亡現(xiàn)場離奇詭異捞魁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)离咐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門谱俭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來奉件,“玉大人,你說我怎么就攤上這事昆著∠孛玻” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵凑懂,是天一觀的道長煤痕。 經(jīng)常有香客問我,道長接谨,這世上最難降的妖魔是什么摆碉? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮脓豪,結(jié)果婚禮上巷帝,老公的妹妹穿的比我還像新娘。我一直安慰自己扫夜,他們只是感情好楞泼,可當(dāng)我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著历谍,像睡著了一般现拒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上望侈,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天印蔬,我揣著相機(jī)與錄音,去河邊找鬼脱衙。 笑死侥猬,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的捐韩。 我是一名探鬼主播退唠,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼荤胁!你這毒婦竟也來了瞧预?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤仅政,失蹤者是張志新(化名)和其女友劉穎垢油,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體圆丹,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡滩愁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了辫封。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片硝枉。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡廉丽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出妻味,到底是詐尸還是另有隱情正压,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布弧可,位于F島的核電站蔑匣,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏棕诵。R本人自食惡果不足惜裁良,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望校套。 院中可真熱鬧价脾,春花似錦、人聲如沸笛匙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽妹孙。三九已至秋柄,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蠢正,已是汗流浹背骇笔。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留嚣崭,地道東北人笨触。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像雹舀,于是被迫代替她去往敵國和親芦劣。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,724評論 2 354

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

  • 22年12月更新:個人網(wǎng)站關(guān)停说榆,如果仍舊對舊教程有興趣參考 Github 的markdown內(nèi)容[https://...
    tangyefei閱讀 35,181評論 22 257
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,108評論 25 707
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理虚吟,服務(wù)發(fā)現(xiàn),斷路器签财,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • 這本來是我為了過稿寫的一篇文章稍味,但是沒有后續(xù),我就把它放在了簡書上荠卷。希望自己重拾寫作的熱情,慢慢來吧烛愧。 十年前油宜,當(dāng)...
    帥氣的炮閱讀 370評論 0 2
  • 一掂碱、料器造假南紅鑒別 料器是以一種熔點較低的玻璃為原料制作,這種造假料器造假南紅瑪瑙雖然在外觀上與天然南紅瑪瑙很像...
    小雨點41閱讀 1,770評論 0 0