Python爬虫实战(1):爬取糗事百科段子

大家好,前面入门已经说了那么多基础知识了,下面我们做几个实战项目来挑战一下吧。那么这次为大家带来,Python爬取糗事百科的小段子的例子。

首先,糗事百科大家都听说过吧?糗友们发的搞笑的段子一抓一大把,这次我们尝试一下用爬虫把他们抓取下来。

本篇目标

1.抓取糗事百科热门段子

2.过滤带有图片的段子

3.实现每按一次回车显示一个段子的发布时间,发布人,段子内容,点赞数。

糗事百科是不需要登录的,所以也没必要用到Cookie,另外糗事百科有的段子是附图的,我们把图抓下来图片不便于显示,那么我们就尝试过滤掉有图的段子吧。

好,现在我们尝试抓取一下糗事百科的热门段子吧,每按下一次回车我们显示一个段子。

1.确定URL并抓取页面代码

首先我们确定好页面的URL是 http://www.qiushibaike.com/hot/page/1,其中最后一个数字1代表页数,我们可以传入不同的值来获得某一页的段子内容。

我们初步构建如下的代码来打印页面代码内容试试看,先构造最基本的页面抓取方式,看看会不会成功

运行程序,哦不,它竟然报错了,真是时运不济,命途多舛啊

好吧,应该是headers验证的问题,我们加上一个headers验证试试看吧,将代码修改如下

嘿嘿,这次运行终于正常了,打印出了第一页的HTML代码,大家可以运行下代码试试看。在这里运行结果太长就不贴了。

2.提取某一页的所有段子

好,获取了HTML代码之后,我们开始分析怎样获取某一页的所有段子。

首先我们审查元素看一下,按浏览器的F12,截图如下

20150218002912

我们可以看到,每一个段子都是<div class=”article block untagged mb15″ id=”…”>…</div>包裹的内容。

现在我们想获取发布人,发布日期,段子内容,以及点赞的个数。不过另外注意的是,段子有些是带图片的,如果我们想在控制台显示图片是不现实的,所以我们直接把带有图片的段子给它剔除掉,只保存仅含文本的段子。

所以我们加入如下正则表达式来匹配一下,用到的方法是 re.findall 是找寻所有匹配的内容。方法的用法详情可以看前面说的正则表达式的介绍。

好,我们的正则表达式匹配语句书写如下,在原来的基础上追加如下代码

现在正则表达式在这里稍作说明

1).*? 是一个固定的搭配,.和*代表可以匹配任意无限多个字符,加上?表示使用非贪婪模式进行匹配,也就是我们会尽可能短地做匹配,以后我们还会大量用到 .*? 的搭配。

2)(.*?)代表一个分组,在这个正则表达式中我们匹配了五个分组,在后面的遍历item中,item[0]就代表第一个(.*?)所指代的内容,item[1]就代表第二个(.*?)所指代的内容,以此类推。

3)re.S 标志代表在匹配时为点任意匹配模式,点 . 也可以代表换行符。

现在我们可以看一下部分运行结果

儒雅男神 2015-02-17 14:34:42

小时候一个一个拆着放的举个爪…

<div class=”thumb”>

<a href=”/article/100705418?list=hot&s=4747301″ target=”_blank” onclick=”_hmt.push([‘_trackEvent’, ‘post’, ‘click’, ‘signlePost’])”>
<img src=”http://pic.qiushibaike.com/system/pictures/10070/100705418/medium/app100705418.jpg” alt=”糗事#100705418″ />
</a>

</div>

7093
奇怪的名字啊 2015-02-17 14:49:16

回家的路,你追我赶,回家的心情和窗外的阳光一样灿烂。一路向前,离亲人越来越近了。哪里有爸妈哪里才是家,希望所有糗友的爸爸妈妈都身体健康…….

4803

这是其中的两个段子,分别打印了发布人,发布时间,发布内容,附加图片以及点赞数。

