1. 程式人生 > >以QQ音樂為例子 解析公共介面的json資料

以QQ音樂為例子 解析公共介面的json資料

根據獲得歌手的json資料的url

json資料部分截圖

根據上面連結裡獲得資料,取出歌手mid,然後再根據下面的url獲得歌曲列表

json資料部分截圖

下面是呼叫的詳細程式碼,其中的HTTPUtil.sendGet方法是傳送http請求的程式碼。在我這個部落格

在測試這個方法,dao方法可以註釋掉, HotSongSpider是引入別的類中轉換list的方法,可以註釋掉。

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.jsoup.Jsoup;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * <p>description: </p>
 *
 * @author chenrui
 * @since 2018-08-09
 */
@Component
public class SpiderSingerNew {

    private static Logger log = LogManager.getLogger(SpiderSingerNew.class);

    @Resource
    private HotSongSpider hotSongSpider;
    @Resource
    private HotSongDao hotSongDao;

    private Integer singerListTotalPage = 1;  //所有頁數

    private Integer singerSongListTotalPage = 1;

    //成功的歌手列表
    List<QQ2Singer> singerList = new ArrayList<>();
    //爬取歌手列表出現的錯誤標籤和頁面
    public static Queue<Map<String, Integer>> errorSingerList = new LinkedList<>();
    public static Queue<Map<String, String>> errorSingerSongList = new LinkedList<>();

    //爬取所有歌手的url
    String singerListUrl = "https://u.y.qq.com/cgi-bin/musicu.fcg?format=jsonp&inCharset=utf8&outCharset=utf-8&data=";
    String param = "{\"comm\":{\"ct\":24,\"cv\":10000},\"singerList\":{\"module\":\"Music.SingerListServer\",\"method\":\"get_singer_list\",\"param\":{\"area\":-100,\"sex\":-100,\"genre\":-100,\"index\":{index},\"sin\":{sin},\"cur_page\":{cur_page}}}}";
    //歌手下的全部歌曲
    String singerSongListUrl = "https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg?inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq&needNewCode=0&singermid={singermid}&order=listen&begin={begin}&num={num}";

    public void spiderSingerList() {
        log.info("--start spider singer list--");
        int index = 1;
        //singer的list
        List<QQ2Singer> qq2SingerList = new ArrayList<>();
        try {
            for(; index<=27; index++) {
                for(int i=0; i < singerListTotalPage; i++) {
                    String sendParam = param.replace("{index}", String.valueOf(index)).replace("{sin}", String.valueOf(i*80)).replace("{cur_page}", String.valueOf(i+1));
                    String sendUrl = singerListUrl + URLEncoder.encode(sendParam, String.valueOf(StandardCharsets.UTF_8));
                    String content = HTTPUtil.sendGet(sendUrl).getContent();
                    parseSingerList(content, index, i);
                }
                //TODO 計算總頁數
                singerListTotalPage=1;
            }
        } catch (Exception e) {
            log.error("error : start spider singer list {} {}", e.getMessage(), e.getCause());
            e.printStackTrace();
        }

        //迴圈處理出錯的歌手,最多四次
        if(errorSingerList.size() > 0) {
            int singerEndlessFlag = 1;
            do {
                spiderErrorSingerList();
                singerEndlessFlag++;
            } while(singerEndlessFlag < 4 && errorSingerList.size()>0);
        }
        log.info("--finish spider singer list--");


        log.info("--start spider singer song list--");
        for(int i=0; i<singerList.size();i++) {
            String singermid = singerList.get(i).getSinger_mid();
            System.out.println("--開始第"+ (i+1) +"/"+singerList.size()+" singermid: "+ singermid +" 歌手的歌曲爬取--");
            spiderSingerSongList(singermid);
        }

        log.info("--處理歌手歌曲下的錯誤--");
        if(errorSingerSongList.size() > 0) {
            //迴圈處理出錯的歌手,最多四次
            int songEndlessFlag = 1;
            do {
                spiderErrorSingerSongList();
                songEndlessFlag++;
            } while(errorSingerSongList.size()>0 && songEndlessFlag < 4  );
        }
        log.info("--finish spider singer song list--");
    }

    private int sum = 0;

