实现一个成语接龙

实现一个成语接龙

成语接龙是中华民族传统的文字游戏。它不仅有着悠久的历史和广泛的社会基础,同时还是体现我国文字、文化、文明的一个缩影,是老少皆宜的民间文化娱乐活动。
规则:规则比较多样,这里先只讨论基础玩法,上词的结尾字接下一词的开头字。

实现一利用现有API

聚合函数:https://www.juhe.cn/
(倒也不是打广告 主要是这个免费API一天一百次 新手用也够了)

API -> 免费接口大全 -> 成语接龙 或 直接搜索成语接龙
点击立即申请 跳转后 或手动在 数据中心 -> 我的API中找到成语接龙
记下key

秉承no code say a jb的原则,这里上个demo抛砖引玉

public class Test {
    //正则 是否是四个汉字
    private static final String reg = "[\\u4e00-\\u9fa5]{4}";
    private static final String url = "http://apis.juhe.cn/idiomJie/query";
    private static final String key = "改成你自己的key,别想嫖我的。";

    private static final RestTemplate restTemplate = new RestTemplate();

    public static void main(String[] args) {
        String lastWord = "居安思危";
        String nowWord = "围魏救赵";
        System.out.println(isIdiom(lastWord, nowWord));
    }

    public static boolean isIdiom(String s1, String s2) {
        if (s1 == null || s2 == null) return false;//传入空串
        if (!Pattern.matches(reg, s1) || !Pattern.matches(reg, s2)) return false;//不是四字汉语

        List<String> idioms = getNextWorldArr(s1);
        System.out.println(idioms);
        return idioms.contains(s2);
    }

    public static String getUrl(String wd) {
        return url + "?key=" + key + "&wd=" + wd.substring(3);
    }

    public static List<String> getNextWorldArr(String wd) {
//        JSONObject result = restTemplate.postForObject(url, getParams(wd), JSONObject.class);
        JSONObject result = restTemplate.getForObject(getUrl(wd), JSONObject.class);
        if (null == result) return new ArrayList<>();
        result = result.getJSONObject("result");
        if (null == result) return new ArrayList<>();
        JSONArray data = result.getJSONArray("data");
        if (data == null) return new ArrayList<>();
        return JSONArray.parseArray(data.toJSONString(), String.class);
    }
//    本来是想用post的 api也说支持post 但是报key错误就算了还是get来的快
//    public static Map<String, Object> getParams(String wd) {
//        Map<String, Object> params = new HashMap<>();
//        params.put("key", key);
//        params.put("wd", wd.substring(3));
//        //params.put("size", 99);//返回结果进行限制 默认返回所有
//        params.put("is_rand", 2);//	是否随机返回结果,1:是 2:否。默认2
//        return params;
//    }
}

----------------------------------------------------------
console print

String lastWord = "居安思危";
String nowWord = "围魏救赵";
[危于累卵, 危在旦夕, 危如朝露, 危如累卵, 危峰兀立, 危急存亡, 危机一发, 危机四伏, 危而不持, 
危若朝露, 危言危行, 危言悚听, 危言核论, 危言正色, 危言竦论, 危言耸听, 危言覈论, 危言逆耳, 
危言骇世, 危言高论, 危迫利诱, 危邦不入]
false

String lastWord = "居安思危";
String nowWord = "危在旦夕";
[危于累卵, 危在旦夕, 危如朝露, 危如累卵, 危峰兀立, 危急存亡, 危机一发, 危机四伏, 危而不持, 
危若朝露, 危言危行, 危言悚听, 危言核论, 危言正色, 危言竦论, 危言耸听, 危言覈论, 危言逆耳, 
危言骇世, 危言高论, 危迫利诱, 危邦不入]
true

相信如果看完demo,或者上手一试,就会发现问题:
这里虽然检查了第二个成语是否是成语,那开头怎么办,固定一个祖传开头?API也有100次的调用限制,难不成一天只玩一百个?
那当然不是,一个比较简单的办法就是,爬虫爬数据存起来用。

实现二 爬虫+数据库

百度随便找了一个成语接龙网站:http://www.leleketang.com/chengyu/list501-1.shtml

通过F12开发者工具找到他们对应的样式,使用Jsoup进行解析。
Jsoup依赖(版本也可以自己定):

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.11.3</version>
</dependency>

然后上demo

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;

