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

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

项目结构

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

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

3.微信公众号部分

                                             主要结构内容划分

糗事百科爬虫

技术实现: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代码:控制整个流程

# -*- coding: utf-8 -*-
import thread
import time
from qiushi_history_spider import html_parser
from qiushi_history_spider import html_outputer
from qiushi_history_spider import txt_outputer
from qiushi_history_spider import page_manger
from qiushi_history_spider import mysql_outputer
from qiushi_history_spider import date_manager
import urllib2


# ----------- 加载处理糗事百科 -----------
class Spider_Model:
    # 声明self:含有page pages enabled
    def __init__(self):
        self.pagemanger = page_manger.PageManager()
        self.htmlparser = html_parser.HtmlParser()
        # self.outputer=html_outputer.HtmlOutputer()
        self.txtoutputer = txt_outputer.TxtOutputer()
        self.mysqlOutputer = mysql_outputer.MysqlOutputer()
        self.datemanager = date_manager.DateManager()
        self.page = 1
        self.pages = []
        self.storys = []
        self.enable = True
        self.maxPageNum = 400;

    def Start(self):
        self.enable = True
        page = self.page
        print u'正在加载中请稍候......'
        # 新建一个线程在后台加载段子并存储
        startUrl = 'http://www.qiushibaike.com/history/'
        self.pagemanger.AddUrl(startUrl)
        connection = self.mysqlOutputer.open_conneciton()  # 打开数据库连接
        self.datemanager.set_conn(connection)
        thread.start_new_thread(self.pagemanger.LoadPage, ())

        time.sleep(3)
        self.pages = self.pagemanger.pages
        # ----------- 加载处理糗事百科 -----------
        while self.enable:
            # 存储并检验是否到达预订页数
            if (page > self.maxPageNum):
                self.pagemanger.enable = False
                self.enable = False
                break

            # 如果self的page数组中存有元素
            # 等待,防止pagemanager尚未读取完毕
            time.sleep(2)
            if self.pages:
                if(len(self.pages)<=0):
                    continue
                nowPage = self.pages[0]

                if(nowPage==None):
                    del self.pages[0]
                    continue
                del self.pages[0]
                #读取内容
                self.storys, thisUrlCode, nextUrlCode, dateStr = self.htmlparser.GetStorys2(nowPage)

                # 判断是否重复
                nodump = self.datemanager.no_duplicate(thisUrlCode)
                if (nodump):
                    # region-----------保存当前日期的第一页---------------
                    self.datemanager.save_code(dateStr, thisUrlCode)

                    # 当前这一步没有用,现在的页面是不停的刷新‘穿越’页得到的
                    # print '存入待读列表:',startUrl + nextUrlCode
                    # self.pagemanger.AddUrl(startUrl + nextUrlCode)

                    dateStoryDic = {'story': self.storys, 'urlcode': thisUrlCode, 'date': dateStr, 'page': 1}
                    # print dateStoryDic
                    self.mysqlOutputer.collect_data(dateStoryDic)
                    self.PrintInfo(page, dateStr, thisUrlCode, nextUrlCode, 1)
                    page += 1
                    # endregion

                    # region-----------保存当前日期的第二页----------------
                    print "总页数:" + bytes(page), "日期:" + dateStr, "页码:" + bytes(2)
                    print "当前页Code:",thisUrlCode+'/page/2'
                    secondPageUrl = startUrl+thisUrlCode+'/page/2'
                    secondPage=self.pagemanger.ReadPageFromUrl(secondPageUrl)

                    if(secondPage==None):
                        print "加载此页失败!"
                        continue
                    self.storys, thisUrlCode, nextUrlCode, dateStr = self.htmlparser.GetStorys2(secondPage)
                    dateStoryDic = {'story': self.storys, 'urlcode': thisUrlCode, 'date': dateStr, 'page': 2}
                    self.mysqlOutputer.collect_data(dateStoryDic)

                    #page += 1
                    # endregion

                    self.mysqlOutputer.output_database()
            else:
                self.enable = False
                break

        self.mysqlOutputer.close_conneciton()

    # 打印输出 抓取的总页数,日期字符串,当前code,下一错的,当前日期第几页
    def PrintInfo(self, pagecount, datestr, thiscode, nextcode, pageIndex):
        print "总页数:" + bytes(pagecount), "日期:" + datestr, "页码:" + bytes(pageIndex)
        print "当前页Code:", thiscode
        #print "下一页Code:", nextcode


