Scrapy爬取博客内容

http://www.cnblogs.com/Jaryleely/p/careerOne.html

python中常用的写爬虫的库有urllib2、requests,对于大多数比较简单的场景或者以学习为目的,可以用这两个库实现。这里有一篇我之前写过的用urllib2+BeautifulSoup做的一个抓取百度音乐热门歌曲的例子,有兴趣可以看一下。

本文介绍用Scrapy抓取我在博客园的博客列表,只抓取博客名称、发布日期、阅读量和评论量这四个简单的字段,以求用较简单的示例说明Scrapy的最基本的用法。

环境配置说明

操作系统:Ubuntu 14.04.2 LTS
Python:Python 2.7.6
Scrapy:Scrapy 1.0.3

注意:Scrapy1.0的版本和之前的版本有些区别,有些类的命名空间改变了。

创建项目

执行如下命令创建一个Scrapy项目

1
scrapy startproject scrapy_cnblogs

创建之后查看项目的目录结构如下:

1
2
3
4
5
6
7
8
9
scrapy_cnblogs
    ├── botcnblogs
    │     ├── __init__.py
    │     ├── items.py        #用于定义抓取内容的实体
    │     ├── pipelines.py    #处理抓取的item的管道
    │     ├── settings.py     #爬虫需要的配置参数在这里
    │   └── spiders
    │      └── __init__.py
    └── scrapy.cfg             #项目的配置文件,可以不去理会,默认即可

其中scrapy.cfg所在的目录为项目的根目录,此文件是项目的配置文件,项目建立后,此文件的内容可以不用理会。其内容如下:

1
2
3
4
5
6
[settings]
default  =  botcnblogs.settings
 
[deploy]
project  =  botcnblogs

在items.py文件里定义在抓取网页内容中抽象出来的数据结构的定义,由于这里需要博客名称、发布日期、阅读量和评论量这四个字段,定义的Item结构如下:

1
2
3
4
5
6
7
8
from  scrapy  import  Item,Field    #引入Item、Field
 
class  BotcnblogsItem(Item):
     # define the fields for your item here like:
     title  =  Field()         #标题
     publishDate  =  Field()   #发布日期
     readCount  =  Field()     #阅读量
     commentCount  =  Field()  #评论数<br><br>

在pipelines.py里对爬虫抓取到的信息(这里的信息是已经组织好的上面定义的Item对象)进行处理,官方介绍的典型的应用场景为:

  • 清理HTML数据
  • 验证爬取的数据(检查item包含某些字段)
  • 查重(并丢弃)
  • 将爬取结果保存到数据库中

它的定义也很简单,只需要实现process_item方法即可,此方法有两个参数,一个是item,即要处理的Item对象,另一个参数是spider,即爬虫。

另外还有open_spider和close_spider两个方法,分别是在爬虫启动和结束时的回调方法。

本例中处理很简单,只是将接收的Item对象写到一个json文件中,在__init__方法中以“w+”的方式打开或创建一个item.json的文件,然后把对象反序列化为字符串,写入到item.json文件中。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# -*- coding: utf-8 -*-
 
import  json
 
class  BotcnblogsPipeline( object ):
     def  __init__( self ):
         self . file  =  open ( "item.json" "w+" )
 
     def  process_item( self , item, spider):
         record  =  json.dumps( dict (item), ensure_ascii = False ) + "\n"   #此处如果有中文的话,要加上ensure_ascii=False参数,否则可能出现乱码
         self . file .write(record)
         return  item
 
     def  open_spider( self , spider):
         pass
 
     def  close_spider( self , spider):
         self . file .close()

setting.py是爬虫的配置文件,配置爬虫的一些配置信息,这里用到的就是设置pipelines的ITEM_PIPELINES参数,此参数配置项目中启用的pipeline及其执行顺序,以字典的形式存在,{“pipeline”:执行顺序整数}

此例中的配置如下:

1
2
3
4
5
6
SPIDER_MODULES  =  [ 'botcnblogs.spiders' ]
NEWSPIDER_MODULE  =  'botcnblogs.spiders'
 
ITEM_PIPELINES  =  {
    'botcnblogs.pipelines.BotcnblogsPipeline' 1 ,
}

准备工作都做好了,爬虫呢,爬虫在哪里实现呢,我们看到项目中有个spiders目录,里面只有一个init.py文件,没错,爬虫文件需要自己创建,就在这个目录下,这里创建一个botspider.py的文件,对网页进行解析的工作就要在这里实现了,此例中定义的爬虫类继承自CrawlSpider类。

定义一个Spider需要如下几个变量和方法实现:

name:定义spider名字,这个名字应该是唯一的,在执行这个爬虫程序的时候,需要用到这个名字。

allowed_domains:允许爬取的域名列表,例如现在要爬取博客园,这里要写成cnblogs.com

start_urls:爬虫最开始爬的入口地址列表。

rules:如果要爬取的页面不是单独一个或者几个页面,而是具有一定的规则可循的,例如爬取的博客有连续多页,就可以在这里设置,如果定义了rules,则需要自己定义爬虫规则(以正则表达式的方式),而且需要自定义回调函数。

代码说话:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#-*- coding:utf-8 -*-
__author__  =  'linuxfengzheng'
 
