玩转python爬虫之爬取糗事百科段子


Posted in Python onFebruary 17, 2016

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

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

  • 抓取糗事百科热门段子
  • 过滤带有图片的段子
  • 实现每按一次回车显示一个段子的发布时间,发布人,段子内容,点赞数。

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

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

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

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

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

# -*- coding:utf-8 -*-
import urllib
import urllib2
 
 
page = 1
url = 'http://www.qiushibaike.com/hot/page/' + str(page)
try:
  request = urllib2.Request(url)
  response = urllib2.urlopen(request)
  print response.read()
except urllib2.URLError, e:
  if hasattr(e,"code"):
    print e.code
  if hasattr(e,"reason"):
    print e.reason

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

line 373, in _read_status
 raise BadStatusLine(line)
httplib.BadStatusLine: ''

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

# -*- coding:utf-8 -*-
import urllib
import urllib2
 
page = 1
url = 'http://www.qiushibaike.com/hot/page/' + str(page)
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
headers = { 'User-Agent' : user_agent }
try:
  request = urllib2.Request(url,headers = headers)
  response = urllib2.urlopen(request)
  print response.read()
except urllib2.URLError, e:
  if hasattr(e,"code"):
    print e.code
  if hasattr(e,"reason"):
    print e.reason

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

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

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

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

玩转python爬虫之爬取糗事百科段子

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

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

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

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

content = response.read().decode('utf-8')
pattern = re.compile('<div.*?author">.*?<a.*?<img.*?>(.*?)</a>.*?<div.*?'+
             'content">(.*?)<!--(.*?)-->.*?</div>(.*?)<div class="stats.*?class="number">(.*?)</i>',re.S)
items = re.findall(pattern,content)
for item in items:
  print item[0],item[1],item[2],item[3],item[4]

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

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

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

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

这样我们就获取了发布人,发布时间,发布内容,附加图片以及点赞数。

在这里注意一下,我们要获取的内容如果是带有图片,直接输出出来比较繁琐,所以这里我们只获取不带图片的段子就好了。

所以,在这里我们就需要对带图片的段子进行过滤。

我们可以发现,带有图片的段子会带有类似下面的代码,而不带图片的则没有,所以,我们的正则表达式的item[3]就是获取了下面的内容,如果不带图片,item[3]获取的内容便是空。

<div class="thumb">
 
<a href="/article/112061287?list=hot&s=4794990" target="_blank">
<img src="http://pic.qiushibaike.com/system/pictures/11206/112061287/medium/app112061287.jpg" alt="但他们依然乐观">
</a>
 
</div>

所以我们只需要判断item[3]中是否含有img标签就可以了。

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

for item in items:
    haveImg = re.search("img",item[3])
    if not haveImg:
      print item[0],item[1],item[2],item[4]

现在,整体的代码如下

# -*- coding:utf-8 -*-
import urllib
import urllib2
import re
 
page = 1
url = 'http://www.qiushibaike.com/hot/page/' + str(page)
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.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('<div.*?author">.*?<a.*?<img.*?>(.*?)</a>.*?<div.*?'+
             'content">(.*?)<!--(.*?)-->.*?</div>(.*?)<div class="stats.*?class="number">(.*?)</i>',re.S)
  items = re.findall(pattern,content)
  for item in items:
    haveImg = re.search("img",item[3])
    if not haveImg:
      print item[0],item[1],item[2],item[4]
except urllib2.URLError, e:
  if hasattr(e,"code"):
    print e.code
  if hasattr(e,"reason"):
    print e.reason

运行一下看下效果

玩转python爬虫之爬取糗事百科段子

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

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

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

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

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

# -*- 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/4.0 (compatible; MSIE 5.5; Windows NT)'
    #初始化headers
    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编码
      pageCode = response.read().decode('utf-8')
      return pageCode
 
    except urllib2.URLError, e:
      if hasattr(e,"reason"):
        print u"连接糗事百科失败,错误原因",e.reason
        return None
 
 
  #传入某一页代码,返回本页不带图片的段子列表
  def getPageItems(self,pageIndex):
    pageCode = self.getPage(pageIndex)
    if not pageCode:
      print "页面加载失败...."
      return None
    pattern = re.compile('<div.*?author">.*?<a.*?<img.*?>(.*?)</a>.*?<div.*?'+
             'content">(.*?)<!--(.*?)-->.*?</div>(.*?)<div class="stats.*?class="number">(.*?)</i>',re.S)
    items = re.findall(pattern,pageCode)
    #用来存储每页的段子们
    pageStories = []
    #遍历正则表达式匹配的信息
    for item in items:
      #是否含有图片
      haveImg = re.search("img",item[3])
      #如果不含有图片,把它加入list中
      if not haveImg:
        replaceBR = re.compile('<br/>')
        text = re.sub(replaceBR,"\n",item[1])
        #item[0]是一个段子的发布者,item[1]是内容,item[2]是发布时间,item[4]是点赞数
        pageStories.append([item[0].strip(),text.strip(),item[2].strip(),item[4].strip()])
    return pageStories
 
  #加载并提取页面的内容,加入到列表中
  def loadPage(self):
    #如果当前未看的页数少于2页,则加载新一页
    if self.enable == True:
      if len(self.stories) < 2:
        #获取新一页
        pageStories = self.getPageItems(self.pageIndex)
        #将该页的段子存放到全局list中
        if pageStories:
          self.stories.append(pageStories)
          #获取完之后页码索引加一,表示下次读取下一页
          self.pageIndex += 1
  
  #调用该方法,每次敲回车打印输出一个段子
  def getOneStory(self,pageStories,page):
    #遍历一页的段子
    for story in pageStories:
      #等待用户输入
      input = raw_input()
      #每当输入回车一次,判断一下是否要加载新页面
      self.loadPage()
      #如果输入Q则程序结束
      if input == "Q":
        self.enable = False
        return
      print u"第%d页\t发布人:%s\t发布时间:%s\t赞:%s\n%s" %(page,story[0],story[2],story[3],story[1])
  
  #开始方法
  def start(self):
    print u"正在读取糗事百科,按回车查看新段子,Q退出"
    #使变量为True,程序可以正常运行
    self.enable = True
    #先加载一页内容
    self.loadPage()
    #局部变量,控制当前读到了第几页
    nowPage = 0
    while self.enable:
      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()

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

