(ElasticsSearch学习)歌词检索Demo的实现:一. 爬取歌词信息,写入ES

简介: 一个ElasticSearchDemo,讲解如何使用Jsoup爬取歌词数据写入阿里云Elasticsearch,并搭建Web框架实现歌词的全文检索。

1.说明

2. 歌词网站分析

此处采用了比较笨的一种方式,即逐个分析每个请求的url,这样可以方便代码编写,就不用模拟器了(如需使用模拟器可参考使用cdp4j模拟点击事件等,但简单试了下不是很好用,且效率低)

a) 歌手获取分析

在http://www.kuwo.cn/artist/index 查看分页按钮的click事件,从js中找到分页请求的url
image
从click事件的artist.js中找到相关url如下图所示
image
其中pn参数即为页码参数

var b = host + "/artist/indexAjax?category=" + index + "&prefix=" + $("#artistContent").attr("data-letter") + "&pn=" + pn;

如http://www.kuwo.cn/artist/indexAjax?category=0&prefix=&pn=5

返回结果如下图所示,即可获取各个歌手的链接,取得每个歌手名字的链接url即为歌手详情页。
image
接下来代码中只要循环遍历即可。

b)歌词分页获取分析

歌手详情页如:http://www.kuwo.cn/artist/content?name=%E5%88%98%E7%8F%82%E7%9F%A3
image

同样查看分页按钮click事件的js相关代码,可以找到如下url,以获取分页歌词信息。
image

如:http://www.kuwo.cn//artist/contentMusicsAjax?artistId=2&pn=1&rn=100
其中artistId为歌手id,pn为分页参数
接下来循环遍历即可

c)歌词获取分析

每首歌歌词的详情页,从上面已经可以循环遍历到,详情页如下,
跟进html元素获取到相应歌词信息即可
image
注意:此处有时因为无版权,无法显示歌词,需要增加异常处理

3. 项目配置

maven配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.lewis</groupId>
    <artifactId>crawlerToES</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.10.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.10.0</version>
        </dependency>
        <dependency>
            <groupId>io.webfolder</groupId>
            <artifactId>cdp4j</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.9.2</version>
        </dependency>
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>x-pack-transport</artifactId>
            <version>5.3.3</version>
        </dependency>

        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <optional>true</optional>
            <version>5.3.3</version>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-assembly-plugin</artifactId>
                <configuration>
                    <descriptorRefs>
                        <descriptorRef>jar-with-dependencies</descriptorRef>
                    </descriptorRefs>
                </configuration>
            </plugin>
            <plugin>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

4. 爬虫解析代码

a)获取歌手

