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’} |