其中,附加图片的内容我把图片代码整体抠了出来,这个对应item[3],所以我们只需要进一步判断item[3]里面是否含有img这个字样就可以进行过滤了。

好,我们再把上述代码中的for循环改为下面的样子

现在,整体的代码如下

运行一下看下效果

20150218005549

恩,带有图片的段子已经被剔除啦。是不是很开森?

3.完善交互,设计面向对象模式

好啦,现在最核心的部分我们已经完成啦,剩下的就是修一下边边角角的东西,我们想达到的目的是:

按下回车,读取一个段子,显示出段子的发布人,发布日期,内容以及点赞个数。

另外我们需要设计面向对象模式,引入类和方法,将代码做一下优化和封装,最后,我们的代码如下所示

好啦,大家来测试一下吧,点一下回车会输出一个段子,包括发布人,发布时间,段子内容以及点赞数,是不是感觉爽爆了!

我们第一个爬虫实战项目介绍到这里,欢迎大家继续关注,小伙伴们加油!

打赏支持我写出更多好文章,谢谢!

打赏作者

打赏支持我写出更多好文章,谢谢!

任选一种支付方式

1 25 收藏 24 评论

关于作者:崔庆才

静觅 静静寻觅生活的美好个人站点 cuiqingcai.com 个人主页 · 我的文章 · 17 ·    

相关文章

可能感兴趣的话题