public static List<String> getSingers() {
        String url = host + "/artist/indexAjax?category=0&prefix=&pn=%d";
        List<String> singer_urls = new ArrayList<String>();
        int i = 0;
        int max_len = 10000;
        String last = null;
        while (true) {
            String cur_url = String.format(url, i);
            try {
                if(i>=max_len){
                    break;
                }
                Document doc = Jsoup.connect(cur_url).get();
                String cur = doc.text();
                if (cur.equals(last)) {
                    break;
                }
                Elements a_name = doc.getElementsByClass("a_name");

                for (int i1 = 0; i1 < a_name.size(); i1++) {
                    Element a = a_name.get(i1);
                    String singer_url = a.attr("href");
                    System.out.println(singer_url);
                    singer_urls.add(singer_url);
                }
                last = cur;
                i++;

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return singer_urls;
    }

b)获取某歌手的所有歌曲

    public static List<String> getSongs(String singer_url) {
        Document doc = null;
        List<String> song_urls = new ArrayList<String>();
        try {
            doc = Jsoup.connect(host + singer_url).get();
            String artistid = doc.getElementsByClass("artistTop").get(0).attr("data-artistid");
            String url = "/artist/contentMusicsAjax?artistId=%s&pn=%d&rn=100";
            int i = 0;
            String last = null;
            while (true) {
                String cur_url = String.format(url, artistid, i);
                doc = Jsoup.connect(host + cur_url).get();
                String cur = doc.text();
                if (cur.equals(last)) {
                    break;
                }
                Elements songs = doc.getElementsByClass("listMusic").get(0).children();
                for (int j = 0; j < songs.size(); j++) {
                    Element song = songs.get(j);
                    String name = song.getElementsByClass("name").get(0).text();
                    String href = song.getElementsByClass("name").get(0).getElementsByTag("a").get(0).attr("href");
//                    System.out.println(name+":"+href);
                    song_urls.add(href);
                }
                i++;
                last = cur;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            return song_urls;
        }
    }

c)获取某歌曲的歌词

public static SongBean getSong(String song_url) {
        try {
            Document doc = Jsoup.connect(host+song_url).get();
            Element div_song = doc.getElementById("musiclrc");
//            System.out.println(host+song_url);
            if(div_song==null){
                logger.info(song_url + "无法获取歌曲内容,可能是无版权!");
                return null;
            }
            String song_name = div_song.getElementById("lrcName").text();
            String singer = div_song.getElementsByClass("artist").get(0).getElementsByTag("span").get(0).getElementsByTag("a").get(0).text();
            String album = div_song.getElementsByClass("album").get(0).getElementsByTag("span").get(0).getElementsByTag("a").get(0).text();
            StringBuffer lyric = new StringBuffer();
            if(div_song.getElementById("llrcId")!=null) {
                Elements lyric_ps = div_song.getElementById("llrcId").children();
                for (int i = 0; i < lyric_ps.size(); i++) {
                    lyric.append(lyric_ps.get(i).text() + "\n");
                }
            }else{
                lyric.append("暂无歌词");
            }
            SongBean song = new SongBean(song_name,host+song_url,lyric.toString(),singer,album);
//            System.out.println(String.format("%s:%s",song.getName(),song.getSinger()));
            return song;

        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

5. 写入es

a)创建ElasticSearch Client

public static TransportClient openClient(String host, String clusterName, String securityUser){
        Settings settings = null;
        if (StringUtils.isNotBlank(securityUser)) {
            settings = Settings.builder()
                    .put("cluster.name", clusterName)
                    .put("xpack.security.user",securityUser)
                    .build();
        } else {
            settings = Settings.builder()
                    .put("cluster.name", clusterName).build();
        }

        TransportClient transportClient = new PreBuiltXPackTransportClient(settings);
        String ip = host.split(":")[0].trim();
        int port = Integer.parseInt(host.split(":")[1].trim());
        InetAddress add = null;
        try {
            add = InetAddress.getByName(ip);
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        transportClient.addTransportAddress(new InetSocketTransportAddress(add, port));
        return transportClient;
    }

b)批量写入elasticsearch

public static void writeToES(List<SongBean> list) throws Exception {
        BulkRequestBuilder blukBuilder = client.prepareBulk();
        for (SongBean song : list) {
            IndexRequestBuilder indexBuilder =  client.prepareIndex(es_indexName,es_indexType);
            HashMap<String,String> map  = new HashMap<>();
            map.put("name",song.getName());
            map.put("singer",song.getSinger());
            map.put("album",song.getAlbum());
            map.put("href",song.getHref());
            map.put("lyric",song.getLyric());
            indexBuilder.setSource(map);
            indexBuilder.setTimeout("5d");
            blukBuilder.add(indexBuilder);
        }
        BulkResponse bulkResponse = blukBuilder.execute().actionGet();
        logger.info(String.format("write to elasticsearch songs count = %d",list.size()));
        if (bulkResponse.hasFailures()) {
            throw new Exception(bulkResponse.buildFailureMessage());
        }
    }

6. 结果验证

爬取完歌曲数据后,登录阿里云ElasticSearch控制台,在Kibana控制台中,使用Dev Tools查询爬取到的歌曲数据,如下:

POST songs/sample/_search
{
  "query": {
    "match_all": {}
  }
}

image

相关实践学习
以电商场景为例搭建AI语义搜索应用
本实验旨在通过阿里云Elasticsearch结合阿里云搜索开发工作台AI模型服务,构建一个高效、精准的语义搜索系统,模拟电商场景,深入理解AI搜索技术原理并掌握其实现过程。
ElasticSearch 最新快速入门教程
本课程由千锋教育提供。全文搜索的需求非常大。而开源的解决办法Elasricsearch(Elastic)就是一个非常好的工具。目前是全文搜索引擎的首选。本系列教程由浅入深讲解了在CentOS7系统下如何搭建ElasticSearch,如何使用Kibana实现各种方式的搜索并详细分析了搜索的原理,最后讲解了在Java应用中如何集成ElasticSearch并实现搜索。 &nbsp;
目录
相关文章
|
12月前
|
人工智能 运维 Apache
我们为什么没用 Apache Answer,而是选择了 KoalaQA
团队搭建问答论坛时,发现Apache Answer难以满足多团队私有圈子与精细权限需求。KoalaQA 由此诞生,以“板块+权限”为核心,支持售后、内测、公开社区等多场景隔离与协同,为AI赋能奠定基础,更适配复杂业务场景的售后与运营需求。
338 1
|
12月前
|
人工智能 自然语言处理 Apache
有哪些类似 Danswer 的开源问答系统?
本文介绍了五款开源问答系统:Apache Answer适合团队知识沉淀;QAnything可将杂乱文档变为可对话的知识库;Haystack适合作为企业级问答中台基础;KoalaQA聚焦售后与智能客服场景;Khoj则是个人AI知识助手。选择时需根据使用场景、技术投入与团队需求匹配,系统落地后更需持续运营才能真正发挥作用。
662 0
|
11月前
|
人工智能 自然语言处理 搜索推荐
当下数字人定制平台哪家好?实测后我推荐它
2025年数字人已成商业基础设施,市场规模突破480亿。本文亲测来灵数字人、腾讯智影、百度曦灵、小冰、Synthesia五大平台,从客服、直播、内容生产三大场景出发,揭示选型关键:不看功能多全,而看是否适配业务。真正赢家,是选对场景、匹配流程、坚持12个月以上投入的企业。
|
缓存 API 数据安全/隐私保护
京东商品详情API接口全攻略:从数据获取到业务落地
在电商数据驱动决策的时代,京东商品详情API为开发者、商家及数据分析机构提供了获取精准商品数据的重要通道。本文详解接口调用流程、代码示例及实战应用,涵盖前期准备、签名生成、多语言调用及关键参数解析,助力构建比价系统、库存管理与竞品分析等应用。
|
传感器 人工智能 物联网
智能鞋:从脚下开始的科技革命
智能鞋:从脚下开始的科技革命
1555 6
|
人工智能 运维 Serverless
云端问道8期方案教学-基于Serverless计算快速构建AI应用开发
本文介绍了基于Serverless计算快速构建AI应用开发的技术和实践。内容涵盖四个方面:1) Serverless技术价值,包括其发展趋势和优势;2) Serverless函数计算与AI的结合,探讨AIGC应用场景及企业面临的挑战;3) Serverless函数计算AIGC应用方案,提供一键部署、模型托管等功能;4) 业务初期如何低门槛使用,介绍新用户免费额度和优惠活动。通过这些内容,帮助企业和开发者更高效地利用Serverless架构进行AI应用开发。
551 1
|
分布式计算 运维 API
针对MaxCompute经典网络域名下线,Dataphin应对策略的公告
针对MaxCompute经典网络域名下线,Dataphin应对策略的公告
642 7
|
JSON 人工智能 自然语言处理
Way To Prompt系列(1): 为什么大模型连"Strawberry"的"r"都数不对?一招“理由先行”显著提升模型思考能力
本文将从两个常见的大模型翻车问题入手解析这些问题背后体现的大模型技术原理(Tokenization与预测下一个Token),并解释了为什么会导致这些问题,接着我们利用CoT(思维链)方法解决这些问题并基于上述原理试图剖析CoT方法起作用的可能原因,最后提出【理由先行】风格这一简单有效的Prompt Trick。
694 2
|
存储 安全 Android开发
F-Droid:尊重自由与隐私的安卓应用商店
F-Droid 是安卓平台上的自由开源应用商店,专为关注隐私和数据安全的用户设计。本文详细介绍了 F-Droid 的特点,包括其对自由和隐私的重视、无广告和无追踪代码的承诺、强大的应用搜索与管理功能,以及对开源社区的支持。用户可以通过 F-Droid 安全地浏览、安装和管理应用程序,并且开发者也可以发布开源应用。未来,F-Droid 将继续提升用户体验,鼓励更多的开发者与用户参与其中,推动自由开源软件的发展。
5223 1
给出一个数据序列,建立二叉排序树,并实现插入功能 对二叉排序树进行中序遍历,可以得到有序的数据序列
该文章通过C++代码示例讲解了如何根据输入数据序列构建二叉排序树,并实现插入功能,随后通过中序遍历输出有序的数据序列,展示了对二叉排序树进行操作和遍历的完整过程。

热门文章

最新文章