一、目的

qq音乐提供免费在线试听,但是下载需要付费,通过开发爬虫,绕过付费环节,直接下载我们需要的歌曲。

二、方法

爬取对象是web端qq音乐,爬取范围是全站的歌曲信息,爬取方式是在歌手列表下获取每一位歌手的全部歌曲。

整个爬虫项目按功能分为爬虫规则和数据入库。

爬虫规则:
       在歌手列表https://y.qq.com/portal/singer_list.html按姓氏字母类别对歌手进行分类,遍历每个分类下的每个歌手页面,然后获取每个歌手页面下的全部歌曲信息。设计遍历方案(由内循环到外循环):

  1. 遍历每个歌手的每个页面的歌曲信息。
  2. 遍历每个歌手的歌曲页数;
  3. 遍历每个字母分类下的每个页面的歌手信息;
  4. 遍历每个字母分类下的歌手总页数;
  5. 遍历26个字母分类的歌手列表;

理论设计上至少需要五次遍历,实际开发中遍历次数要多得多。整个开发过程采用模块化设计思想,划分模块如下:

  • 歌曲下载
  • 歌手信息和歌曲信息
  • 字母分类下的歌手列表
  • 全部歌手列表

(一)歌曲下载

下载歌曲前,首先要找到某个歌手某个歌曲的下载链接。

在网页中,点击播放某歌曲,打开谷歌的开发者模式,在Media选项卡可以找到该歌曲的播放文件,复制该URL在浏览器打开,发现歌曲可以播放:

分析这个歌曲信息的URL,这是一个GET请求,并附带各种请求参数,如下:

http://dl.stream.qqmusic.qq.com/C400002stZ4548h0kT.m4a?guid=9613835105&vkey=FFE06ED227150F12AC92890FF951088A7B37F68950DD08F8994A633D908621681BC3F74798A3F4F7E30E3B057ECF62EC1AF4A00DAE934E0D&uin=0&fromtag=66

那么,要实现歌曲的下载,首先要找到歌曲文件的URL请求参数。以vkey为例,复制这个请求参数到其他请求信息的Preview响应内容里查找,结果在JS选项卡下找到该请求参数:

从上图分析,purl的值是歌曲URL信息的组成部分,再前边只需加上完整的域名就可以得到完整的歌曲文件URL。对于域名的选择,qq音乐提供了五个域名,每个域名都可以获取文件,这是一种集群的管理方式。在req的sip下可以找到具体的五个域名:

我们继续这个URL请求的地址,这个URL地址很长,并且有复杂的请求参数,请求参数分为三大类型:

  • 整个参数可以直接去掉;
  • 参数值固定不变;
  • 参数值从其他请求信息获取。

复制整个URL到地址栏进行访问,逐一实验把各参数去掉,观察响应内容是否发生变化 , 对于尚不明确的参数guid和songmid,songmid从命名角度看,是歌曲的唯一标识符,每首歌曲的songmid是固定且唯一的。参数guid则来自cookies,这是一种常见的反爬虫机制。我们将歌曲下载定义为函数download,并设置参数guid、songmid和cookie_dict,分别代表请求参数guid、songmid和用户的cookie信息。具体代码如下:


#guid请求参数来自cookies,这是常见的反爬措施。采用selenium获取cookies
def getCookies():#某个歌手的歌曲信息,用于获取Cookiesurl='https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg?g_tk=5381&'\'jsonpCallback=MusicJsonCallbacksinger_track&loginUin=0&hostUin=0&format=jsonp'\'&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0&singermid=001'\'fNHEf1SFEFN&order=listen&begin=0&num=30&songstatus=1'#配置chrome启动选项chrome_options=Options()#添加启动参数#--headless是不显示浏览器并执行过程chrome_options.add_argument('__headless')driver=webdriver.Chrome(chrome_options=chrome_options)#访问两个url,qq音乐才能生成cookiesdriver.get('https://y.qq.com/')time.sleep(5)driver.get(url)time.sleep(5)#调用chromeDriver自带的方法获取cookiesone_cookie=driver.get_cookies()driver.quit()print(one_cookie)#Cookies格式化cookie_dict={}for i in one_cookie:cookie_dict[i['name']]=i['value']return cookie_dict

