Android使用jsoup解析音乐网站获取歌名与歌手名显示在ListView上
最近这段时间老师让我们学生自己去做个项目,我自己选择做一个音乐播放器,因为这个比较简单,上手也比较容易,老师教的知识基本都用得上。
我自己就无聊跑去弄个网络获取歌单,当然也只是歌单,并不能实现播放功能,不过也学到了一些知识。获取网络的歌曲名与歌手名,我是这样弄的,我参考了很多前辈的用法,大多数的前辈们基本都是使用jsoup这个包去解析一些音乐网站,然后再获取想要的信息,这里我对我学到的知识做一下总结:
首先我们需要去导一下jsoup的jar包,在Android Studio中导入的方法:
第一种方法:可以直接在app文件夹下的build.gradle中的
dependencies{
compile 'org.jsoup:jsoup:1.10.3' //版本看情况
}
第二种方法:可以直接去https://jsoup.org/download 这个网址下载jsoup的jar包,下载完后,
然后对着这个jar包点击右键 找到Add As Library,软件就会帮你导入这个jar包啦
第三种方法:这种最为方便,可以直接在Android Studio中下载
这些准备工作基本都做完了,下面我们就开始进入项目中:
首先我们先把他的布局给弄出来,不弄布局哪里会显示哦。。
布局文件名:activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.caijixieshou.NetMusic.MainActivity">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/net_song_listview"
/>
</LinearLayout>
弄好布局后,我们创建在java文件夹下创建一个包叫domain,在该包下创建一个Bean类NetSongsResults来封装歌曲的信息
public class NetSongsResults {private String MusicName;//歌名
private String Artist;//歌手名
private String Album;//专辑
//用系统自带的Getter和Setter封装
public String getMusicName() {
return MusicName;
}
public void setMusicName(String musicName) {
MusicName = musicName;
}
public String getArtist() {
return Artist;
}
public void setArtist(String artist) {
Artist = artist;
}
public String getAlbum() {
return Album;
}
public void setAlbum(String album) {
Album = album;
}
//这里用系统生成的toString方法为了方便打印信息
@Override
public String toString() {
return "NetSongsResults{" +
"MusicName='" + MusicName + '\'' +
", Artist='" + Artist + '\'' +
", Album='" + Album + '\'' +
'}';
}
}
弄好了bean文件后,我们还在java文件夹下创建一个工具util包,在该包下创建一个解析类JsoupUtils,该类用来解析音乐网站
public class JsoupUtils {
private static JsoupUtils instance;
//这里用单例的方式,为了方便使用
public static JsoupUtils getInstance(){
if (instance == null){
instance = new JsoupUtils();
}
return instance;
}
public void getMusic(final String url, final String title,
final String artist, final ArrayList<NetSongsResults> datalist,
final CallBack mCallBack){
//因为联网解析网页属于一种耗时操作,所以我们需要在这里另起一个线程
new Thread(new Runnable() {
@Override
public void run() {
try {
/**这里用Jsoup去调用这一些一系列的方法,这些基本都是固定写法
* 其中connect方法中的参数为一个String类型的url即网页的链接
* userAgent方法中的参数为手机浏览器的模拟器(不知道是不是,我感觉好像是)
* timeout即超时链接的时间
* 最后调用get方法,然后传给一个Document的对象doc来接收,该方法需要对异常做处理
*/
Document doc = Jsoup.connect(url).userAgent("Mozilla/5.0 (Windows NT 10.0; WOW64) " +
"AppleWebKit/537.36 (KHTML, like G" +
"ecko) Chrome/53.0.2785.104 " +
"Safari/537.36 Core/1.53.3226.400").timeout(10000).get();
/**然后用doc调用jsoup包下的select方法,该方法中有一个String类型的cssQuery参数,怎样获取
* 这个参数,后面会讲,这里先用一个String类型的参数顶上,该方法返回的类型为Elements,是
* 在jsoup包下的一个类
*/
Elements titleEls = doc.select(title);
Elements artistEls = doc.select(artist);
//这里我们遍历一下我们所获取的信息,然后通过bean类封装信息再把信息添加到集合中
for (int i = 0;i<titleEls.size();i++){
//实例化NetSongsResults
NetSongsResults netSongsResults = new NetSongsResults();
//getAllElements方法为获取该cssQuery下的所有标签(我网页学得不好,我也不知道是不是)
Elements title = titleEls.get(i).getAllElements();
//这里用netSongsResults调用set方法封装信息
netSongsResults.setMusicName(title.get(0).text());
Elements artist = artistEls.get(i).getAllElements();
netSongsResults.setArtist(artist.get(0).text());
//这里添加专辑名,随便起一个
netSongsResults.setAlbum("最新推荐");
//最后把netSongsResults添加到集合中
datalist.add(netSongsResults);
}
//判断如果数据源不为空的时候调用回调接口的Success方法,为空时调用Error方法
if (datalist != null){
mCallBack.Success(datalist);
} else {
mCallBack.Error(datalist);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
//这里我们定义一个回调接口,判断是否成功的获取到了datalist
public interface CallBack{
public void Success(ArrayList<NetSongsResults> datalist);
public void Error(ArrayList<NetSongsResults> datalist);
}
}
弄好了工具类之后,我们弄一个自定义适配器去为了让ListView显示数据,ListView中的每一项的布局为:
item_net_song_layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:id="@+id/item_net_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
android:textSize="17sp"
/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:orientation="horizontal"
android:layout_marginLeft="10dp"
android:layout_marginBottom="10dp"
>
<TextView
android:id="@+id/item_net_artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="未知艺术家"
android:textSize="15sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" - "
android:textSize="20sp"
/>
<TextView
android:id="@+id/item_net_ablum"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:text="未知专辑"
android:textSize="15sp" />
</LinearLayout>
</LinearLayout>
弄好了布局之后就开始写自定义适配器了
public class NetSongAdapter extends BaseAdapter {
private Context context; //上下文
private ArrayList<NetSongsResults> datalist; //数据源
private LayoutInflater inflater; //布局填充器
private NetSongsResults netSongsResults;
public NetSongAdapter(Context context, ArrayList<NetSongsResults> datalist) {
this.context = context;
this.datalist = datalist;
this.inflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return datalist.size();
}
@Override
public Object getItem(int i) {
return datalist.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int position, View convertView, ViewGroup viewGroup) {
ViewHolder holder = null;
if (convertView == null){
holder = new ViewHolder();
convertView = inflater.inflate(R.layout.item_net_song_layout,null);
holder.title = convertView.findViewById(R.id.item_net_title);
holder.artist = convertView.findViewById(R.id.item_net_artist);
holder.ablum = convertView.findViewById(R.id.item_net_ablum);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
//实例化NetSongsResults对象,去接收datalist.get(position);
netSongsResults = datalist.get(position);
String artist = netSongsResults.getArtist();
String ablum = netSongsResults.getAlbum();
holder.title.setText(netSongsResults.getMusicName());
if (!TextUtils.isEmpty(artist)){
holder.artist.setText(artist);
} else {
holder.artist.setText("未知艺术家");
}
if (!TextUtils.isEmpty(ablum)){
holder.ablum.setText(ablum);
}else {
holder.ablum.setText("未知专辑");
}
return convertView;
}
class ViewHolder{
TextView title;
TextView artist;
TextView ablum;
}
}
适配器也弄好了,我们就开始去写MainActivity了
public class MainActivity extends AppCompatActivity {
private ListView net_song_listview;
private static final String url = "http://music.baidu.com/search?key=%E8%96%9B%E4%B9%8B%E8%B0%A6";
private ArrayList<NetSongsResults> datalist;
private NetSongAdapter netSongAdapter;
private Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
net_song_listview = (ListView) findViewById(R.id.net_song_listview);
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case Contents.ConnectSuccess:
netSongAdapter = new NetSongAdapter(MainActivity.this,datalist);
net_song_listview.setAdapter(netSongAdapter);
Toast.makeText(MainActivity.this,"更新完成...",Toast.LENGTH_SHORT).show();
break;
case Contents.ConnectFail:
//msg.what = (int) msg.obj;
Toast.makeText(MainActivity.this,"更新失败...",Toast.LENGTH_SHORT).show();
break;
}
}
};
datalist = new ArrayList<NetSongsResults>();
JsoupUtils.getInstance().getMusic(url, "span.song-title", "span.author_list",
datalist, new JsoupUtils.CallBack() {
@Override
public void Success(ArrayList<NetSongsResults> datalist) {
if (datalist != null ){
handler.sendEmptyMessage(Contents.ConnectSuccess);
}else {
return;
}
}
@Override
public void Error(ArrayList<NetSongsResults> datalist) {
if (datalist == null){
handler.sendEmptyMessage(Contents.ConnectFail);
}
}
});
}
}
这样就把获取网络歌曲信息的功能写完了,最后需要在配置文件AndroidManifest.xml中添加网络权限
<uses-permission android:name="android.permission.INTERNET" />
实际图是这样的:
之前也有提到在Elements中的select方法的参数怎么去用,这里我也是参考前辈的用法粗略的说一下:
首先打开一个网址:https://try.jsoup.org/
打开网页后点击Fetch Url,会出现下面的窗口:
这时候就会出现这样:
这些都是网页的解析出来的数据,然后再做以下操作:
这样我们就讲完了,如果我讲的有什么地方不好的还请帮忙指出,因为我也是个初学者,学得也不太好,而且也是第一次写博客,希望大家多多支持!