前言
“白日依山盡,___”。下句自然填黃河入海流,那么“日月忽其不淹兮,___,___,恐美人之遲暮”,中間兩句怎么填呢?
最近工作中有個需求,就是1500道語文詩詞填空題沒有答案,現(xiàn)在需要給這些題目大爺們匹配它們對應(yīng)的答案,好在題目信息都很完整,指出了詩詞出處、作者信息。自然想到到網(wǎng)上爬取對應(yīng)文章信息然后字符串匹配答案。目前做完,效果還可以,基本上所有題目的答案都有了,現(xiàn)把操作流程記錄下,做個總結(jié)。
1. 文章信息獲取
網(wǎng)上找了很久,目前發(fā)現(xiàn)百度漢語對古詩詞收錄的比較好,格式也比較規(guī)范,整個爬取過程還是比較簡單,瀏覽器分析網(wǎng)站,找到它們的搜索接口:http://hanyu.baidu.com/hanyu/ajax/sugs只需要傳一個參數(shù):mainkey,是一個urlencode格式的字符串。接口會返回一個匹配列表,再用作者名稱對這個列表做篩選,詳細(xì)代碼如下:
baseUrl := "http://hanyu.baidu.com/hanyu/ajax/sugs?"
client := http.Client{
}
u, _ := url.Parse(baseUrl)
q := u.Query()
q.Set("mainkey", name)
u.RawQuery = q.Encode()
//添加Header
req, _ := http.NewRequest("GET", u.String(), nil)
req.Header.Add("User-Agent", `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36`)
req.Header.Add("DNT", "1")
req.Header.Add("Host", "hanyu.baidu.com")
req.Header.Add("Accept-Language", "zh-CN,zh;q=0.8")
req.Header.Add("Referer", "http://hanyu.baidu.com/shici/detail?pid=be520db056da43238035dc18bb1e1798tn=sug_click")
resp, errDo := client.Do(req)
拿到返回值以后,篩選出對應(yīng)的作者信息。
//如果有多個搜索結(jié)果,比對author是否正確
respJson.ForEach(func(key, value gjson.Result) bool {
//先看看有沒有display_name
displayName := value.Get("display_name.0").String()
sid := value.Get("sid.0").String()
if len(displayName) == 0 {
//不是這條記錄
return true
}
//看看type
typeStr := value.Get("type.0").String()
if typeStr == "poemline"{
//取Source
displayName = value.Get("source_poem.0").String()
sid = value.Get("source_poem_sid.0").String()
}
literatureAuthor := value.Get("literature_author.0").String()
//author是否一致
if literatureAuthor == author {
searchResult.Sid = sid
searchResult.DisplayName = displayName
searchResult.Author = literatureAuthor
return false
}
return true // keep iterating
})
SearchResult保存了搜索到的結(jié)果,根據(jù)sid拿到文章頁面,解析出其中的文章。
func GetContent(sid string) (content string, err error) {
baseUrl := "http://hanyu.baidu.com/shici/detail"
result := make([]string, 0, 0)
client := http.Client{
}
u, _ := url.Parse(baseUrl)
q := u.Query()
q.Set("pid", sid)
u.RawQuery = q.Encode()
req, _ := http.NewRequest("GET", u.String(), nil)
req.Header.Add("User-Agent", `Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36`)
req.Header.Add("DNT", "1")
req.Header.Add("Host", "hanyu.baidu.com")
req.Header.Add("Accept-Language", "zh-CN,zh;q=0.8")
req.Header.Add("Referer", "http://hanyu.baidu.com/shici/detail?pid=be520db056da43238035dc18bb1e1798tn=sug_click")
resp, errDo := client.Do(req)
if errDo != nil || resp.StatusCode != 200 {
err = errors.New("無法連接百度漢語 " + errDo.Error())
return
}
docm, errDoc := goquery.NewDocumentFromResponse(resp)
if errDoc != nil {
err = errors.New("解析Doc錯誤 " + errDoc.Error())
return
}
//詩詞信息都保存在 body_p 的div中,可以通過PuerkitoBio/goquery庫獲取
pSelect := docm.Find("#body_p")
pSelect.Each(func(pos int, selection *goquery.Selection) {
content := strings.TrimSpace(selection.Text())
result = append(result, content)
})
content = strings.Join(result, "")
return
}
目前會爬取百度漢語、古詩詞兩個網(wǎng)站的數(shù)據(jù),如果有更好的數(shù)據(jù)源,只需要實現(xiàn)Spider接口,在MapSpiderManifest()方法中注冊即可。
type Spider interface {
GetContent(SearchResult) (string, error)
FindContent(string, string) (SearchResult, error)
}
func MapSpiderManifest() map[string]Spider {
//初始化并且注冊所有的Spider
spiderMap := make(map[string]Spider)
//百度
baiduSpider := new(BaiduSpider)
spiderMap["baiduSpider"] = baiduSpider
//古詩文網(wǎng)
gushiwenSpider := new(GushiwenSpider)
spiderMap["gushiwenSpider"] = gushiwenSpider
return spiderMap
}
2.詩詞句子查找
古詩文默寫,以前上學(xué)的時候做的多了,把一句話摳出來,隨機(jī)選其中幾段話讓學(xué)生默寫。一般可以歸類為一下幾種模式:
開頭留空 :_,[_,...],何人不起故園情。
末尾留空:俱往矣,_,[_,...]。
中間留空:月出于東山之上,_,白露橫江,
不管是什么樣的模式,就每個填空處來看,只有它前面或者后面有提示句,我們才能知道這個空的答案是什么。也就是說,這樣的填空可以自主的找到答案,姑且稱之為自主空。而前后都沒有提示句的空,只能等待附近有自主空找到了答案,才能找到它本身的答案,用一個圖說明更加清晰:
圖中灰色的塊因為有提示句,所以可以通過步驟一種爬取下來的文章內(nèi)容找到對應(yīng)的答案,填入Blank中,具體的查找算法如下代碼所示:
//已知newFind的PreString,求BlankString和PostString
func makeWithPreContent(contentsSplit []string, newFind *Find) {
for l := range contentsSplit {
if isEqual(contentsSplit[l], newFind.PreString) l len(contentsSplit)-1 {
newFind.BlankString = contentsSplit[l+1]
if l len(contentsSplit)-2 {
newFind.PostString = contentsSplit[l+2]
}
newFind.BlankFinish = true
}
}
}
//已知newFind的PostString,求BlankString和PreString
func makeWithPostContent(contentsSplit []string, newFind *Find) {
for l := range contentsSplit {
if isEqual(contentsSplit[l], newFind.PostString) l > 0 {
newFind.BlankString = contentsSplit[l-1]
if l-1 > 0 {
newFind.PreString = contentsSplit[l-2]
}
newFind.BlankFinish = true
}
}
}
// 按標(biāo)點符號分隔內(nèi)容
func SplitByPunctuation(s string) ([]string, []string) {
regPunctuation, _ := regexp.Compile(`[,,。.??!!;;::]`)
//匹配標(biāo)點符號,保存下來。 然后分割字符串
toPun := regPunctuation.FindAllString(s, -1)
result := regPunctuation.Split(s, -1)
if len(result[len(result)-1]) == 0 {
result = result[:len(result)-1]
}
//去掉前后空格,去掉引號
for i := range result {
result[i] = strings.TrimSpace(result[i])
regQuoting := regexp.MustCompile("[“”‘'']")
result[i] = regQuoting.ReplaceAllString(result[i], "")
}
return result, toPun
}
所有的自主塊都找到答案后,可以把每個自主塊看作一個雙向鏈表的表頭,我們所要做的就是遍歷每個雙向鏈表,把其中每個節(jié)點的答案都通過查找算法找出來。當(dāng)下一個節(jié)點是NULL,或者下個節(jié)點是一個自主塊時,就停止遍歷,處理下一個雙向鏈表。這樣,無論給出的需要填空的內(nèi)容多么復(fù)雜,都可以順利的完整自動填寫工作。
3.效果
一些常用的文言文或詩詞:
1、 前赤壁賦
2、 離騷
項目地址:AncientPoetryFillBlank (本地下載)
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
您可能感興趣的文章:- go語言實現(xiàn)通過FTP庫自動上傳web日志