对于cookies信息的获取,需要使用selenium实现,并且进行两次操作,才能获得cookies信息:第一次先访问qq音乐首页,第二次访问歌手页面,在JS选项卡下的请求中能找到cookie信息:

生成cookies信息还需要将其转化成字典格式。

(二)歌手信息和歌曲信息

在上一节中,实现了单首歌曲的下载,调用download()函数,传入不同的参数songmid即可实现下载不同的歌曲。在本节,通过歌手页面获取不同歌曲的songmid值。以邓紫棋为例,打开歌手页面,并在开发者工具下查找歌曲信息,最后在JS选项卡下找到歌曲信息,如下:

分析图上请求的url,某些参数存在固定规律,比如singermid是每位歌手的唯一标识符;begin是页数,每一页有30个歌曲,第一页为0,第二页为30...其余参数固定不变。

本小节实现的代码主要针对图上的请求URL进行。首先获取歌手的总歌曲数量,然后根据总歌曲数量来计算页数,最后遍历每一页来获取每首歌曲的信息以及歌曲的songmid进行歌曲下载,代码如下:

#获取歌手的全部歌曲
def get_singer_songs(singermid,cookie_dict):#获取歌手姓名和歌曲总数url='https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg?&loginUin=0&hostUin=0'\'singermid=%s&order=listen&begin=0&num=30&songstatus=1' % (singermid)r=session.get(url)#获取歌手姓名song_singer=r.json()['data']['singer_name']#获取歌曲总数song_count=r.json()['data']['total']#根据歌曲总数计算总页数pagecount=math.ceil(int(song_count)/30)#循环页数,获取每一页歌曲信息for p in range(pagecount):url='https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg?&loginUin=0&hostUin=0'\'singermid=%s&order=listen&begin=%s&num=30&songstatus=1' % (singermid,p*30)r=session.get(url)music_data=r.json()['data']['list']#songname--歌名,albumname--专辑,interval--时长,songmid--歌曲id用于下载#将歌曲信息存放到字典song_dict,用于入库song_dict={}for i in music_data:song_dict['song_name']=i['musicData']['songname']song_dict['song_album']=i['musicData']['albumname']song_dict['song_interval']=i['musicData']['interval']song_dict['song_songmid']=i['musicData']['songmid']song_dict['song_singer']=song_singer#下载歌曲info=download(cookie_dict[pgv_pvid],song_dict['song_songmid'],cookie_dict)#入库处理,参数song_dictif info:insert_data(song_dict)#song_dict清空处理song_dict={}函数get_singer_songs()用于爬取某个歌手的全部歌曲:

参数singermid代表歌手的唯一标识符,只需传入不同歌手的singermid,就能爬取不同歌手的全部歌曲;
代码有两个相同的变量url:第一个动态设置歌手的singermid,获取每位歌手的歌曲总数和歌手姓名;第二个动态设置页数,获取当前歌手每一页的歌曲信息;
下载歌曲调用已实现的download()函数,入库处理是调用入库函数insert_data(),后续小节会介绍。

(三)分类歌手列表

通过以上小节内容,现在已经可以下载某一歌手的全部歌曲,只要在这功能基础上遍历输入不同歌手的singermid,就能获取所有不同歌手的全部歌曲信息。用开发者工具对歌手列表进行分析,发现每页有80个歌手,共297页,全站歌手共有23760位。

将循环次数按字母分类划分。在歌手列表页上使用字母A-Z对歌手进行分类筛选,利用这个分类功能可以将全部歌手分成两层循环。拆分成两层循环主要是为异步编程提供切入点,具体实现方式会在后续小节讲解。首先在网页上单击分类“A”,在开发者模式下JS选项卡下看到相应请求信息:

点击不同字母和页数,发现参数变化规律:

  • index表示字母,“A”=1,"B"=2;
  • sin根据页数计算歌手数量,第一页为0,第二页为80;
  • cur_page表示当前页,从1开始

根据上述分析,本章的功能代码如下:

#获取当前字母下全部歌曲

