0 前言
先说说看这篇博客你能知道什么:1 腾讯、网易、新浪不同新闻的地址格式以及评论内容的地址格式(返回数据为json的异步接口);2 一些比较通用的设计方法,对软件设计的菜鸟可能有帮助;
之前也说了要写这边博客,现在终于写出来了。我的毕业设计的指导老师说毕设论文的字数不够……所以我决定把这些本不应该出现在论文中的实现细节凑到论文中。至于下面说到的东西要解决什么问题,各位可以先看看这个网站(我毕设的初步结果,目前还在优化中,包括代码结构还有UI设计):http://reetseenews.duapp.com/
这个网站要实现以下功能:从腾讯新闻、网易新闻、新浪新闻中(当然以后可能会更多其它新闻网站)通过代码自动分析出哪些新闻内容是相似的,然后把相似的内容组织到一起,例如三个网站都有“北京成功申奥”这样的新闻,这个网站会自动把三条新闻归到一起,然后把三个新闻网站的热门评论都下载下来,按照时间排序,最后通过代码计算出一段“评论概括”,因为有的人很懒,看新闻基本就是看了标题然后看评论,看评论可能只看热门评论,看热门评论可能还看那种被顶得最多的,所以我就这么帮下这些懒人了……哈哈哈。
网站打开得有点慢,这个是因为我的代码写得不够好,在百度也看到很多牛逼的技术内容,回头看看这个毕设的各种设计,觉得有点丢脸,迟点重构····目前先凑合着用。咳咳,跑题了,这个网站的所有内容可以说都是通过爬虫抓取下来了,我的工作只不过是将爬虫获取的内容进行了组织然后重新展现。
这篇博客的内容仅仅是说一下爬虫是怎么写的,完全不会涉及分析新闻相似的算法是怎么样的以及对热门评论进行概括的算法是怎么样的。
那么下面开始。
1 新闻格式规范化
我在同学介绍下选择了Python的Scrapy爬虫框架进行爬虫编写。要在自己的网站上展示新闻内容,就应该将下载好的新闻按照统一的格式处理好,这样前端(网页端)才能够方便地处理、展示。一篇新闻有以下几个需要关注的地方:
- 新闻标题
- 新闻正文
- 新闻时间
由于要知道新闻的来源以及对应的评论,所以加上这两项:4. 新闻来源,5. 新闻评论地址。
那么最终在我这里,一篇处理后的新闻格式应该是这样的(这里使用类似json的格式表示,“[]”表示一个数组,“{}”表示一个字典):
{ ‘source‘ : ‘来源网站‘, ‘date‘ : ‘时间(长度8位,如20140501)‘, ‘contents‘ : { ‘link‘ : ‘新闻详细内容页面的地址‘, ‘title‘ : ‘新闻标题‘, ‘passage‘ : ‘新闻正文‘, }, ‘comments‘ : { ‘link‘ : ‘新闻对应的评论页面地址‘, }, }
如果有人问既然已经有了新闻的地址页面,为什么还要将新闻标题、新闻正文这些内容保存到本地呢?因为要在本地对新闻进行分类,就必须将这些内容下载下来。
上面的格式只是一个初步的统一格式,最终的格式相比这个可能会有小变化,这个后面再说。
2 评论格式规范化
要在我的网站对评论进行展示,就要有统一的格式,通常评论的要素包括三个:
- 用户名
- 评论时间
- 评论内容
由于我要标注评论的来源,所以再加一个:4. 评论来源,那么最终被我处理过的评论格式如下:
{ ‘source‘ : ‘来自哪个新闻网站‘, ‘user‘ : ‘用户名‘, ‘time‘ : ‘10位长度的时间戳‘, //转换成时间戳是为了方便以后排序 ‘content‘ : ‘评论内容‘ }
那么最终重新按照时间排序后的评论列表格式应该是像下面这样的:
[ { ‘source‘ : ‘‘, ‘user‘ : ‘‘, ‘time‘ : ‘‘, ‘content‘ : ‘‘ }, { ‘source‘ : ‘‘, ‘user‘ : ‘‘, ‘time‘ : ‘‘, ‘content‘ : ‘‘ }, { ‘source‘ : ‘‘, ‘user‘ : ‘‘, ‘time‘ : ‘‘, ‘content‘ : ‘‘ }, ... ]
这里先规范好最终的评论格式,下面针对评论部分的讲解设计就有头有据了。
另外就是评论并不需要爬虫下载!因为评论是实时更新的,不像新闻,基本上发布了就不怎么变化了。因为评论的内容是在用户点击某条新闻后才通过HTTP请求获取的。
3 URL规则分析
3.1 腾讯新闻
- targetid就是新闻的id;
- time就是时间戳,有了这个可以转换成公元时间;
- content就是回复的内容(这位网友真是热心回复这么多);
- up就是人家给的赞的数量;
- userinfo中的nick就是用户名了,后面竟然还有对应的腾讯微博的用户信息。
{ id: "5828439023791190273", targetid: 1004703995, parent: "0", replyuser: "", replyuserid: 0, timeDifference: "01月13日 18:15:50", time: "1389608150", content: "很高兴看到*军委日前印发《关于军队贯彻落实〈党政机关厉行节约反对浪费条例〉的措施》,规定今后所有公务用车采购国产自主品牌,其实不光是汽车,像电脑、打印机、扫描仪、复印机、服务器、网络设备、软件等产品更要带头采购国产自主品牌,防止军队内部机密资料遭受国外网络黑客攻击!希望*军委、中国海陆空军队能够切实担负起捍卫国土、捍卫钓鱼岛的职责!其实中国的部队,大部分军官素质是好的,但也有相当一部分军官也十分腐败,作风腐化堕落,为人霸道欺负新兵,道德品质极差;艰苦奋斗、勤俭建军的思想有所淡化;革命意志、进取精神有所退化;铺张浪费、大手大脚现象有所抬头。加强军队的反腐倡廉建设对于巩固党的执政地位、树立中国军队的新形象、提升国防战斗力十分重要。要保卫好祖国国土完整,加强军队作风建设和反腐倡廉建设责任重大。“物必自腐,而后虫之”,如果任由各种腐败作风在军队系统漫延,将使中国军队的整体战斗力、中国军官的整体政治素质大大下降,后果不堪设想!希望*军委一定要在全军大力开展创先争优活动,加强中国军人综合素质建设与作风建设,全面推广党风廉政建设巡视检查制度,大力清除军队内部的腐败分子,大力整顿军队内部的“吃喝玩乐嫖赌、卖官鬻爵收受贿赂违规提拔*”等各类不正之风,营造“勤于学习、善于学习、钻研技术、廉洁从业”的良好氛围,铸造一支英勇善战、战斗力凝聚力号召力超强的中国军队!同时中国军队应该坚持人性化细节化管理,对于长期夫妻两地分居的军官、军人要免费发放性慰器,防止他们流入社会进入嫖娼卖淫场所影响中国军队形象!希望中国能够集国家和中华民族的坚强意志,大力加强自主创新型国家建设,努力攻克在大型民用飞机、军用飞机、导弹系统、航空母舰等制约国家核心技术的关键高端产业领域,早日使我国的民用与军用飞机、导弹、航空母舰早日上市,彻底摆脱对欧洲、美国、日本在先进技术上的制约,成为一个装备制造强国!中国不能搞、不会搞军事扩张但也绝不能太懦弱,一定要夯实军事装备,提升军队战斗力,保护好国土、海域的安全,捍卫民族尊严!", up: "91", rep: "0", type: "2", hotscale: "3", checktype: "2", checkstatus: "1", isdeleted: "0", tagself: "", taghost: "", source: "1", userinfo: { userid: "21059616", uidex: "ec52a391823814524e8446efa3bd6a48d9", nick: "炎黄子孙中国梦", head: "http://q1.qlogo.cn/g?b=qq&k=ACz0AuPMwiaSfHTmIGWbIOw&s=40&t=0", gender: "1", viptype: "0", mediaid: 0, region: "中国:安徽:宣城", thirdlogin: 0, wbuserinfo: { name: "cjppjc17", nick: "炎黄子孙中国梦", url: "", vip: 0, ep: 0, brief: "", identification: "", intro: "", liveaddr: { country: "1", province: "34", city: "18", area: "" }, level: 0, classify: "" }, commentnum: "283", commentednum: "168", upnum: "2108" } },
还记得在“评论规范化”中定的格式吗?现在我们就从上面的json格式找到一个将其规范化的方法,假设热门评论连接中返回的json格式的变量名为js,i为遍历时的下标,那么用下面这个方法进行评论格式的规范化:
{ ‘source‘ : ‘tencent‘, ‘user‘ : js[‘data‘][‘commentid‘][i][‘userinfo‘][‘nick‘], ‘time‘ : js[‘data‘][‘commentid‘][i][‘time‘], //原格式就是10位时间戳了不用转换 ‘content‘ : js[‘data‘][‘commentid‘][i][‘content‘] }
腾讯新闻的算是搞掂了。
3.2 网易新闻
<script type="text/javascript"> if (typeof isForceShowComment != "undefined") { isShowComments = isForceShowComment; }; function tieInit() { tie_load({ urs: "http://img1.cache.netease.com/utf8/microblog/urs/urs1.0.3.js", tie: "http://img1.cache.netease.com/utf8/tie/tieanywhere2.6.6-min.js" }, function() { var replyCount = 10068, totalCount = 59215, threadId = "9IISVTCG0001124J", boardId = "news_guonei8_bbs", host = tieChannel, tId = tId; NTES(".js-tielink").attr("href", "http://comment." + host + ".163.com/" + boardId + "/" + threadId + ".html") NTES(".js-tiecount").attr("innerHTML", totalCount); NTES(".js-joinCount").attr("innerHTML", TieAnywhere.formatNumber(totalCount.toString(), 3)); NTES(".js-bjoinCount").attr("innerHTML", TieAnywhere.formatNumber(totalCount.toString(), 3)); NTES(".js-actCount").attr("innerHTML", TieAnywhere.formatNumber(replyCount.toString(), 3)); NTES(".js-bactCount").attr("innerHTML", TieAnywhere.formatNumber(replyCount.toString(), 3)); new TieAnywhere.Tie({ wraper: NTES("#tieArea"), threadId: threadId, boardId: boardId, host: host, isShowComment: isShowComments, isStrict: isStrict, tId: tId }); }) }; tie_ready(function() { if (!window.NTES) { tie_load({ lib: "http://img1.cache.netease.com/cnews/js/ntes_jslib_1.x.js" }, function() { tieInit(); }, "GBK") } else { tieInit(); }; }); </script> <!--tie end-->
新闻的所有评论页面地址:http://comment.news.163.com/boardId/新闻ID.html,例如http://comment.news.163.com/news_guonei8_bbs/9IISVTCG0001124J.html。关于boardId及新闻ID,请参见上面一项。
- 前面的数字1表示1楼;
- f是显示的内容,对应分别为“网易XX网友”,用户ID,用户IP,没有用户ID的就显示“网易XX网友”,连“网易XX网友”都没有的就显示IP地址,我猜的;
- v是支持数量;
- u不知道是什么粗略看一眼值全部都是‘u‘;
- d是当前评论的新闻id;
- t就是发表评论的时间啦;
- b就是评论的内容;
- a就是表示这个楼是不是最后一层楼的;
- p就是上面那个div标签的id;
- n怀疑是网易帐号或者用户名;
- l不知道是什么;
- ip就是ip了。
{ 1: { f: "网易日本手机网友 <span id=9KUDRCJ8 class=cGray>ip:126.255.*.*:</span>", v: "11014", u: "u", d: "9KUD5BUN00011229", t: "2014-02-13 03:34:08", b: "媽的,這傻逼幹嘛不索取精液流失費?", a: "0", p: "9KUDRCJ8", n: "chinese_cc@163.com", l: "0", ip: "126.255.*.*" } },
另外一个例子:
{ 1: { f: "网易山东省济南市手机网友 [<a href=‘‘>逗狗儿玩呵呵</a>]: ", d: "9KUD5BUN00011229", b: "这么一搞这女的以后还能嫁出去?够阴的。", a: "0", n: "逗狗儿玩呵呵", l: "0", tsn: "8058245774", ip: "113.128.*.*", timg: "http://mimg.126.net/p/butter/1008031648/img/face_big.gif", v: "9219", u: "u", t: "2014-02-13 04:04:49", p: "9KUFJMLA", tid: "4983565181079566873" } },
有没有觉得网易新闻的评论内容中key都很短?而且几乎没有冗余信息。
{ ‘source‘ : ‘netease‘, ‘user‘ : js[‘hotPosts‘][i][‘1‘][‘n‘], ‘time‘ : js[‘hotPosts‘][i][‘1‘][‘t‘], //原格式“%Y-%m-%d %H:%M:%S”,要转换成10位长度的时间戳 ‘content‘ : js[‘hotPosts‘][i][‘1‘][‘b‘] }
要注意的是,上面的‘n‘不是一定存在的,因为如果有N楼,那么除了最后一层楼外,从1~N-1楼都是没有‘n‘标签的。呼,网易新闻的也搞掂了。
3.3 新浪新闻
<meta name="comment" content="kj:2-1-9122947"> <meta name="sudameta" content="comment_channel:kj;comment_id:2-1-9122947">comment_id同时也是官方所谓的newsid,comment_id的组成是:channelId-1-新闻Id后8位,其中中间的1还不知道是什么意思,comment_channel就是评论通道。
{ ‘source‘ : ‘sina‘, ‘user‘ : js[‘hot_list‘][i][‘nick‘], ‘time‘ : js[‘hot_list‘][i][‘time‘], //原格式“%Y-%m-%d %H:%M:%S”,要转换为10位时间戳 ‘content‘ : js[‘hot_list‘][i][‘content‘] }
4 最终格式及补充说明
{ ‘source‘ : ‘tencent‘, ‘date‘ : ‘20140214‘, ‘newsId‘ : ‘015412‘, ‘cmtId‘ : ‘1004980094‘, ‘contents‘ : { ‘link‘ : ‘http://news.qq.com/a/20140214/015412.htm‘, ‘title‘ : ‘哈尔滨一夜清查酒店洗浴等2700余家‘, ‘passage‘: ‘正文内容‘ }, ‘comments‘ : { ‘link‘ : ‘http://coral.qq.com/1004980094‘, } }
{ ‘source‘ : ‘netease‘, ‘date‘ : ‘20140214‘, ‘newsId‘ : ‘9L2525NG0001124J‘, ‘cmtId‘ : ‘9L2525NG0001124J‘, ‘boardId‘ : ‘news_guonei8_bbs‘, ‘contents‘ : { ‘link‘ : ‘http://news.163.com/14/0214/14/9L2525NG0001124J.html‘, ‘title‘ : ‘哈尔滨4800余警力一夜清查酒店洗浴等2700余家‘, ‘passage‘ : ‘正文内容‘ }, ‘comments‘ : { ‘link‘ : ‘http://comment.news.163.com/news_guonei8_bbs/9L2525NG0001124J.html‘, }, }
{ ‘source‘ : ‘sina‘, ‘date‘ : ‘20140214‘, ‘newsId‘ : ‘1-1-29471498‘, ‘cmtId‘ : ‘1-1-29471498‘, ‘channelId‘ : ‘gn‘, ‘contents‘ : { ‘link‘ : ‘http://news.sina.com.cn/c/2014-02-14/142829471498.shtml‘, ‘title‘ : ‘哈尔滨4800余警力一夜清查2700家酒店洗浴场所‘, ‘passage‘ : ‘正文内容‘, }, ‘comments‘ : { ‘link‘ : ‘http://comment5.news.sina.com.cn/comment/skin/default.html?channel=gn&newsid=1-1-29471498‘, }, }