使用Go默写古诗词

“白日依山尽,___”。下句自然填黄河入海流,那么“日月忽其不淹兮,___,___,恐美人之迟暮”,中间两句怎么填呢?

最近工作中有个需求,就是1500道语文诗词填空题没有答案,现在需要给这些题目大爷们匹配它们对应的答案,好在题目信息都很完整,指出了诗词出处、作者信息。自然想到到网上爬取对应文章信息然后字符串匹配答案。目前做完,效果还可以,基本上所有题目的答案都有了,现把操作流程记录下,做个总结。

1. 文章信息获取

网上找了很久,目前发现百度汉语对古诗词收录的比较好,格式也比较规范,整个爬取过程还是比较简单,浏览器分析网站,找到它们的搜索接口:http://hanyu.baidu.com/hanyu/ajax/sugs只需要传一个参数:mainkey,是一个urlencode格式的字符串。接口会返回一个匹配列表,再用作者名称对这个列表做筛选,详细代码如下:

    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=be520db056da43238035dc18bb1e1798&tn=sug_click")

    resp, errDo := client.Do(req)

拿到返回值以后,筛选出对应的作者信息。

//如果有多个搜索结果,比对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保存了搜索到的结果,根据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=be520db056da43238035dc18bb1e1798&tn=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
}

目前会爬取百度汉语、古诗词两个网站的数据,如果有更好的数据源,只需要实现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

    //古诗文网
    gushiwenSpider := new(GushiwenSpider)
    spiderMap["gushiwenSpider"] = gushiwenSpider

    return spiderMap
}

2.诗词句子查找

古诗文默写,以前上学的时候做的多了,把一句话抠出来,随机选其中几段话让学生默写。一般可以归类为一下几种模式:

  1. 开头留空 :_,[_,…],何人不起故园情。
  2. 末尾留空:俱往矣,_,[_,…]。
  3. 中间留空:月出于东山之上,_,白露横江,

不管是什么样的模式,就每个填空处来看,只有它前面或者后面有提示句,我们才能知道这个空的答案是什么。也就是说,这样的填空可以自主的找到答案,姑且称之为自主空。而前后都没有提示句的空,只能等待附近有自主空找到了答案,才能找到它本身的答案,用一个图说明更加清晰:

image

图中灰色的块因为有提示句,所以可以通过步骤一种爬取下来的文章内容找到对应的答案,填入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
        }
    }
}

// 按标点符号分隔内容
func SplitByPunctuation(s string) ([]string, []string) {
    regPunctuation, _ := regexp.Compile(`[,,。.??!!;;::]`)
    //匹配标点符号,保存下来。 然后分割字符串
    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
}

所有的自主块都找到答案后,可以把每个自主块看作一个双向链表的表头,我们所要做的就是遍历每个双向链表,把其中每个节点的答案都通过查找算法找出来。当下一个节点是NULL,或者下个节点是一个自主块时,就停止遍历,处理下一个双向链表。这样,无论给出的需要填空的内容多么复杂,都可以顺利的完整自动填写工作。

3.效果

一些常用的文言文或诗词:

1、 前赤壁赋

github

2、 离骚

github


项目地址:
AncientPoetryFillBlank

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的 Java 系统代码,包含登录注册功能和选择五句古诗词默写或100以内的加减法测试。 ```java import java.util.Scanner; public class Main { static Scanner scanner = new Scanner(System.in); static String[] users = new String[10]; static String[] passwords = new String[10]; static String[] poems = {"白日依山尽", "黄河入海流", "欲穷千里目", "更上一层楼", "静夜思床前明月光"}; public static void main(String[] args) { boolean isRunning = true; while (isRunning) { System.out.println("请选择操作:1.登录 2.注册 3.退出"); int choice = scanner.nextInt(); switch (choice) { case 1: login(); break; case 2: register(); break; case 3: isRunning = false; break; default: System.out.println("输入有误,请重新选择"); break; } } } // 登录 public static void login() { System.out.println("请输入用户名:"); String user = scanner.next(); System.out.println("请输入密码:"); String password = scanner.next(); for (int i = 0; i < users.length; i++) { if (user.equals(users[i]) && password.equals(passwords[i])) { System.out.println("登录成功!"); choose(); return; } } System.out.println("用户名或密码错误!"); } // 注册 public static void register() { System.out.println("请输入用户名:"); String user = scanner.next(); System.out.println("请输入密码:"); String password = scanner.next(); for (int i = 0; i < users.length; i++) { if (users[i] == null) { users[i] = user; passwords[i] = password; System.out.println("注册成功!"); return; } } System.out.println("用户已满,注册失败!"); } // 选择操作 public static void choose() { System.out.println("请选择操作:1.古诗词默写 2.100以内加减法测试"); int choice = scanner.nextInt(); switch (choice) { case 1: poemTest(); break; case 2: mathTest(); break; default: System.out.println("输入有误,请重新选择"); break; } } // 古诗词默写 public static void poemTest() { int correctCount = 0; for (int i = 0; i < 5; i++) { System.out.println("请默写下面这句诗:"); System.out.println(poems[i]); String answer = scanner.next(); if (answer.equals(poems[i])) { System.out.println("回答正确!"); correctCount++; } else { System.out.println("回答错误!"); } } System.out.println("您总共默写对了 " + correctCount + " 句诗!"); } // 100以内加减法测试 public static void mathTest() { int correctCount = 0; for (int i = 0; i < 10; i++) { int a = (int) (Math.random() * 99) + 1; int b = (int) (Math.random() * 99) + 1; int operator = (int) (Math.random() * 2); int result; String operatorStr; if (operator == 0) { result = a + b; operatorStr = "+"; } else { result = a - b; operatorStr = "-"; } System.out.println("请计算下面这道题:" + a + operatorStr + b + " = ?"); int answer = scanner.nextInt(); if (answer == result) { System.out.println("回答正确!"); correctCount++; } else { System.out.println("回答错误!"); } } System.out.println("您总共答对了 " + correctCount + " 道题!"); } } ``` 注意:该代码仅供参考,可能有不完善的地方,需要根据实际需求进行修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值