def get_genre_singer(index,page_list,cookie_dict):#参数guid来自cookies的pgv_pvidfor page in page_list:url='https://u.y.qq.com/cgi-bin/musicu.fcg?callback=getUCGI8089630466192212&'\'g_tk=5381&jsonpCallback=getUCGI8089630466192212&loginUin=0&hostUin=0&format=jsonp'\'&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0&data=%7B%22'\'comm%22%3A%7B%22ct%22%3A24%2C%22cv%22%3A10000%7D%2C%22singerList%22%3A%7B%22module'\'%22%3A%22Music.SingerListServer%22%2C%22method%22%3A%22get_singer_list%22%2C%22'\'param%22%3A%7B%22area%22%3A-100%2C%22sex%22%3A-100%2C%22genre%22%3A-100%2C%22'\'index%22%3A'+str(index)+'%2C%22sin%22%3A'+str((page-1)*80)+'%2C%22cur_page%22%3A'+str(page)+'%7D%7D%7D'r=session.get(url)#循环每一个歌手for k in r.json()['singerList']['data']['singerList']:singermid=k['singer_mid']#传入不同的singermid来获取不同歌手的全部歌曲get_singer_songs(singermid,cookie_dict)

函数get_genre_singer()是获取单个字母分类的歌手列表,函数参数说明如下:

  • index代表字母      对应index
  • page_list代表当前字母分类下的总页数        对应(page-1)*80
  • cookie_dict代表函数getCookies的返回值,即用户的Cookies信息      对应page

(四)全站歌手列表

现在得到函数get_genre_singer(),只需传入不同的参数index和page_list即可实现26个英文字母分类的歌手列表。在此基础上遍历26个英文字母即可实现,将这个遍历定义在函数get_all_singer(),具体代码如下:

#单进程单线程
#获取全部歌手
def get_all_singer():#获取字母A-Z已经#的所有歌手for index in range(1,28):#获取每个字母分类下总歌手页数url='https://u.y.qq.com/cgi-bin/musicu.fcg?callback=getUCGI06463872642677693&'\'g_tk=5381&jsonpCallback=getUCGI06463872642677693&loginUin=0&hostUin=0&format=jsonp'\'&inCharset=utf8&outCharset=utf-8¬ice=0&platform=yqq&needNewCode=0&data=%7B%22'\'comm%22%3A%7B%22ct%22%3A24%2C%22cv%22%3A10000%7D%2C%22singerList%22%3A%7B%22module'\'%22%3A%22Music.SingerListServer%22%2C%22method%22%3A%22get_singer_list%22%2C%22'\'param%22%3A%7B%22area%22%3A-100%2C%22sex%22%3A-100%2C%22genre%22%3A-100%2C%22'\'index%22%3A'+str(index)+'%2C%22sin%22%3A0%2C%22cur_page%22%3A1%7D%7D%7D'r=session.get(url,headers=headers)total=r.json()['singerList']['data']['total']pagecount=math.ceil(int(total)/80)page_list=[x for x in range(1,pagecount+1)]#调用函数,获取当前字母下所有歌手get_genre_singer(index,page_list,cookie_dict)if __name__ == '__main__':#执行单线程进程get_all_singer()

上述代码是整个项目的程序入口,函数运行顺序如下:

  • get_all_singer():循环26个字母,构建参数并调用get_genre_singer()
  • get_genre_singer(index,page_list,cookie_dict):遍历当前分类总页数,获取每页每位歌手的歌曲信息
  • get_singer_songs(singermid,cookie_dict):实现歌手的歌曲入库和下载
  • download(guid,songmid,cookie_dict):下载歌曲
  • getCookies():使用selenium获取用户的cookies
  • insert_data(song_dict):入库处理

通过函数层层调用实现整个网站的歌曲下载和信息入库。
          将代码进行整合:

爬取:

