浮云脱水小说站的搭建

3,073 views

Published on

Published in: Technology
0 Comments
7 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
3,073
On SlideShare
0
From Embeds
0
Number of Embeds
6
Actions
Shares
0
Downloads
47
Comments
0
Likes
7
Embeds 0
No embeds

No notes for slide

浮云脱水小说站的搭建

  1. 1. 《浮云脱水小说站》的搭建<br />http://www.zhikanlz.com<br />Python、BeautifulSoup、web.py、 twisted、nginx<br />Author: piglei2007@gmail.com<br />
  2. 2. 起因<br /> 一个寂寞的周末,作者手贱点开了这个“直播贴”<br />翻了十页以后发现贴吧直播贴完全是个 坑爹 的存在…<br />偷懒的程序员,做点什么吧!<br />
  3. 3. 想要的结果<br />最初的想法:<br />我需要一个脚本来替我完成抓取帖子中所有楼主的发言,并把它们存成一个静态页面来供阅读<br />我需要:<br />一个解析页面的工具(BeautifulSoup )<br />http://www.crummy.com/software/BeautifulSoup/ <br />
  4. 4. Code<br />import urllib<br />import urlparse<br />from BeautifulSoup import BeautifulSoup<br />class BaiduZhibo(object):<br /> """<br />可迭代的对象,不停返回下一页含有给定username的内容(默认为楼主)<br /> 返回格式:<br />字典:{<br /> "url": "..." #当前链接地址<br />"page": 5 #当前页数<br />"content": [...] #列表,里面有当前页每一个指定username的发言内容<br />}<br />参数:<br />url: 帖子地址<br />
  5. 5. Code<br />obj_name: 需要抓取的用户昵称,默认为楼主<br />limit: 限定抓取页面的数量,默认无限制<br />html: 设定输出格式,True不做处理,False替换换行符、空格<br />"""<br /> def __init__(self, url, obj_name=None, limit=0, html=False):<br /> … …<br /> def next(self):<br /> if (self.limit and self.counter == self.limit) or (self.nowurl is None):<br /> print "finished."<br /> raise StopIteration<br /> … …<br /> def __iter__(self):<br /> return self<br /> … …<br />if __name__ == ‘__main__’:<br /> # 初始化一个BaiduZhibo对象,然后拼装html页面<br />
  6. 6. Thinking <br />效果页面<br />虽然已经初步达到了效果,能不能把这个东西升级成一个站点呢。<br />初步想法:<br />管理员(我)可以通过后台来添加帖子的原始url、分类、作者等信息。<br />另外用一个cron来负责根据后台添加的信息抓取生成html页面<br />项目到此分为两个:<br />nowater:<br />负责被cron调用,从数据库查询出小说资源进行抓取工作<br />nowater_web:<br />负责前端web的展示,提供添加小说资源的后台<br />
  7. 7. 准备工作<br />Web框架的选择:<br />特点:<br />数据库结构简单(最初设计只有寥寥几张表)<br />业务逻辑简单(展示)<br />web.py (http://webpy.org/)<br />足够简洁<br />换个新鲜的<br />模板引擎(mako)<br />数据库(postgresql)<br />
  8. 8. 表结构设计<br />CREATE TABLE "nowater_novel" (<br /> "id" serial NOT NULL PRIMARY KEY,<br /> "title" varchar(100) NOT NULL,<br /> "author" varchar(20) NOT NULL,<br /> "content_type" integer NULL, --小说类型<br /> "type" varchar(10) NOT NULL default 'baidu', --来源类型<br /> “url” varchar(200) NOT NULL unique, --来源url<br /> “update_interval” integer default 10, --更新间隔<br /> "email" varchar(40) NULL, --推荐人email,如果有的话<br /> “last_update_floor” integer NOT NULL default 0, --最后更新的楼层<br /> “last_update_url” varchar(500) NOT NULL, --最后更新的url<br /> “last_update_time” timestamp with time zone, --最后更新时间<br /> "ip" varchar(20) NOT NULL default '',<br /> "status" integer NOT NULL default 0,<br /> "jointime" timestamp with time zone NOT NULL DEFAULT CURRENT_TIMESTAMP<br />);<br />NOVEL_STATUS = { <br /> 0: u"未初始化",<br /> 1: u"更新中...",<br /> 2: u"等待更新",<br /> 3: u"已完结",<br /> 4: u"更新异常",<br /> 100: u"已删除"<br />}<br />
  9. 9. 项目结构<br />[database]<br />novels<br />view_count<br />page_info<br />… …<br />获得小说url等信息<br />添加novel小说源信息<br />#hash<br />ff/<br /> #novel id<br /> 373/<br /> 1.html<br /> 2.html<br /> … …<br />[nowater]<br />main.py<br />basedb.py<br />baidu_crawler.py<br />writer.py<br />novels/<br />… …<br />[nowater_web]<br />web.py<br />包含两个app<br />Novel:浏览脱水小说的前台<br />Admin:一个添加修改小说源的管理后台<br />封装一些简单的对数据库的操作<br />处理对baidu贴吧帖子的抓取逻辑<br />接收格式通用的数据,写入html文件,需要处理分页<br />提供抓取完成的html静态文件<br />5min – main.py<br />Crontab<br />
  10. 10. 问题和解决<br />多线程抓取<br />Threading模块<br />Queue模块<br />抓取内容的分页<br />建立page_info表(字段:page、word_count)<br />在writer.py中集中处理写入逻辑(是否应该分页,分页参数的设置)<br />支持其他社区<br />baidu_crawler.py,封装页面处理逻辑,格式化内容输出<br />由于对输出格式的统一,后期可方便添加tianya_crawler.py … …<br />图片反盗链的处理<br />在**_crawler.py里面对盗链图片url进行预处理<br />使用nginx的反向代理<br />CREATE TABLE "page_info" (<br /> "id" integer NOT NULL PRIMARY KEY,<br /> "page" integer NOT NULL default 1,<br /> "word_count" integer NOT NULL default 0<br />)<br />http://hiphotos.baidu.com/%C2%B9%CD%E8%C3%A8/pic/item/e575bcf5acb72b73730eecf4.jpg<br />-------------------------转换为-------------------------<br />http://www.zhikanlz.com/get_outside_image/hiphotos.baidu.com/%C2%B9%CD%E8%C3%A8/pic/item/e575bcf5acb72b73730eecf4.jpg<br /> location ~* ^/get_outside_image/(.*?)/(.*) {<br /> expires 30d;<br /> set $obj_referrer http://$1/;<br /> resolver 8.8.8.8;<br />proxy_pass http://$1/$2;<br />proxy_set_headerreferer $obj_referrer;<br />proxy_set_header host $1;<br /> }<br />
  11. 11. 后续问题&改进途径<br />小说源由管理员(我)手动添加效率太低<br />提供txt下载<br />对天涯社区的支持<br />改进途径:<br />允许用户直接添加原帖地址,并实时的展现抓取的过程<br />从抓取的html文件生成txt。<br />新建tianya_crawler.py<br />
  12. 12. 准备工作<br />Comet<br />long-polling<br />基于ajax的长轮询<br />框架选择(Twisted)<br />
  13. 13. 项目结构<br />[nowater_web]<br />web.py<br />包含两个app<br />Novel: …<br />Admin: …<br />添加novel小说源信息<br />[database]<br />… …<br />Nginx<br />使用twisted.web维持一个长连接<br />该长连接起到实时返回抓取进度的作用,同时writer.py也通过这个文件提供的http接口与之交互<br />[nowater]<br />main.py<br />basedb.py<br />baidu_crawler.py<br />writer.py<br />long_looping.py<br />tianya_crawler.py<br />… …<br />提供抓取完成的html静态文件<br />调用http接口驱动爬虫<br />对于抓取进度方面的长连接请求反向代理到这里来<br />
  14. 14. Comet<br />实时显示抓取进度<br />class Progress(resource.Resource):<br /> def render_GET(self, request):<br /> id = request.args.get("id", [""])[0]<br />client_id = request.args.get("client_id", [""])[0]<br /> client = mb.connection_made(id, client_id, request)<br /> if isinstance(client, str):<br /> return client<br /> else:<br />request.notifyFinish().addErrback(self.disconnect, client)<br /> return server.NOT_DONE_YET<br /> def disconnect(self, ret, client):<br />client.request = None<br />client.status = False<br />
  15. 15. Comet<br />实时显示抓取进度<br /> def render_POST(self, request):<br /> """<br />通知某小说更新到了某一页<br />page:<br /> start 开始<br />r_12 表示初始页面的12页<br />o_12 表示小说页面的12页<br />end 结束<br />"""<br /> id = request.args.get("id", [""])[0]<br /> page = request.args.get("page", [""])[0]<br />mb.new_msg(id, page)<br /> return "ok"<br />
  16. 16. 问题和解决<br />下载txt文件的实现<br />Nginxsendfile<br />location /download_txt/ {<br /> internal;<br /> alias /;<br />}<br />PY:<br />file = get_txt_file(str(id), web.utf8(novel.title))<br /> if file:<br />web.replace_header("Content-Type", "text/plaintext")<br />web.replace_header("Content-Disposition", "attachment; filename=%s.txt" % id)<br />web.header("X-Accel-Redirect", "/download_txt/%s" % file)<br /> return "ok"<br />
  17. 17. 生态链<br />baidu_crawler.py<br />… …<br />twisted<br />main.py<br />writer.py<br />新帖,先插入帖子的相关信息,调用twisted的接口,开始抓取<br />旧帖,一定更新间隔<br />Comet<br />Client<br />web.py<br />提交帖子地址给web.py<br />如果是已经存在的帖子,直接返回供阅读<br />
  18. 18. Thanks<br />希望Python在中国的应用越来越多<br />Enjoy coding!<br />Author: piglei2007@gmail.com<br />

×