# ----------- 程序的入口处 -----------
# 1从数据库中读取所有id?
# 2每次从首页(history)读取
# 3首先获取当前页id,查重
# 4如无重复则解析 存储到数据库
# 5如果重复,直接读取下一页
# 6读取下一页,重复2-6

# Todolist:
# 1.读取当天的第二页第三页
# 2.输出mysql数据库中的数据到文件
# 3.把datecode从数据库中读出到数组,以后查重更快

myModel = Spider_Model()
myModel.Start()

# 否则 python错误Unhandled exception in thread started by Error in sys.excepthook
time.sleep(3)
主控制程序

html_parser.py 页面解析代码

# coding:utf8
import re

class HtmlParser(object):
    #auther AllenRobin  cnblogs.com/GISRSMAN/
    # 将所有的段子都扣出来,添加到列表中并且返回列表
    def __init__(self):
        self.storys=[]
        self.keywords=[]
        self.nextUrlCode=''

    #获得笑话正文、当前页Code、下一页Code
    def GetStorys(self,page):
        # print myPage
        unicodePage = page.decode("utf-8")
        # 找出所有class="content"的div标记
        #re.S是任意匹配模式,也就是.可以匹配换行符
        #myItems = re.findall('<div.*?class="content">(.*?)</div>',unicodePage,re.S)
        myItems = re.findall('<div.*?class="content">

+<span>(.*?)</span>

+</div>',unicodePage,re.S)
        thisUrlCode=re.findall('<link rel="canonical" href="http://www.qiushibaike.com/history/(.*?)/">',unicodePage,re.S)[0]
        nextUrlCode=re.findall('<a class="random" href="/history/(.*?)/".*?',unicodePage,re.S)[0]

        return myItems,thisUrlCode,nextUrlCode

    #获得笑话正文(作者赞数)、当前页Code、下一页Code
    def GetStorys2(self,pageContent):
        try:
            unicodePage= pageContent.decode("utf-8")
            pattern = re.compile('<div class="author clearfix">.*?<h2>(.*?)</h2>.*?<div.*?' +'content">

+<span>(.*?)</span>

+</div>(.*?)<span class="stats-vote"><i class="number">(.*?)</i>',re.S)
            
            #items三个要素依次为用户名、段子内容、赞数
            items = re.findall(pattern, pageContent)

            for item in items:
                #去除段子内容中的查看全文
                item[1].replace("<span class="contentForAll">查看全文","").replace("</span>","").replace("'",""")
                #除去含有图片的
                haveImg = re.search("img", item[3])
                if  haveImg:
                    print item
                    del item
                    
            #可以将这三个合并到上一个提高效率
            thisUrlCode = re.findall('<link rel="canonical" href="http://www.qiushibaike.com/history/(.*?)/">', unicodePage, re.S)[0]
            nextUrlCode = re.findall('<a class="random" href="/history/(.*?)/".*?', unicodePage, re.S)[0]
            dateStrs = re.findall('<meta name="keywords" content="(.*?)" />', unicodePage, re.S)[0]

            return items,thisUrlCode,nextUrlCode,dateStrs
        except Exception, e:
            print Exception, ":", e
页面解析代码

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

GitHub地址:https://github.com/csdallen/qiushi_history_spider

参考资源:

Python开发简单爬虫

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

 

原文地址:https://www.cnblogs.com/GISRSMAN/p/6245401.html