我们第一个爬虫实战项目介绍到这里,希望大家喜欢。

Python 相关文章推荐
python基础教程之缩进介绍
Aug 29 Python
Python列表生成器的循环技巧分享
Mar 06 Python
Python图算法实例分析
Aug 13 Python
Python3.6 Schedule模块定时任务(实例讲解)
Nov 09 Python
AI人工智能 Python实现人机对话
Nov 13 Python
Python输出由1,2,3,4组成的互不相同且无重复的三位数
Feb 01 Python
python判断数字是否是超级素数幂
Sep 27 Python
解决PyCharm import torch包失败的问题
Oct 13 Python
Python判断一个list中是否包含另一个list全部元素的方法分析
Dec 24 Python
PYQT5设置textEdit自动滚屏的方法
Jun 14 Python
python 轮询执行某函数的2种方式
May 03 Python
详解Python中的文件操作
Jan 14 Python
玩转python爬虫之正则表达式
Feb 17 #Python
玩转python爬虫之URLError异常处理
Feb 17 #Python
玩转python爬虫之cookie使用方法
Feb 17 #Python
Python 爬虫爬取指定博客的所有文章
Feb 17 #Python
Using Django with GAE Python 后台抓取多个网站的页面全文
Feb 17 #Python
python实现RSA加密(解密)算法
Feb 17 #Python
使用python实现rsa算法代码
Feb 17 #Python
You might like
PHP常用的排序和查找算法
2015/08/06 PHP
PHP+jQuery+Ajax实现分页效果 jPaginate插件的应用
2015/10/09 PHP
CI框架出现mysql数据库连接资源无法释放的解决方法
2016/05/17 PHP
PHP面向对象五大原则之依赖倒置原则(DIP)详解
2018/04/08 PHP
jQuery 连续列表实现代码
2009/12/21 Javascript
JQuery1.4+ Ajax IE8 内存泄漏问题
2010/10/15 Javascript
js修改table中Td的值(定义td的单击事件)
2013/01/10 Javascript
js/jquery获取文本框输入焦点的方法
2014/03/04 Javascript
JavaScript中的单引号和双引号报错的解决方法
2014/09/01 Javascript
jQuery中unwrap()方法用法实例
2015/01/16 Javascript
jQuery树形下拉菜单特效代码分享
2015/08/15 Javascript
js实现图片放大和拖拽特效代码分享
2015/09/05 Javascript
js中scrollTop()方法和scroll()方法用法示例
2016/10/03 Javascript
JS实现websocket长轮询实时消息提示的效果
2017/10/10 Javascript
微信小程序实现tab页面切换功能
2018/07/13 Javascript
[47:12]TFT vs Secret Supermajor小组赛C组 BO3 第三场 6.3
2018/06/04 DOTA
python中查找excel某一列的重复数据 剔除之后打印
2013/02/10 Python
python模拟登陆阿里妈妈生成商品推广链接
2014/04/03 Python
Python3实现连接SQLite数据库的方法
2014/08/23 Python
Python模拟登录12306的方法
2014/12/30 Python
Windows环境下python环境安装使用图文教程
2018/03/13 Python
Python序列循环移位的3种方法推荐
2018/04/09 Python
Python封装原理与实现方法详解
2018/08/28 Python
python集合比较(交集,并集,差集)方法详解
2018/09/13 Python
Python利用pandas处理Excel数据的应用详解
2019/06/18 Python
利用PyQt5+Matplotlib 绘制静态/动态图的实现代码
2020/07/13 Python
Python3利用scapy局域网实现自动多线程arp扫描功能
2021/01/21 Python
美国折扣网站:jClub
2017/08/07 全球购物
REN Clean Skincare官网:英国本土有机护肤品牌
2019/02/23 全球购物
当我正在为表建立索引的时候,SQL Server 会禁止对表的访问吗
2014/04/28 面试题
高中班级口号
2014/06/09 职场文书
大学生党员个人剖析材料
2014/10/08 职场文书
离婚案件原告代理词
2015/05/23 职场文书
MySQL中使用or、in与union all在查询命令下的效率对比
2021/05/26 MySQL
vue-cropper组件实现图片切割上传
2021/05/27 Vue.js
Python中可变和不可变对象的深入讲解
2021/08/02 Python