from  scrapy.spiders  import  Spider, Rule
from  scrapy.selector  import  Selector
from  botcnblogs.items  import  BotcnblogsItem
from  scrapy.linkextractors  import  LinkExtractor
import  re
from  scrapy.spiders  import  CrawlSpider
 
class  botspider(CrawlSpider):
     name  =  "cnblogsSpider"    #设置爬虫名称
 
     allowed_domains  =  [ "cnblogs.com" ]   #设置允许的域名
     start_urls  =  [
         "http://www.cnblogs.com/fengzheng/default.html?page=3" ,   #设置开始爬取页面
     ]
 
 
     rules  =  (
         Rule(LinkExtractor(allow = ( 'fengzheng/default.html\?page\=([\d]+)' , ),),callback = 'parse_item' ,follow = True ),
     )   #制定规则
 
 
     def  parse_item( self , response):
         sel  =  response.selector
         posts  =  sel.xpath( '//div[@id="mainContent"]/div/div[@class="day"]' )
         items  =  []
         for  in  posts:
             #content = p.extract()
             #self.file.write(content.encode("utf-8"))
             item  =  BotcnblogsItem()
             publishDate  =  p.xpath( 'div[@class="dayTitle"]/a/text()' ).extract_first()
 
             item[ "publishDate" =  (publishDate  is  not  None  and  [publishDate.encode( "utf-8" )]  or  [""])[ 0 ]
             #self.file.write(title.encode("utf-8"))
             title  =  p.xpath( 'div[@class="postTitle"]/a/text()' ).extract_first()
             item[ "title" =  (title  is  not  None  and  [title.encode( "utf-8" )]  or  [""])[ 0 ]
 
             #re_first("posted @ 2015-11-03 10:32 风的姿态 阅读(\d+")
 
             readcount   =  p.xpath( 'div[@class="postDesc"]/text()' ).re_first(u "阅读\(\d+\)" )
 
             regReadCount  =  re.search(r "\d+" , readcount)
             if  regReadCount  is  not  None :
                 readcount  =  regReadCount.group()
             item[ "readCount" =  (readcount  is  not  None  and  [readcount.encode( "utf-8" )]  or  [ 0 ])[ 0 ]
 
             commentcount   =  p.xpath( 'div[@class="postDesc"]/text()' ).re_first(u "评论\(\d+\)" )
             regCommentCount  =  re.search(r "\d+" , commentcount)
             if  regCommentCount  is  not  None :
                 commentcount  =  regCommentCount.group()
             item[ "commentCount" =  (commentcount  is  not  None  and  [commentcount.encode( "utf-8" )]  or  [ 0 ])[ 0 ]
             items.append(item)
 
         return  items
         #self.file.close()

因为1.0版和之前的版本在包上有所改变,这里列出此例中所涉及的不同版本的区别

所在包
1.0版本 之前版本
Spider
scrapy.spiders
scrapy.spider
CrawlSpider
scrapy.spiders
scrapy.contrib.spiders
LinkExtractor
scrapy.linkextractors
scrapy.contrib.linkextractors
Rule
scrapy.spiders scrapy.contrib.spiders

 

 

 

 

 

 

 

爬虫思路:

首先进入到我的博客页面http://www.cnblogs.com/fengzheng/,这是我的博客首页,以列表形式显示已经发布的博文,这是第一页,点击页面下面的下一页按钮,进入第二页,页面地址为http://www.cnblogs.com/fengzheng/default.html?page=2,由此看出网站以page作为参数来表示页数,这样看来爬虫的规则就很简单了, fengzheng/default.html\?page\=([\d]+),这个就是爬虫的规则,爬取default.html页面,page参数为数字的页面,这样无论有多少页都可以遍历到。当然,如果页面数量很少可以在start_urls列表中,将要爬取的页面都列出来,但是这样当博文数量增多就会出现问题,如下:

当爬取的网页具有规则定义的情况下,要继承CrawlSpider爬虫类,使用Spider就不行了,在规则定义(rules)时,如果要对爬取的网页进行处理,而不是简单的需要Url,这时,需要定义一个回调函数,在爬取到符合条件的网页时调用,并且设置follow=Ture,定义如下:

1
2
3
rules  =  (
         Rule(LinkExtractor(allow = ( 'fengzheng/default.html\?page\=([\d]+)' , ),),callback = 'parse_item' ,follow = True ),
     )

回调函数名称为parse_item,在parse_item方法中,就是真正要分析网页html,获取需要的内容的时候了。观察页面,查看需要的信息在什么位置,如图:  

之后,分析网页源码,分析出xpath

 

 用如下代码找到所有的class为day的div,每一个就是一个博文区域:

1
posts  =  sel.xpath( '//div[@id="mainContent"]/div/div[@class="day"]' )

 之后遍历这个集合,获取所需内容,其中注意一下几点:

  •  因为有中文内容,要对获取的内容进行encode("utf-8")编码
  •  由于评论数和阅读量混在一起,要对那个字符串再进行正则表达式提取 

 至此,简单的爬虫已经完成,接下来要运行这个爬虫,cd进入到爬虫项目所在的目录,执行以下命令:

1
scrapy crawl cnblogsSpider

会输出爬取过程信息

之后会看到,根目录中多了一个item.json文件,cat此文件内容,可以看到信息已经被提取出来:

  

点击这里在github获取源码

人生没有回头路,珍惜当下。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值