从Python爬虫到SAE云和微信公众号:一、糗事百科爬虫

爱被打了一巴掌 2021-10-26 13:52 323阅读 0赞

这是写给自己玩的练习项目,从糗事百科中爬取段子放到微信公众号上去,这样我就能随时随地的看段子了,啊哈哈哈

项目结构

1.糗事百科爬虫:Pthon实现,MySQL做持久化存储

2.用免费的新浪SAE云搭建微信公众号的服务器

3.微信公众号部分

610639-20170103193823331-2126935612.png

  1. 主要结构内容划分

糗事百科爬虫

技术实现:Python2.7

插件使用:re,urllib2,pymysql。没有使用beatifulsoap

实现思路:

1)起始:从糗事百科的“穿越”(http://www.qiushibaike.com/history/)栏,可以随机打开之前的某一天的糗事页面

2)查重:根据日期判断数据库中是否已经抓取过此页面

3)页面下载:如果没有抓去过,则下载当天前两个页面到页面队列中,等待解析

4)解析页面:糗事、页面url、日期、下一天的url(页面中随机展示)

5)持久化存储:将分离出来的逐个段子和其他信息,存储到mysql中

6)下一轮抓取:根据4中得到的下一页url,进入2流程(此步骤也可以是不断刷新“穿越”页面得到新的页面,即进入1流程)

7)数据输出:为了能在新浪云SAE中使用段子数据(免费云空间不能使用mysql),从mysql中导出糗事段子为文本文件

下面是爬虫中最主要的部分,流程控制的代码和页面解析的代码:

qiushi_history_main.py代码:控制整个流程

ContractedBlock.gif ExpandedBlockStart.gif

  1. # -*- coding: utf-8 -*-
  2. import thread
  3. import time
  4. from qiushi_history_spider import html_parser
  5. from qiushi_history_spider import html_outputer
  6. from qiushi_history_spider import txt_outputer
  7. from qiushi_history_spider import page_manger
  8. from qiushi_history_spider import mysql_outputer
  9. from qiushi_history_spider import date_manager
  10. import urllib2
  11. # ----------- 加载处理糗事百科 -----------
  12. class Spider_Model:
  13. # 声明self:含有page pages enabled
  14. def __init__(self):
  15. self.pagemanger = page_manger.PageManager()
  16. self.htmlparser = html_parser.HtmlParser()
  17. # self.outputer=html_outputer.HtmlOutputer()
  18. self.txtoutputer = txt_outputer.TxtOutputer()
  19. self.mysqlOutputer = mysql_outputer.MysqlOutputer()
  20. self.datemanager = date_manager.DateManager()
  21. self.page = 1
  22. self.pages = []
  23. self.storys = []
  24. self.enable = True
  25. self.maxPageNum = 400;
  26. def Start(self):
  27. self.enable = True
  28. page = self.page
  29. print u'正在加载中请稍候......'
  30. # 新建一个线程在后台加载段子并存储
  31. startUrl = 'http://www.qiushibaike.com/history/'
  32. self.pagemanger.AddUrl(startUrl)
  33. connection = self.mysqlOutputer.open_conneciton() # 打开数据库连接
  34. self.datemanager.set_conn(connection)
  35. thread.start_new_thread(self.pagemanger.LoadPage, ())
  36. time.sleep(3)
  37. self.pages = self.pagemanger.pages
  38. # ----------- 加载处理糗事百科 -----------
  39. while self.enable:
  40. # 存储并检验是否到达预订页数
  41. if (page > self.maxPageNum):
  42. self.pagemanger.enable = False
  43. self.enable = False
  44. break
  45. # 如果self的page数组中存有元素
  46. # 等待,防止pagemanager尚未读取完毕
  47. time.sleep(2)
  48. if self.pages:
  49. if(len(self.pages)<=0):
  50. continue
  51. nowPage = self.pages[0]
  52. if(nowPage==None):
  53. del self.pages[0]
  54. continue
  55. del self.pages[0]
  56. #读取内容
  57. self.storys, thisUrlCode, nextUrlCode, dateStr = self.htmlparser.GetStorys2(nowPage)
  58. # 判断是否重复
  59. nodump = self.datemanager.no_duplicate(thisUrlCode)
  60. if (nodump):
  61. # region-----------保存当前日期的第一页---------------
  62. self.datemanager.save_code(dateStr, thisUrlCode)
  63. # 当前这一步没有用,现在的页面是不停的刷新‘穿越’页得到的
  64. # print '存入待读列表:',startUrl + nextUrlCode
  65. # self.pagemanger.AddUrl(startUrl + nextUrlCode)
  66. dateStoryDic = {
  67. 'story': self.storys, 'urlcode': thisUrlCode, 'date': dateStr, 'page': 1}
  68. # print dateStoryDic
  69. self.mysqlOutputer.collect_data(dateStoryDic)
  70. self.PrintInfo(page, dateStr, thisUrlCode, nextUrlCode, 1)
  71. page += 1
  72. # endregion
  73. # region-----------保存当前日期的第二页----------------
  74. print "总页数:" + bytes(page), "日期:" + dateStr, "页码:" + bytes(2)
  75. print "当前页Code:",thisUrlCode+'/page/2'
  76. secondPageUrl = startUrl+thisUrlCode+'/page/2'
  77. secondPage=self.pagemanger.ReadPageFromUrl(secondPageUrl)
  78. if(secondPage==None):
  79. print "加载此页失败!"
  80. continue
  81. self.storys, thisUrlCode, nextUrlCode, dateStr = self.htmlparser.GetStorys2(secondPage)
  82. dateStoryDic = {
  83. 'story': self.storys, 'urlcode': thisUrlCode, 'date': dateStr, 'page': 2}
  84. self.mysqlOutputer.collect_data(dateStoryDic)
  85. #page += 1
  86. # endregion
  87. self.mysqlOutputer.output_database()
  88. else:
  89. self.enable = False
  90. break
  91. self.mysqlOutputer.close_conneciton()
  92. # 打印输出 抓取的总页数,日期字符串,当前code,下一错的,当前日期第几页
  93. def PrintInfo(self, pagecount, datestr, thiscode, nextcode, pageIndex):
  94. print "总页数:" + bytes(pagecount), "日期:" + datestr, "页码:" + bytes(pageIndex)
  95. print "当前页Code:", thiscode
  96. #print "下一页Code:", nextcode
  97. # ----------- 程序的入口处 -----------
  98. # 1从数据库中读取所有id?
  99. # 2每次从首页(history)读取
  100. # 3首先获取当前页id,查重
  101. # 4如无重复则解析 存储到数据库
  102. # 5如果重复,直接读取下一页
  103. # 6读取下一页,重复2-6
  104. # Todolist:
  105. # 1.读取当天的第二页第三页
  106. # 2.输出mysql数据库中的数据到文件
  107. # 3.把datecode从数据库中读出到数组,以后查重更快
  108. myModel = Spider_Model()
  109. myModel.Start()
  110. # 否则 python错误Unhandled exception in thread started by Error in sys.excepthook
  111. time.sleep(3)