import requests, time
import math
from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from music_db import *
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
# 创建请求头和会话
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
session = requests.session()# 下载歌曲
def download(guid, songmid,cookie_dict):# 参数guid来自cookies的pgv_pvidurl = 'https://u.y.qq.com/cgi-bin/musicu.fcg?-=getplaysongvkey11136773093082608&g_tk=5381&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq.json&needNewCode=0&data={"req":{"module":"CDN.SrfCdnDispatchServer","method":"GetCdnDispatch","param":{"guid":"'+guid+'","calltype":0,"userip":""}},"req_0":{"module":"vkey.GetVkeyServer","method":"CgiGetVkey","param":{"guid":"'+guid+'","songmid":["'+songmid+'"],"songtype":[0],"uin":"0","loginflag":1,"platform":"20"}},"comm":{"uin":0,"format":"json","ct":24,"cv":0}}'r = session.get(url, headers=headers,cookies=cookie_dict)purl = r.json()['req_0']['data']['midurlinfo'][0]['purl']# 下载歌曲if purl:url = 'http://isure.stream.qqmusic.qq.com/%s' %(purl)r = requests.get(url, headers=headers)f = open('song/' + songmid + '.m4a', 'wb')f.write(r.content)f.close()return Trueelse:return False# 获取歌手的全部歌曲
def get_singer_songs(singermid, cookie_dict):# 获取歌手姓名和歌曲总数url = 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg?loginUin=0&hostUin=0&singermid=%s' \'&order=listen&begin=0&num=30&songstatus=1' % (singermid)r = session.get(url)# 获取歌手姓名song_singer = r.json()['data']['singer_name']# 获取歌曲总数songcount = r.json()['data']['total']# 根据歌曲总数计算总页数pagecount = math.ceil(int(songcount) / 30)# 循环页数,获取每一页歌曲信息for p in range(pagecount):url = 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg?loginUin=0&hostUin=0&singermid=%s' \'&order=listen&begin=%s&num=30&songstatus=1' % (singermid, p * 30)r = session.get(url)# 得到每页的歌曲信息music_data = r.json()['data']['list']# songname-歌名,ablum-专辑,interval-时长,songmid歌曲id,用于下载音频文件# 将歌曲信息存放字典song_dict,用于入库song_dict = {}for i in music_data:song_dict['song_name'] = i['musicData']['songname']song_dict['song_ablum'] = i['musicData']['albumname']song_dict['song_interval'] = i['musicData']['interval']song_dict['song_songmid'] = i['musicData']['songmid']song_dict['song_singer'] = song_singer# 下载歌曲info = download(cookie_dict['pgv_pvid'], song_dict['song_songmid'], cookie_dict)# 入库处理,参数song_dictif info:insert_data(song_dict)# song_dict清空处理song_dict = {}# 获取当前字母下全部歌手
# 修改了请求地址URL以及数据获取
def get_genre_singer(index, page_list, cookie_dict):for page in page_list:url = 'https://u.y.qq.com/cgi-bin/musicu.fcg?-=getUCGI771604139451213&g_tk=5381&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq.json&needNewCode=0&data={"comm":{"ct":24,"cv":0},"singerList":{"module":"Music.SingerListServer","method":"get_singer_list","param":{"area":-100,"sex":-100,"genre":-100,"index":'+str(index)+',"sin":'+str((page-1)*80)+',"cur_page":'+str(page)+'}}}'r = session.get(url)# 循环每一个歌手for k in r.json()['singerList']['data']['singerlist']:singermid = k['singer_mid']get_singer_songs(singermid, cookie_dict)# 单进程单线程
# 获取全部歌手
# 修改了请求地址URL以及数据获取
def get_all_singer():# 获取字母A-Z全部歌手cookie_dict = getCookies()for index in range(1, 28):# 获取每个字母分类下总歌手页数url = 'https://u.y.qq.com/cgi-bin/musicu.fcg?-=getUCGI771604139451213&g_tk=5381&loginUin=0&hostUin=0&format=json&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq.json&needNewCode=0&data={"comm":{"ct":24,"cv":0},"singerList":{"module":"Music.SingerListServer","method":"get_singer_list","param":{"area":-100,"sex":-100,"genre":-100,"index":'+str(index)+',"sin":0,"cur_page":1}}}'r = session.get(url, headers=headers)total = r.json()['singerList']['data']['total']pagecount = math.ceil(int(total) / 80)page_list = [x for x in range(1, pagecount+1)]# 获取当前字母下全部歌手get_genre_singer(index, page_list, cookie_dict)# 使用Selenium获取Cookies
# 因为歌曲下载的请求参数guid是来自Cookies,因此要使用Selenium获取Cookies,这是常见的反爬虫措施之一
def getCookies():# 某个歌手的歌曲信息,用于获取Cookies,因为不是全部请求地址都有Cookiesurl = 'https://c.y.qq.com/v8/fcg-bin/fcg_v8_singer_track_cp.fcg?g_tk=5381&jsonpCallback=MusicJsonCallbacksinger_track&loginUin=0&hostUin=0&format=jsonp&inCharset=utf8&outCharset=utf-8&notice=0&platform=yqq&needNewCode=0&singermid=001fNHEf1SFEFN&order=listen&begin=0&num=30&songstatus=1'chrome_options = Options()# 设置浏览器参数# --headless是不显示浏览器启动以及执行过程chrome_options.add_argument('--headless')driver = webdriver.Chrome(chrome_options=chrome_options)# 访问两个URL,QQ网站才能生成Cookiesdriver.get('https://y.qq.com/')time.sleep(1)driver.get(url)time.sleep(1)one_cookie = driver.get_cookies()driver.quit()# Cookies格式化cookie_dict = {}for i in one_cookie:cookie_dict[i['name']] = i['value']return cookie_dict# 源码更新后可以与书本的源码对比分析,更新后的爬虫代码只修改了部分代码
# 变动最大是歌曲下载的代码,同时注意函数之间调用的参数都比之前的源码有所变化。
if __name__=='__main__':# 执行单进程单线程#get_all_singer()