直接登录
最新评论
  • lucifiarain   2015/05/01

    糗百的这个例子里边正则表达式有问题吧,看了看现在糗百的html代码和这个貌似不匹配了

    • 姜凡   2015/05/04

      '.*?<a.*?.*?(.*?)"(.*?)"(.*?)(.*?)'

      这是我对着糗百改的,不过我想问这个例子要运行很久么,为什么跑不出来

    • .*?<a.*?.*?(.*?).*?(.*?)(.*?)(.*?)

      试试这个,我对着网页改过的,应该没问题

      • eric   2015/05/13

        我这把运行楼主的代码一直不行,自己改了一下,发现可以
        # -*- coding:utf-8 -*-
        import urllib
        import urllib2
        import re

        #糗事百科爬虫类
        class QSBK:

        #初始化方法,定义一些变量
        def __init__(self):
        self.pageIndex = 1
        self.user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
        self.host = 'www.qiushibaike.com'
        #初始化headers
        self.headers = { 'User-Agent' : self.user_agent , 'Host' : self.host}
        #存放段子的变量,每一个元素是每一页的段子们
        self.stories = []
        #存放程序是否继续运行的变量
        self.enable = False
        #传入某一页的索引获得页面代码
        def getPage(self,pageIndex):
        try:
        url = 'http://www.qiushibaike.com/8hr/page/' + str(pageIndex)
        #构建请求的request
        request = urllib2.Request(url, headers = self.headers)
        # print request

        #利用urlopen获取页面代码
        response = urllib2.urlopen(request)
        # print response
        #将页面转化为UTF-8编码
        pageCode = response.read().decode('utf-8')
        # print pageCode
        return pageCode

        except urllib2.URLError, e:
        if hasattr(e,"reason"):
        print u"连接糗事百科失败,错误原因",e.reason
        return None

        #传入某一页代码,返回本页不带图片的段子列表
        def getPageItems(self,pageIndex):
        pageCode = self.getPage(pageIndex)
        # print pageCode
        if not pageCode:
        print "页面加载失败...."
        return None
        # pattern = re.compile('.*?(.*?)(.*?).*?.*?(.*?).*?.*?.*?(.*?).*?.*?',re.S)
        pattern = re.compile('(.*?)(.*?).*?', re.S)
        # pattern = re.compile('.*?<a.*?.*?(.*?).*?(.*?)(.*?)(.*?)',re.S)
        items = re.findall(pattern, pageCode)
        # print items
        #用来存储每页的段子们
        pageStories = []
        #遍历正则表达式匹配的信息
        for item in items:
        #是否含有图片
        haveImg = re.search("img",item[0])
        # print haveImg
        #如果不含有图片,把它加入list中
        if not haveImg:
        #item[0]是一个段子的发布者,item[1]是发布时间,item[2]是内容,item[4]是点赞数
        pageStories.append([item[1].strip(), item[2].strip()])
        return pageStories

        #加载并提取页面的内容,加入到列表中
        def loadPage(self):
        #如果当前未看的页数少于2页,则加载新一页
        if self.enable == True:
        if len(self.stories) 0:
        #从全局list中获取一页的段子
        pageStories = self.stories[0]
        #当前读到的页数加一
        nowPage += 1
        #将全局list中第一个元素删除,因为已经取出
        del self.stories[0]
        #输出该页的段子
        self.getOneStory(pageStories,nowPage)

        spider = QSBK()
        spider.start()

    • 为什么打出来的东西就变了= =
      .*?<a.*?.*?(.*?).*?(.*?)(.*?)(.*?)

    • 泡泡先生 Student 2016/07/21

      自己看下source code可以修改下

  • lucifiarain   2015/05/04

    .*?<a.*?.*?(.*?).*?.*?(.*?).*?\s*(.*?).*?'+
    '(.*?)
    这个是我改的正则表达式,但是还是会筛选出有图片的,但是想了想并没有好办法解决~

  • jcchen   2015/05/07

    pageCode = response.read().decode('utf-8') 这里为什么要decode?但是print pageCode没有结果是什么回事啊~~~

  • 小时   2015/05/25

    刚刚开始写爬虫,为什么程序在windows下面运行后,闪个黑屏什么都没有出现,鼠标双击了好几次,都是没有结果,这个是为什么

  • renyouyin   2015/08/25

    LZ的正则表达式,无法抓到匿名用户发的帖子。

  • neo1218   2015/08/26

    decode('utf-8')不是将"页面转化为UTF-8编码",而是解码为unicode!

  • Bjarne-Scott-Lee 游戏开发 2016/01/10

    今天写了个匹配现在糗百的版本

    request = urllib2.Request(url,headers = headers)
    response = urllib2.urlopen(request)

    content = response.read().decode('utf-8')
    # print content.encode("GBK","ignore")
    # pattern = re.compile('(.*?).*?',re.S)

    pattern = re.compile('<div.*?class="author.*?<a.*?.*?<a.*?title="(.*?)".*?.*?.*?(.*?).*?.*?(.*?)',re.S)

    #[0] author
    #[1] content
    #[2] date
    #[3] number of likes
    items = re.findall(pattern,content)

    for item in items:
    have_img = re.search("img",item[1])
    if not have_img:
    local_time = time.localtime(float(item[2]))
    time_str = time.strftime("%Y-%m-%d %H:%M:%S",local_time)
    text = item[1].encode("GBK","ignore")
    print "author:",item[0],"time:",time_str,"like:",item[3],text

  • Bjarne-Scott-Lee 游戏开发 2016/01/10

    为嘛代码贴上去变了

  • 孤灯明不灭 数据挖掘 2016/01/20

    import urllib.request
    import urllib,re
    class qsbk:
    def __init__(self):
    self.pageindex = 1
    self.user_agent = 'Mozilla/5.0 (Windows NT 10.0; WOW64)'
    self.headers = {'User-Agent':self.user_agent}
    def getpage(self):
    try:
    url = r'http://www.qiushibaike.com/hot/page/'+str(self.pageindex)
    request = urllib.request.Request(url,headers = self.headers)
    response = urllib.request.urlopen(request)
    pagecode = response.read().decode('utf-8')
    return pagecode
    except:
    print('Error!')
    def getpageitems(self):
    pagecode = self.getpage()
    if not pagecode:
    print('页面失败')
    return None
    pattern = re.compile(r'.*?<a.*?<a.*?(.*?).*?(.*?)<!.*?.*?(.*?)',re.S)
    items = re.findall(pattern,pagecode)
    pagestories = []
    for item in items:
    pagestories.append([item[0].strip(),item[1].strip().replace('','\n'),item[2].strip()])
    return pagestories
    def load(self,k):
    while self.pageindex<k:
    for i in self.getpageitems():
    print('作者:'+i[0]+'\n',' '+i[1]+'\n','点赞数:'+i[2].strip()+'\n\n')
    self.pageindex += 1

    qs=qsbk()
    qs.load(4)

    • 孤灯明不灭 数据挖掘 2016/01/20

      r'.*?<a.*?<a.*?(.*?)'+
      '.*?(.*?)<!.*?.'+
      '*?(.*?)',re.S)

      正则表达式太长,刚才没有显示出来
      我是Python3.5

  • franklingcn   2016/04/06

    最新代码,由于糗百已经改版了,楼主的代码部分失效了,我自己更新了正则表达式。

    # -*- coding:utf-8 -*-
    import urllib
    import urllib2
    import re
    page = 2
    url = 'http://www.qiushibaike.com/hot/page/'+str(page)
    user_agent = 'Mozilla/4.0 (compatible; MSIE5.5; Windows NT)'
    headers = {'User-Agent': user_agent}
    try:
    request = urllib2.Request(url, headers=headers)
    response = urllib2.urlopen(request)
    content = response.read().decode('utf-8')
    pattern = re.compile(r'.*?(.*?).*?(.*?)', re.S)
    items = pattern.findall(content)
    for item in items:
    print item[0], item[1], item[2]

    except urllib2.URLError, e:
    if hasattr(e, "code"):
    print e.code
    if hasattr(e,"reason"):
    print e.reason

  • ヅHopeヅ   2016/04/24

    __author__='RingoYan'
    # -*- coding:utf-8 -*-
    import urllib
    import urllib2
    import re
    import thread
    import time

    #糗事百科爬虫类
    class QSBK:
    #初始化方法,定义一些变量
    def __init__(self):
    self.pageIndex = 1
    self.user_agent = 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.132 Safari/537.36s'
    self.headers = { 'User-Agent' : self.user_agent }
    #存放段子的变量,每一个元素是每一页的段子
    self.stories = []
    #存放是否继续运行的变量
    self.enable = False

    #传入某一页的索引获得页面
    def getPage(self, pageIndex):
    try:
    url = 'http://www.qiushibaike.com/hot/page/' + str(pageIndex)
    #构建请求的request
    request = urllib2.Request(url,headers = self.headers)
    #利用urlopen获取页面代码
    response = urllib2.urlopen(request)
    #将页面转化为utf-8编码
    content = response.read().decode('utf-8')
    return content
    except urllib2.URLError, e:
    if hasattr(e,"code"):
    print u"连接糗事百科失败,错误原因",e.code
    if hasattr(e,"reason"):
    print u"连接糗事百科失败,错误原因",e.reason
    return None
    #传入某一页代码,返回本页的段子列表
    def getPageItems(self, pageIndex):
    content = self.getPage(pageIndex)
    if not content:
    print "页面加载失败..."
    return None
    #正则匹配
    pattern = re.compile('.*?<a.*?.*?(.*?).*?(.*?).*?(.*?)',re.S)
    items = re.findall(pattern,content)
    #用来存储每页的段子
    pageStories = []
    #遍历正则表达式匹配的信息
    for item in items:
    pageStories.append([item[0].strip(),item[1].strip(),item[2].strip()])
    return pageStories
    #加载并提取页面的内容,加入到列表中
    def loadPage(self):
    #如果当前未看的页数少于2页,则加载新一页
    if self.enable == True:
    if len(self.stories) 0:
    #从全局list中获取一页的段子
    pageStories = self.stories[0]
    #当前读到的页数加一
    nowPage += 1
    #将全局list中第一个元素删除,因为已经取出
    del self.stories[0]
    #输出该页的段子
    self.getOneStory(pageStories,nowPage)

    #开始定义类,并调用方法
    spider = QSBK()
    spider.start()
    '''
    page = 1
    url = 'http://www.qiushibaike.com/hot/page/' + str(page)

    #add header
    user_agent = 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.132 Safari/537.36s'
    headers = { 'User-Agent' : user_agent }
    try:
    request = urllib2.Request(url,headers = headers)
    response = urllib2.urlopen(request)

    content = response.read().decode('utf-8')
    pattern = re.compile('.*?<a.*?.*?(.*?).*?(.*?).*?(.*?)',re.S)
    items = re.findall(pattern,content)
    for item in items:
    print u"第%d页\t发布人:%s\n发布内容:%s点赞数:%s\n" %(page,item[0],item[1],item[2])
    #print item[0],item[1],item[2],item[3]
    #print response.read()
    except urllib2.URLError, e:
    if hasattr(e,"code"):
    print e.code
    if hasattr(e,"reason"):
    print e.reason
    '''

    最新代码,没有去掉。
    下面的注释部分是核心,可以先跑起来。呵呵

  • 只能在Python2版本运行, 已经测试过, 全部通过。

    !/usr/bin/env python
    # -*- coding: utf-8 -*-
    import urllib
    import urllib2
    import re
    page = 2
    url = 'http://www.qiushibaike.com/hot/page/'+str(page)
    user_agent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36'
    headers = {'User-Agent': user_agent}
    try:
    request = urllib2.Request(url, headers=headers)
    response = urllib2.urlopen(request)
    content = response.read().decode('utf-8')
    class="content">(.*?).*?.*?(.*?)',re.S)
    pattern = re.compile('<div.*?class="author.*?<a.*?.*?<a.*?.*?.*?title="(.*?)">.*?<div class="content">(.*?)</div>.*?<i class="number">(.*?)</i>',re.S)
    items = pattern.findall(content)
    for item in items:
    #print(item[0] ,item[1], item[2])
    print u"发布人:%s\n发布内容:%s\n点赞数:%s\n" %(item[0],item[1],item[2])
    except urllib2.URLError, e:
    if hasattr(e,"code"):
    print e.code
    if hasattr(e,"reason"):
    print e.reason

  • 水水的羊 学生 2016/07/19

    因为作者上一节讲到了beautifulsoup,我没有和作者一样用正则表达,感觉用bs解决更加清晰。

    因为刚开始接触爬虫,以前也没有用过正则表达,感觉正则表达好难啊。我认为可以先用bs缩小代码的范围,再用正则表达找到需要的内容,不知道这种思路对不对。

  • 泡泡先生 Student 2016/07/21

    讲解深入浅出,非常易懂,糗事百科的代码已经很清楚了,代码也只是DEMO,自己改一下就行。

  • Bolian   02/10

    现在的糗事百科的网页源代码已经改变了,好像我找不到段子的发步时间,自己就着作者的改了改,也能实现以上的功能,刚刚学Python没多久,大家多多指点有什么因该规范改正的,哈哈哈哈哈哈

    我的代码:

  • Bolian   02/10

    刚开始学Python3没多久,哈哈,按着作者的代码运行发现获取到的返回结果并不是料想到的内容,然后翻看了一下糗事百科的网页源代码,发现!!!不一样!!,可能人家早就改了,所以作者的正则表达式匹配不上想要的内容,所以,嘿嘿,我就改了改,然后再逻辑函数也没参考作者的。。。感觉作者的逻辑代码有点绕。。。。。看不懂。。。所以自己就瞎写了点,有啥不对的,不规范的,谢谢多多指正。。。哈哈哈哈

    这是我自己写的,也可以实现作者的功能。。

  • wx_dNOdOCCd   02/28

    虽然正则还是看不明白,但是代码逻辑都学习了!谢谢!

跳到底部
返回顶部