    /**
     * singerlist是全域性變數
     * 爬取全網的歌手速度很快,不用受到限制,所以先將歌手爬完再去爬取歌曲。
     * @param content
     * @param index
     * @param page
     */
    public void parseSingerList(String content, int index, int page) {
        log.info("開始 index:{} page:{} 將singer的json陣列轉為singer的list集合", index, page+1);
        JSONObject json = JSONObject.parseObject(content);
        try {
            //TODO 計算總頁數
            if(singerListTotalPage<=1) {
                int total = json.getJSONObject("singerList").getJSONObject("data").getInteger("total");
                sum += total;
                System.out.println("singer list的index: "+index+" page: "+ page +" " + total + " sum " + sum);
                singerListTotalPage = (total + 80 - 1) / 80;
            }
            //將singer的json陣列轉為singer的list集合
            JSONArray singerListJsonArray = json.getJSONObject("singerList").getJSONObject("data").getJSONArray("singerlist");
            for(int i=0; i<singerListJsonArray.size(); i++) {
                QQ2Singer qq2Singer = singerListJsonArray.getJSONObject(i).toJavaObject(QQ2Singer.class);
                //將歌手資訊新增到全域性變數中
                singerList.add(qq2Singer);

                /*hotSongDao.saveQQSinger(qq2Singer);*/
                hotSongDao.update(qq2Singer);

            }
        } catch (Exception e) {
            log.error("轉換singerlist,url標籤位置{}, 所在頁面{}, 出現錯誤 {} {}", index, page,e.getMessage(), e.getCause());

            Map<String , Integer> errorInfo = new HashMap<>();
            errorInfo.put("index", index);
            errorInfo.put("page", page);
            errorSingerList.add(errorInfo);
        }
        log.info("完成 index:{} page:{} 將singer的json陣列轉為singer的list集合", index, page+1);
    }
    /**
     * 繼續根據歌手列表出現的錯誤標籤和頁面爬取歌手
     * errorSingerList定義的全域性變數
     */
    public void spiderErrorSingerList() {
        Map<String, Integer> errorMap = errorSingerList.poll();
        while(errorMap!= null) {
            int index = errorMap.get("index");
            int page = errorMap.get("page");
            try {
                String sendParam = param.replace("{index}", String.valueOf(index)).replace("{sin}", String.valueOf(page*80)).replace("{cur_page}", String.valueOf(page+1));
                String sendUrl = singerListUrl + URLEncoder.encode(sendParam, String.valueOf(StandardCharsets.UTF_8));
                String content = HTTPUtil.sendGet(sendUrl).getContent();
                //根據index和page爬取對應的歌手
                parseSingerList(content, index, page);

                errorMap = errorSingerList.poll();
            } catch (Exception e) {
                Map<String , Integer> errorInfo = new HashMap<>();
                errorInfo.put("index", index);
                errorInfo.put("page", page);
                errorSingerList.add(errorInfo);
            }
        }

    }




    /**
     * 爬取歌手下的全部歌曲
     * @param singerMid
     */
    public void spiderSingerSongList(String singerMid) {
        List<HotSong> hotSongs = new ArrayList<>();
        try {
            for(int i=0; i<singerSongListTotalPage; i++) {
                String sendUrl = singerSongListUrl.replace("{singermid}", singerMid).replace("{begin}", String.valueOf(30*i)).replace("{num}", String.valueOf(30));
                String content = HTTPUtil.sendGet(sendUrl).getContent();
                parseSingerSongList(content, hotSongs, singerMid, i);
            }
            //TODO
            singerSongListTotalPage=1;
        } catch (Exception e) {
            log.error("爬取 {} 歌手下面的歌曲出現錯誤", singerMid);
        }
        List<Song> songList = hotSongSpider.transToPO(hotSongs);
        hotSongDao.saveOrUpdate(songList);
    }

    /**
     * content
     * @param content
     * @param singerMid
     */
    public void parseSingerSongList(String content, List<HotSong> hotSongs, String singerMid, int page) {
        JSONObject json = JSONObject.parseObject(content);
        //TODO
        if(singerSongListTotalPage<=1) {
            int total = json.getJSONObject("data").getInteger("total");
            singerSongListTotalPage = (total + 30-1)/30;
        }
        JSONArray jsonSongArray = json.getJSONObject("data").getJSONArray("list");
        try {

            for(int i=0; i < jsonSongArray.size(); i++) {
                HotSong song = jsonSongArray.getJSONObject(i).getJSONObject("musicData").toJavaObject(HotSong.class);

                //獲得album_id爬取相應的圖片
                String mid = song.getAlbummid();
                String albumImg = null;
                try {
                    //根據url拼接url+album_mid組成完成url,比如https://y.qq.com/n/yqq/album/004PCOKh1RUAqZ.html
                    String albumImgUri = MusicConstants.ALBUM_IMG_URI + mid + ".html";
                    //降低爬取速度
                    String html = HTTPUtil.sendGet(albumImgUri).getContent();
                    albumImg = Jsoup.parse(html).getElementById("albumImg").attr("src");
                } catch (Exception e) {

                }
                song.setAlbumImg(albumImg);

                hotSongs.add(song);
            }

        } catch (Exception e) {
            Map<String , String> errorInfo = new HashMap<>();
            errorInfo.put("singerMid", singerMid);
            errorInfo.put("page", String.valueOf(page));
            errorSingerSongList.add(errorInfo);

            log.error("爬取 {} 歌手, page: {} 下面的歌曲出現錯誤", singerMid, page);
        }
    }

    /**
     * 繼續根據歌手列表出現的錯誤標籤和頁面爬取歌手
     * errorSingerList定義的全域性變數
     */
    public void spiderErrorSingerSongList() {

        Map<String, String> errorMap = errorSingerSongList.poll();
        while(errorMap!= null) {
            List<HotSong> hotSongs = new ArrayList<>();
            String singerMid = errorMap.get("singerMid");
            int page = Integer.parseInt(errorMap.get("page"));
            try {
                String sendUrl = singerSongListUrl.replace("{singermid}", singerMid).replace("{begin}", String.valueOf(30*page)).replace("{num}", String.valueOf(30));
                String content = HTTPUtil.sendGet(sendUrl).getContent();
                parseSingerSongList(content, hotSongs, singerMid, page);
                List<Song> songList = hotSongSpider.transToPO(hotSongs);
                //儲存歌曲
                hotSongDao.saveOrUpdate(songList);
                errorMap = errorSingerSongList.poll();
            } catch (Exception e) {
                Map<String , String> errorInfo = new HashMap<>();
                errorInfo.put("singerMid", singerMid);
                errorInfo.put("page", String.valueOf(page));
                errorSingerSongList.add(errorInfo);
            }

        }

    }


}

非常感謝有小夥伴能夠重複和測試我的程式碼,如果在重複中出現問題,或者有關鍵方法沒有引入。請聯絡我,或者留言。

特別注意:由於水平有限程式碼肯定存在不足的地方,希望能夠指正,那麼我將做到更好。