数据入库:

from sqlalchemy import *
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base# 连接数据库
engine=create_engine("mysql+pymysql://root:****@localhost:3306/music_db?charset=utf8",echo=False)
# 创建会话对象,用于数据表的操作
DBSession = sessionmaker(bind=engine)
SQLsession = DBSession()
Base = declarative_base()# 映射数据表
class song(Base):# 表名__tablename__ ='song'# 字段,属性song_id = Column(Integer, primary_key=True)song_name = Column(String(50))song_ablum = Column(String(50))song_interval = Column(String(50))song_songmid = Column(String(50))song_singer = Column(String(50))
# 创建数据表
Base.metadata.create_all(engine)
# 数据入库
def insert_data(song_dict):# 连接数据库engine = create_engine("mysql+pymysql://root:****@localhost:3306/music_db?charset=utf8", echo=False)# 创建会话对象,用于数据表的操作DBSession = sessionmaker(bind=engine)SQLsession = DBSession()data = song(song_name = song_dict['song_name'],song_ablum = song_dict['song_ablum'],song_interval = song_dict['song_interval'],song_songmid = song_dict['song_songmid'],song_singer = song_dict['song_singer'],)SQLsession.add(data)SQLsession.commit()if __name__ == '__main__':# 创建数据表Base.metadata.create_all(engine)

