二. Scrapy常用函数及方法

1.spider开发流程:

  • 最简单的Spider只需4个步骤:
    1).继承scrapy.Spider;
    2).为Spider取名;
    3).设置爬取的起始点;
    4).实现页面解析函数。

其中,Spider是一个基类,后面我们使用到的所有其他爬虫都需要继承这个Spider基类,例如:CrawlSpider,XMLFeedSpider,CSVFeedSpider,SitemapSpider等,这些类全部位于scrapy\spiders目录之下。

实际上设置完爬取起始点后,默认由start_reqeusts()方法构建Request对象,然后默认指定由parse方法作为页面解析函数。如果我们希望为Request添加特定的请求头部或想为Request指定特定的页面解析函数,可以考虑在构建的Spider类中实现start_requests方法,即可覆盖基类Spider的start_requests方法。例如,在第一章的基础上进行修改:

import scrapy

class Books(scrapy.Spider):
    name = 'books'
    #start_urls = ['http://books.toscrape.com/']

    #实现start_requests方法,替代start_urls这个类属性
    def start_requests(self):
        yield scrapy.Request(url="http://books.toscrape.com/",
                             callback=self.parse_book,    #此时改用parse_book作为回调函数
                             headers={'User-Agent':'Mozilla/5.0'},
                             dont_filter=True)

    def parse_book(self,response):
        infos = response.xpath('//article')
        for info in infos:
            title = info.xpath("h3/a/@title").extract()[0]
            price = info.xpath('div/p[@class="price_color"]/text()').extract()[0]

            yield {'title': title, 'price': price}   

所以,设置爬取的起爬点有两种方法:

  • 定义start_urls属性
  • 改写start_requests方法

而第四个步骤,页面解析函数需要完成以下两个工作:
1).提取数据,将数据封装后(Item或字典)提交给Scrapy引擎;
2).提取链接,并用链接构造新的Request对象提交给Scrapy引擎;其中,提取链接的方法包括使用选择器或使用LinkExtractor。

2.常用方法

1)提取常用方法
.extract() 对结果以列表的形式进行返回
.extract_first() 对extract()返回的结果列表取第一个元素。
.re() #对结果使用正则表达式进行再提取
.re_first() #返回第一个re()结果。

2)调用selector的方法
selector类的实现位于scrapy.selector模块,通过创建对象即可使用css或xpath解析方法。

from scrapy.selector import Selector

class Book(scrapy.Spider):
    ...
    
    def parse(self,response):
        selector = Selector(response)
        infos = selector.xpath("//h1")
        ...

当然,实际开发中,我们无需创建Selector对象,因为当我们第一次访问Response对象的selector属性时,Response对象会自动创建Selector对象,同时在Response对象中内置了selector对象的css和xpath方法以供使用。

class Book(scrapy.Spider):
    ...

    def parse(self,response):
        infos = response.xpath("//h1")

3)使用Item封装数据(items.py)
相对于使用字典来维护数据信息,使用item封装数据,有以下好处:
①清楚了解数据中包含哪些字段;
②包含对字段名字的检测;
③方便携带元数据,用于传递给其他组件的信息;

  • 数据段的基类:Item基类
  • 描述数据包含哪些字段的类:FIeld类
    在items.py中这样写:
from scrapy import Item,Field

class BooksItem(Item):
    title = Field()
    price = Field()

在project为books,spiders文件夹下的books.py下这样写:

from books.items import BooksItem     #引入items.py中创建的对象
    def parse_book(self,response):
        infos = response.xpath('//article')
        book = BooksItem()   #实例化BooksItem()
        for info in infos:
            book['title'] = info.xpath("h3/a/@title").extract()[0]
            book['price'] = info.xpath('div/p[@class="price_color"]/text()').extract()[0]

            yield book      #返回book

4)使用Item Pipeline处理数据(pipelines.py)
Item Pipeline的几种典型应用:

  • 清洗数据
  • 验证数据的有效性
  • 过滤重复的数据
  • 将数据存入数据库

