hualuo 发表于 2019-12-26 15:21:33

太好玩了,爬虫、部署API、加小程序,一条龙玩转知乎热榜

来源:萝卜大杂烩
ID:luobodazahui
一直想做一个从爬虫到数据处理,到API部署,再到小程序展示的一条龙项目,最近抽了些时间,实现了一个关于知乎热榜的,今天就来分享一下!

由于代码还没有完全整理好,今天只给出一个大致的思路和部分代码,最终的详细代码可以关注后续的文章!
数据爬取

首先我们看下需要爬取的知乎热榜
https://www.zhihu.com/billboard
这个热榜可以返回50条热榜数据,而这些数据都是通过页面的一个 JavaScript 返回的
http://p1.pstatp.com/large/pgc-image/a4fdcb37cdd944ac90cc2dd180eaa2af

于是我们就可以通过解析这段 JS 代码来获取对应数据
url='https://www.zhihu.com/billboard'headers={"User-Agent":"","Cookie":""}defget_hot_zhihu():res=requests.get(url,headers=headers)content=BeautifulSoup(res.text,"html.parser")hot_data=content.find('script',id='js-initialData').stringhot_json=json.loads(hot_data)hot_list=hot_json['initialState']['topstory']['hotList']returnhot_list然后我们再点击一个热榜,查看下具体的热榜页面,我们一直向下下拉页面,并打开浏览器的调试板,就可以看到如下的一个请求
http://p3.pstatp.com/large/pgc-image/250f8eb214cf4158ab8fc4c7b0758d7d

该接口返回了一个包含热榜回答信息的 json 文件,可以通过解析该文件来获取对应的回答
defget_answer_zhihu(id):url='https://www.zhihu.com/api/v4/questions/%s/answers?include='%idheaders={"User-Agent":"","Cookie":""}res=requests.get(url+Config.ZHIHU_QUERY,headers=headers)data_json=res.json()answer_info=[]foriindata_json['data']:if'paid_info'ini:continueanswer_info.append({'author':i['author']['name'],'voteup_count':i['voteup_count'],'comment_count':i['comment_count'],'content':i['content'],'reward_info':i['reward_info']['reward_member_count']})returnanswer_info数据存储

获取到数据之后,我们需要存储到数据库中,以便于后续使用。因为后面准备使用 Flask 来搭建 API 服务,所以这里存储数据的过程也基于 Flask 来做,用插件 flask_sqlalchemy。
定义数据结构


我们定义三张表,分别存储知乎热榜的详细列表信息,热榜的热度信息和热榜对应的回答信息
classZhihuDetails(db.Model):__tablename__='ZhihuDetails'id=db.Column(db.Integer,primary_key=True)hot_id=db.Column(db.String(32),unique=True,index=True)hot_name=db.Column(db.Text)hot_link=db.Column(db.String(64))hot_cardid=db.Column(db.String(32))classZhihuMetrics(db.Model):__tablename__='ZhihuMetrics'id=db.Column(db.Integer,primary_key=True)hot_metrics=db.Column(db.String(64))hot_cardid=db.Column(db.String(32),index=True)update_time=db.Column(db.DateTime)classZhihuContent(db.Model):__tablename__='ZhihuContent'id=db.Column(db.Integer,primary_key=True)answer_id=db.Column(db.Integer,index=True)author=db.Column(db.String(32),index=True)voteup_count=db.Column(db.Integer)comment_count=db.Column(db.Integer)reward_info=db.Column(db.Integer)content=db.Column(db.Text)定时任务


由于我们需要定时查询热榜列表和热榜的热度值,所以这里需要定时运行相关的任务,使用插件 flask_apscheduler 来做定时任务
我们的定时任务,涉及到了网络请求和数据入库的操作,把这部分定时任务代码单独拉出来,在 Flask 项目的根目录下创建一个文件 apschedulerjob.py,由于在运行该文件时,是没有 Flask app 变量的,所以我们需要手动调用 app_context() 方法来创建 app 上下文
defopera_db():withscheduler.app.app_context():...当然,这里的 scheduler 变量是在 create_app 中初始化过的
fromflask_apschedulerimportAPSchedulerscheduler=APScheduler()defcreate_app(config_name):app=Flask(__name__)app.config.from_object(config)config.init_app(app)db.init_app(app)scheduler.init_app(app)...接着,我们就可以根据前面的两个爬虫函数,来分别入库数据了
入库热榜热度数据
update_metrics=ZhihuMetrics(hot_metrics=i['target']['metricsArea']['text'],hot_cardid=i['cardId'],update_time=datetime.datetime.now())入库热榜列表数据
new_details=ZhihuDetails(hot_id=i['id'],hot_name=i['target']['titleArea']['text'],hot_link=i['target']['link']['url'],hot_cardid=i['cardId'])入库热榜回答数据
new_content=ZhihuContent(answer_id=answer_id,author=answer['author'],voteup_count=answer['voteup_count'],comment_count=answer['comment_count'],reward_info=answer['reward_info'],content=answer['content'])最后我们就可以在 Flask 的入口程序中启动定时任务了
importosfromappimportcreate_app,schedulerapp=create_app(os.getenv('FLASK_CONFIG')or'default')if__name__=='__main__':scheduler.start()app.run(debug=True)编写 API

热榜列表 API


