scrapy 爬虫(2)

在利用scrapy startproject bagualu 之后, 每个project可以建立多个spider. 命令如下:

xuyang@xuyang-ubuntu:~/python_scrapy/bagualu$ scrapy genspider bagualu bagualu.net
Created spider 'bagualu' using template 'basic' in module:
  bagualu.spiders.bagualu

# -*- coding: utf-8 -*-
import scrapy


class BagualuSpider(scrapy.Spider):
    name = "bagualu"
    allowed_domains = ["bagualu.net"]
    start_urls = (
        'http://www.bagualu.net/',
    )

    def parse(self, response):
        pass
~                         

如是, 一个缺省的spider就建立起来了. spider的基本结构就是开始抓取start_url指定的页面,然后对于抓取的页面调用parse函数解析.
然后就可以在parse函数种指定需要采集的数据 . 并且抽取感兴趣的链接进行继续爬取.
在parse种采集的数据,可以放在一个Item中,然后把这个Item会被后续的pipeline处理 , 采集的链接也会被继续爬取. 对于每个链接可以指定自己特有的解析函数. 如果不指定解析函数, 那么缺省就会调用parse函数. 注意在将item和链接传出去的时候,使用了python的一个特有语法, 叫yield . 如果没有yield, 采集到的数据是不会被后续处理的.

下面我改一下上面的蜘蛛, 修改后的代码如下:

# -*- coding: utf-8 -*-
import scrapy

class BglDoc(scrapy.Item):
    did=scrapy.Field()
    link = scrapy.Field()
    title = scrapy.Field()
    content = scrapy.Field()

class BagualuSpider(scrapy.Spider):
    name = "bagualu"
    allowed_domains = ["bagualu.net"]
    start_urls = (
        'http://www.bagualu.net/wordpress/archives/5085',
    )
    def printItem(self,arr,msg):
        val=None
        if(len(arr)>0) :
            print arr.extract()[0]
            val = arr.extract()[0]
        else:
            print msg
        return val

    def parse(self, response):
        item = BglDoc()
        item["title"] = self.printItem(response.xpath('//title/text()'),"")
        ps = response.xpath('//div[@class="span-24 single-content"]//p/text()')
        cont=""
        print "length p:" + str(len(ps))
        if len(ps) > 2:
            for j in range(len(ps)-2) :
                cont += ps[j].extract()
        item["content"] = cont
        item["link"] =  self.printItem(response.xpath('//div[@class="span-24 single-content"]//p[@align="center"]//@href'),"")
        yield item

        url = response.xpath('//a[@rel="prev"]/@href')
        if len(url) > 0 :
             url=response.urljoin(url.extract()[0])
             print "more url " + url
             yield scrapy.Request(url)

如果运行这个蜘蛛, 他就会根据抽取的url不断的抓取页面直到页面中找不到这样的链接.

接下来讲讲pipeline.
在蜘蛛中 , yield item 会抛出一个item , pipeline种可以收到这个item , 然后做进一步的处理 , 比如修改某些项,或者直接抛弃这个item,或者把这个item写如数据库等.
下面是一个pipeline的例子:
在上面的蜘蛛中 , 有一个item项没有填, 那就是did , 也就是文档的id , 这个id可以通过link来抽取 . 这个当然可以写在蜘蛛中 , 为了演示pipeline的用法, 我们把这个功能放在pipeline中. 在 跟spider同以及目录下有pipeline.py 文件. 其内容为:

class BagualuPipeline(object):
    def process_item(self, item, spider):
        return item

里面定义了一个process_item函数 , 这里拿到的item就视蜘蛛中送出的item . 我们为每个item添加did . 另外如果碰到某篇文章没有link , 就直接把这篇文章仍掉. 代码如下:

# -*- coding: utf-8 -*-
import pymongo
from scrapy.exceptions import DropItem

class BagualuPipeline(object):
    def process_item(self, item, spider):
        if item["link"] == None :
            raise DropItem("drop invalid Item , no price %s" %item)
        aa=item["link"].split('/')
        if len(aa) > 2 :
            item["did"] = aa[-1]

        return item

另外我们希望把抓到的数据放到mongodb中去. 同时,如果数据库中已经存在这篇文章,那就只需要更新一下就可以了 , 不要重复插入.
为此,我们另建一个pipeline , 叫MongoPipeline , 代码如下:

class MongoPipeline(object):
    def open_spider(self, spider):
        self.client = pymongo.MongoClient("mongodb://localhost:27017")
        self.db=self.client["bagualu_net"]
    def close_spider(self, spider):
        self.client.close()
    def process_item(self, item, spider):
        self.db["doc"].update({"did":item["did"]},dict(item),True);
        return item

我们把着两个类放在同一个文件中 . 这两个pipeline建好后, 还需要声明,才能使用, 声明的方法是修改settings.py

ITEM_PIPELINES = {
    'bagualu.pipelines.BagualuPipeline': 300,
    'bagualu.pipelines.MongoPipeline': 600,
}

后面的数字表示pipeline执行的先后顺序, 数字小的先执行.

所有这些做好以后, 我们来运行蜘蛛.
scrapy crawl bagualu



本文地址: http://www.bagualu.net/wordpress/archives/5088 转载请注明




发表评论

电子邮件地址不会被公开。 必填项已用*标注