主控制程序

html_parser.py 页面解析代码

ContractedBlock.gif ExpandedBlockStart.gif

  1. # coding:utf8
  2. import re
  3. class HtmlParser(object):
  4. #auther AllenRobin cnblogs.com/GISRSMAN/
  5. # 将所有的段子都扣出来,添加到列表中并且返回列表
  6. def __init__(self):
  7. self.storys=[]
  8. self.keywords=[]
  9. self.nextUrlCode=''
  10. #获得笑话正文、当前页Code、下一页Code
  11. def GetStorys(self,page):
  12. # print myPage
  13. unicodePage = page.decode("utf-8")
  14. # 找出所有class="content"的div标记
  15. #re.S是任意匹配模式,也就是.可以匹配换行符
  16. #myItems = re.findall('<div.*?class="content">(.*?)</div>',unicodePage,re.S)
  17. myItems = re.findall('<div.*?class="content">\n\n+<span>(.*?)</span>\n\n+</div>',unicodePage,re.S)
  18. thisUrlCode=re.findall('<link rel="canonical" href="http://www.qiushibaike.com/history/(.*?)/">',unicodePage,re.S)[0]
  19. nextUrlCode=re.findall('<a class="random" href="/history/(.*?)/".*?',unicodePage,re.S)[0]
  20. return myItems,thisUrlCode,nextUrlCode
  21. #获得笑话正文(作者赞数)、当前页Code、下一页Code
  22. def GetStorys2(self,pageContent):
  23. try:
  24. unicodePage= pageContent.decode("utf-8")
  25. pattern = re.compile('<div class="author clearfix">.*?<h2>(.*?)</h2>.*?<div.*?' +'content">\n\n+<span>(.*?)</span>\n\n+</div>(.*?)<span class="stats-vote"><i class="number">(.*?)</i>',re.S)
  26. #items三个要素依次为用户名、段子内容、赞数
  27. items = re.findall(pattern, pageContent)
  28. for item in items:
  29. #去除段子内容中的查看全文
  30. item[1].replace("<span class=\"contentForAll\">查看全文","").replace("</span>","").replace("'","\"")
  31. #除去含有图片的
  32. haveImg = re.search("img", item[3])
  33. if haveImg:
  34. print item
  35. del item
  36. #可以将这三个合并到上一个提高效率
  37. thisUrlCode = re.findall('<link rel="canonical" href="http://www.qiushibaike.com/history/(.*?)/">', unicodePage, re.S)[0]
  38. nextUrlCode = re.findall('<a class="random" href="/history/(.*?)/".*?', unicodePage, re.S)[0]
  39. dateStrs = re.findall('<meta name="keywords" content="(.*?)" />', unicodePage, re.S)[0]
  40. return items,thisUrlCode,nextUrlCode,dateStrs
  41. except Exception, e:
  42. print Exception, ":", e

页面解析代码

整个爬虫项目放到Github上吧,欢迎fork

GitHub地址:https://github.com/csdallen/qiushi\_history\_spider

参考资源:

Python开发简单爬虫

Python爬虫实战一之爬取糗事百科段子

转载于:https://www.cnblogs.com/GISRSMAN/p/6245401.html

发表评论

表情:
评论列表 (有 0 条评论,323人围观)

还没有评论,来说两句吧...

相关阅读

    相关 百科爬虫

    这几天看了不少phtyon 的基础,试着做了一个daemo 但不是很成功 不知道家里网络不太好还正则匹配的不好,re.findall 的数据不是特别的稳定,有时候要加载很长