以QQ音樂為例子 解析公共介面的json資料
阿新 • • 發佈:2019-02-01
根據獲得歌手的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¬ice=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); } } } }
非常感謝有小夥伴能夠重複和測試我的程式碼,如果在重複中出現問題,或者有關鍵方法沒有引入。請聯絡我,或者留言。
特別注意:由於水平有限程式碼肯定存在不足的地方,希望能夠指正,那麼我將做到更好。