NO.44-----QQ音乐全站爬虫相关推荐

  1. NO.35——qq音乐全站分布式爬虫(一)

    一.目的 qq音乐提供免费在线试听,但是下载需要付费,通过开发爬虫,绕过付费环节,直接下载我们需要的歌曲. 二.方法 爬取对象是web端qq音乐,爬取范围是全站的歌曲信息,爬取方式是在歌手列表下获取每 ...

  2. python爬虫qq音乐教学视频_爬取QQ音乐(讲解爬虫思路)

    一.问题描述: 本次爬取的对象是QQmusic,为自己后面做django音乐网站的开发获取一些资源. 二.问题分析: 由于QQmusic和网易音乐的方式差不多,都是讲歌曲信息放入到播放界面播放,在其他 ...

  3. 爬取QQ音乐(讲解爬虫思路)

    一.问题描述: 本次爬取的对象是QQmusic,为自己后面做django音乐网站的开发获取一些资源. 二.问题分析: 由于QQmusic和网易音乐的方式差不多,都是讲歌曲信息放入到播放界面播放,在其他 ...

  4. QQ音乐JS逆向爬虫,我用python全都爬!

    爬虫高级必然JS逆向,QQ音乐爬虫就是一个很好的练手,读完学会了直接在你女朋友面前装一手,读完你将收获到,QQ音乐JS逆向,sign参数获取,songmid参数获取,vkey参数获取,selenium ...

  5. Python爬虫如何去抓取qq音乐的歌手数据?

    自从学会爬虫之后是不是有一种我什么都想爬一下的冲动?今天小千就来教大家如何去抓取qq音乐的歌手数据,项目实操多练习能更快提升自己哦. 今天的项目目标就是获取 QQ 音乐指定歌手单曲排行指定页数的歌曲的 ...

  6. python爬虫爬取音乐单曲_Python爬取qq音乐的过程实例

    一.前言 qq music上的音乐还是不少的,有些时候想要下载好听的音乐,但有每次在网页下载都是烦人的登录什么的.于是,来了个qqmusic的爬虫.至少我觉得for循环爬虫,最核心的应该就是找到待爬元 ...

  7. python爬取qq音乐评论_爬虫:QQ音乐评论

    QQ音乐评论 有些小伙伴反馈,对于爬虫的完整流程,还是不清楚,这边就按顺序,给大家梳理一下 反爬 首先查看一下,我们要爬取的网页,是否反爬(各种侵害人家服务器的事情,我们不能干) QQ音乐网址:htt ...

  8. 【Python爬虫实战】使用Selenium爬取QQ音乐歌曲及评论信息

    本文对使用到的技术仅做简单的介绍,若想了解更多,请前往相应的官网网站进行学习. 本文适合对爬虫相关知识接触不多的新手,主要是普及Selenium如何做爬虫,大佬请跳过. 1.Selenium简单介绍 ...

  9. 【python】实验2项目2:使用爬虫Selenium模拟浏览器获取爬取QQ音乐中你喜欢的某位歌手(陈奕迅)

    请使用爬虫Selenium模拟浏览器获取爬取QQ音乐中你喜欢的某位歌手(可以是任意歌手)最受欢迎的前5首歌曲的歌词.流派.歌曲发行时间.评论条数.评论时间.评论点赞次数.评论内容具体(每一首歌的评论& ...

最新文章

  1. 【数据库】SQLite和MySQL之间的对比和选择
  2. Vim命令行模式调用shell命令方式(笔记)
  3. 一个好用的Chrome倒数计时器扩展 - Calendar and Countdown
  4. zip直链生成网站_安装网站程序
  5. WebService传输DataSet的一点想法和实践-.NET教程,Web Service开发
  6. attention :为什么要用attention机制
  7. SQL Server 创建游标(cursor)
  8. 软考高级 真题 2011年上半年 信息系统项目管理师 综合知识
  9. 中国汽车改装行业运营模式分析及投资战略规划研究报告2021年版
  10. iOS 地址智能解析器(收货地址 或 寄件地址)
  11. 电脑任务栏突然变得很宽很大
  12. Win11分磁盘怎么分?Win11系统怎么分磁盘?
  13. 如何评测光源的好坏?
  14. leetcode59.螺旋矩阵II
  15. Java项目:springboot医院信息管理系统
  16. python遍历是什么意思_在Python中遍历列表的方法有哪些
  17. 苹果一体机安装windows7教程
  18. 20、spring源码系列总结
  19. 设计模式大作业动物运动会系统【多种设计模式+文档】
  20. War-shall 算法 【求传递闭包】 离散数学记录

热门文章

  1. 2023年人不在湖北可以考电工证(高低压电工)吗?
  2. 报告称我国总负债或达50万亿 最大缺口是养老金
  3. 使用dubbo最新版的几个问题
  4. 搜狗输入法 Linux 社区版正式发布
  5. 游戏是人工智能研究的理想场景
  6. DOTA2翻硬币网站规则java版本
  7. 团队协作五大障碍读书笔记
  8. Spring Aop 初级- 概念及作用
  9. 【软考系统架构设计师】2012年下系统架构师综合知识历年真题
  10. 服务器与客户端交互(简述)