①Item Pipeline不需要继承特定基类,只需要实现特定方法,例如:process_item、open_spider、close_spider。
②一个Item Pipeline必须实现一个process_item(item,spider)方法,该方法用来处理每一项由Spider爬取到的数据,其中两个参数:
item: 爬取到的一项数据(Item或字典)
spider:爬取此项数据的Spider对象
例如将Sharp Objects,£47.82中的英镑转换成人民币Sharp Objects,¥406.47
代码为:

class PriceConverterPipeline(object):
    
    exchange_rate = 8.5  #英镑对人民币汇率
    
    def process_item(self, item, spider):
        price = item['price'][1:] * self.exchange_rate
        item['price'] = price
        
        return item

写入MongoDB的代码,方式一:

import pymongo

class MongoDBPipeline(object):
    def __init__(self):
        client = pymongo.MongoClient('localhost',27017)
        test = client['test']
        book = test['book']
        self.post = book
        
    def process_item(self,item,spider):
        info = dict(item)
        self.post.insert(info)
        return item

写入MongoDB的代码,方式二:

import pymongo

class MongoDBPipeline(object):
    DB_URI = 'mongodb://localhost:27017/'
    DB_NAME = 'test'
    
    def open_spider(self,spider):
        self.client = pymongo.MongoClient(self.DB_URI)
        self.db = self.client[self.DB_NAME]
        
    def close_spider(self,spider):
        self.client.close()

    def process_item(self, item, spider):
        collection = self.db['book']
        post = dict(item)
        collection.insert_one(post)
        return item

过滤重复数据,这里以书名作为主键判断重复项,实际上应该以ISBN编号为主键,只是前面仅爬取了书名和价格。

from scrapy.exceptions import DropItem

class DuplicatesPipeline(object):
    def __init__(self):
        self.book_set = set()
        
    def process_item(self,item,spider):
        name = item['name']
        if name in self.book_set:
            raise DropItem('Duplicate book found:%s' %item)
        self.book_set.add(name)
        return item

由于Item Pipeline是可选的组件,想要启用某个Item Pipeline,需要在settings.py中可对Item Pipeline进行设置。
例如:

ITEM_PIPELINES = {
   'books.pipelines.PriceConverterPipeline': 300,
   'books.pipelines.MongoDBPipeline': 500,
   'books.pipelines.DuplicatesPipeline': 400,
}

其中,字典中的key为导入路径,后面的value是0~1000的数字。如果同时启动多个Pipeline,优先处理数字最小的Pipeline。

5)使用LinkExtractor提取链接
提取链接信息有两种方法,简单少量的链接使用Selector就足够了,而对于大量的链接或者复杂规则的链接,使用LinkExtractor更方便。
下面是代码的比较:

  • Selector()
next_url = response.xpath('//li[@class="next"]/a/@href').extract()[0]
if next_url:
    next_url = response.urljoin(next_url)
    yield scrapy.Request(next_url,callback=self.parse)
  • LinkExtractor()
from scrapy.linkextractors import LinkExtractor
next = LinkExtractor(restrict_xpaths='//li[@class="next"]')  #LinkExtractor中添加限制条件,如果为空会提取页面的所有链接
links = next.extract_links(response)  #返回一个Link对象的列表,里面包含链接。
if links:
    next_url = links[0].url   #next_url[0]可获取Link对象,Link对象的url属性就是绝对地址,无需自己构建相对地址。
    yield scrapy.Request(next_url,callback=self.parse)

6)使用Exporter导出数据(settings.py)

  • 可以使用命令行参数指定
  • 通过配置文件指定

命令行: scrapy crawl -o books.csv
scrapy crawl -o books.csv -t csv ## -t可以省略

配置文件:

选项含义示例
FEED_URI导出文件路径FEED_URI = ‘books.csv’
FEED_FORMAT导出数据格式FEED_FORMAT = ‘csv’
FEED_EXPORT_ENCODING导出文件编码方式FEED_EXPORT_ENCODING=’gbk’
FEED_EXPORT_FIELDS指定导出哪些字段并排序FEED_EXPORT_FIELDS={‘title’,’price’}
FEED_EXPORTERS用户自定义Exporter字典,一般用于添加新的导出数据格式FEED_EXPORTERS ={‘excel’:’项目名.新设置的py文件名.ExcelItemExporter’}
    原文作者:橄榄的世界
    原文地址: https://www.jianshu.com/p/0d06d38ca9d5
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