public class IdiomReptileDemo1 {
    public static void main(String[] args) throws IOException {
        Document doc = Jsoup.connect("http://www.leleketang.com/chengyu/list501-1.shtml").get();
        Elements elements = doc.select(".idiom_list");
        for (int i = 0; i < elements.size(); i++) {
            Element element = elements.get(i);
            Elements word = element.select(".idiom_list_title");
            Elements pinyin = element.select(".idiom_list_pinyin");
            Elements shiyi = element.select(".idiom_list_explain");

            System.out.println(word.text().replace(" ", ""));// 成语
            System.out.println(pinyin.text().trim());// 拼音
            String[] split = pinyin.text().trim().split(" ");
            System.out.println(split[0]);
            System.out.println(split[split.length - 1]);

            String de = shiyi.text().trim();
            System.out.println(de.substring(5));
        }
    }

    private String replaceStr(String str) {
        return str.replace("ā", "a").replace("á", "a").replace("ǎ", "a").replace("à", "a").replace("ē", "e")
                .replace("é", "e").replace("ě", "e").replace("ě", "e").replace("ī", "i").replace("í", "i")
                .replace("ǐ", "i").replace("ì", "i").replace("ō", "o").replace("ó", "o").replace("ǒ", "o")
                .replace("ò", "o").replace("ū", "u").replace("ú", "u").replace("ǔ", "u").replace("ù", "u")
                .replace("ǖ", "v").replace("ǘ", "v").replace("ǚ", "v").replace("ǜ", "v").replace("ü", "v");
    }
}

-------------------------------------------------------------
console print

成语:昂昂不动
拼音:áng áng bù dòng
首字拼音:áng
尾字拼音:dòng
成语释义:昂昂:气概轩昂的样子。形容目中无人,十分傲慢的样子

这里解析了A字第一页的成语,并且做了数据的抽取,简单操作一下即可入库,下方提供了替换注音的字母为普通字母,有需要的话可以调用方法转换,这里不做演示。
细心一点多点动几页就会发现规律,如字母A是501、B是502、C是503…以此类推(但是你再细心点会发现 I 和 J 似乎反了),所以使用for循环拼接字符即可做到对26个字母进行爬取。
另外是501-1,后面连接的数字代表页数,且超过最大页数并不会报错为404,而是访问的最后一页,如果不想额外做重复词语判断,建议记录下26字母对应的内层循环。
应该剔除掉非四字的成语,对访问频率也应该做些控制,如果对数据爬取不是很急,慢慢来最好了。

这里暂时不贴详细代码,感兴趣的小伙伴可以自己动手尝试
我直接贴出sql文件:https://download.csdn.net/download/qq_42595758/22339012
也告诉我邮箱发过去也行

现在有了数据之后,成语接龙就简单了,上码

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xu.robot.entity.Idiom;
import com.xu.robot.mapper.IdiomMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import java.util.List;
import java.util.regex.Pattern;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = RobotApplication.class)
public class Test1 {

    //正则 是否是四个汉字
    private static final String reg = "[\\u4e00-\\u9fa5]{4}";

    @Resource
    private IdiomMapper idiomMapper;

    @Test
    public void contextLoads() {
        //获取数据量总数
        int total = idiomMapper.selectCount(Wrappers.lambdaQuery());

        String start = getStart(total);
        System.out.println(start);
        System.out.println(isIdiom(start));;
        System.out.println(getNextIdiom(start, false));;
    }

    //随机抽一个成语作为开始
    public String getStart(int size) {
        return idiomMapper.selectById((int) (Math.random() * size)).getWord();
    }

    //是否是已收录的成语
    public boolean isIdiom(String word) {
        if (word == null || !Pattern.matches(reg, word)) return false;
        Idiom idiom = idiomMapper.selectOne(Wrappers.<Idiom>lambdaQuery().eq(Idiom::getWord, word));
        return idiom != null;
    }

    //获取下一个成语 word上一个成语 order按顺序获取或者乱序
    public String getNextIdiom(String word, boolean order){
        if(isIdiom(word)){
            List<Idiom> idioms = idiomMapper.selectNextIdiom(word.substring(3));
            if(null == idioms || idioms.size() == 0) return null;
            if(order){
               return idioms.get(0).getWord();
           }
            return idioms.get((int) (Math.random() * idioms.size())).getWord();
        }
        return null;
    }
}

-------------------------------------------------------
console print

获取随机的开始成语: 什一之利
是否是成语:true
下一个成语:利害得失

查询的sql奉上

@Select("select * from idiom where word like '${s}%'")
List<Idiom> selectNextIdiom(String s);

其实知道基础玩法后 进阶玩法也都可以举一反三 这里就不多BB了
有什么其他好玩的点子也欢迎和我交流

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值