我们首先来做热榜列表的接口,在数据库表 ZhihuMetrics 中拿到当天热榜的最新热度信息,然后再根据热榜热度信息来获取对应的列表信息,可以总结到如下的一个函数中
defzhihudata():current_time='%s-%s-%s00:00:00'%(datetime.now().year,datetime.now().month,datetime.now().day,)zhihumetrics_data=ZhihuMetrics.query.filter(ZhihuMetrics.update_time>current_time).group_by(ZhihuMetrics.hot_cardid).order_by(ZhihuMetrics.update_time).all()metrics_list=db_opera.db_to_list(zhihumetrics_data)details_list=[]fordinmetrics_list:zhihudetails_data=ZhihuDetails.query.filter_by(hot_cardid=d).first()details_list.append(,d,d])returndetails_list接着定义一个视图函数返回 json 数据
@api.route('/api/zhihu/hot/')defzhihu_api_data():zhihu_data=zhihudata()data_list=[]fordatainzhihu_data:data_dict={'title':data,'link':data,'metrics':data,'hot_id':data,'update_time':data}data_list.append(data_dict)returnjsonify({'code':0,'content':data_list}),200热榜详情 API


下面再来做热榜详情接口,该接口可以返回热榜热度走势信息,为前端画图提供数据。
defzhihudetail(hot_id):zhihumetrics_details=ZhihuMetrics.query.filter_by(hot_cardid=hot_id).order_by(ZhihuMetrics.update_time).all()Column={'categories':[],'series':[{'name':'热度走势','data':[]}]}foriinzhihumetrics_details:Column['categories'].append(datetime.strftime(i.update_time,"%Y-%m-%d%H:%M"))Column['series']['data'].append(int(i.hot_metrics.split()))[email protected]('/api/zhihu/detail//')defzhihu_api_detail(id):zhihu_detail=zhihudetail(id)returnjsonify({'code':0,'data':zhihu_detail}),200接入小程序

对于小程序端,我们这里使用了 uni-app 框架,这是一个可以一份代码多端运行的框架,还是比较不错的。
创建项目


首先通过 IDE HBuilder 创建一个 uni-app 模板
http://p1.pstatp.com/large/pgc-image/74edef9b4fab4463ba12edae5bbdd083

改造项目


我们简单改造下该模板,首先修改下 index.nvue 文件,把 tabList 修改如下
data(){return{tabList:[{id:"tab01",name:'知乎热榜',newsid:0},{id:"tab02",name:'微博热榜',newsid:23},我们暂时只保留两个 tab 页签,没错后面还要再做微博的热榜!
接下来打开 news-page.nvue 文件,修改网络请求地址
uni.request({url:'http://127.0.0.1:5000/api/zhihu/hot/',data:'',把 URL 地址指向我们自己的 API 服务地址
然后再添加我们自己的新闻参数
hot_id:news.hot_id,metrics:news.metrics,news_url:news.link再修改函数 goDetail 如下
goDetail(detail){if(this.navigateFlag){return;}this.navigateFlag=true;uni.navigateTo({url:'/pages/detail/detail-new?query='+encodeURIComponent(JSON.stringify(detail))});setTimeout(()=>{this.navigateFlag=false;},200)},点击每条热榜时,就会跳转到 url 对应的 /pages/detail/detail-new 页面
引入 uCharts


下面编写 detail-new.nvue 文件,这里主要用到了 uni-app 的插件 uCharts。这是一个高性能的跨端图表插件,非常好用。
template 部分
柱状热力分布线性走势创建两个 view,分别用于展示柱状图和折线图
再编写 script 部分
getServerData(){uni.request({url:'http://127.0.0.1:5000/api/zhihu/detail/'+this.details.hot_id,data:{},success:function(res){_self.serverData=res.data.data;letColumn={categories:[],series:[]};Column.categories=res.data.data.categories;Column.series=res.data.data.series;_self.showColumn("canvasColumn",Column);_self.showLine("canvasLine",Column);},fail:()=>{_self.tips="网络错误,小程序端请检查合法域名";},});}再根据 uCharts 的官方文档编写对应的展示图表函数
showColumn(canvasId,chartData){canvaColumn=newuCharts({$this:_self,canvasId:canvasId,type:'column',legend:{show:true},fontSize:11,background:'#FFFFFF',pixelRatio:_self.pixelRatio,animation:true,categories:chartData.categories,series:chartData.series,enableScroll:true,xAxis:{disableGrid:true,scrollShow:true,itemCount:4,},yAxis:{//disabled:true},dataLabel:true,width:_self.cWidth*_self.pixelRatio,height:_self.cHeight*_self.pixelRatio,extra:{column:{type:'group',width:_self.cWidth*_self.pixelRatio*0.45/chartData.categories.length}}});}这样,我们就完成了基本的项目开发
我们可以到小程序的模拟器来查看效果啦
热榜列表页面
http://p1.pstatp.com/large/pgc-image/53046cabefd54e1d92845064097a2dd9

热榜详情页面

http://p3.pstatp.com/large/pgc-image/a6e1d423fb92442dbb25d8bbf5665996

基本的效果是有了,不过还有很多需要优化的地方,下一次,我会分享出优化后的代码以及如何把 API 服务部署到云端,同时还是提供出供大家练习的 API,不要错过哦!
【年薪30w工程师吐血整理资料大合集】
领取IT资料大合集:http://image.qbangmang.com/counselor.html
http://p1.pstatp.com/large/pgc-image/07cb6c600d874100af092e74c1a62b78
页: [1]
查看完整版本: 太好玩了,爬虫、部署API、加小程序,一条龙玩转知乎热榜