关于名字,Scrapy = S + crawl +py ???.
基于Python3.x的scrapy使用,首先配置virtualenv配置python虚拟环境,详见.
在虚拟环境中安装scrapy,使用国内豆瓣源安装:
pip install -i https://pypi.douban.com/simple scrapy
如果安装或编译出错,则使用这里已经编译好的包进行安装,同样使用pip命令安装本地文件。
安装完成之后,使用scrapy startproject jobbole命令创建jobbole项目,目录结构中spiders包下存放的是初始化生成的爬虫模板文件。
简单的来说,Scrapy由以下几部分组成:
(1)任务调度器Scheduler,调度多个异步请求任务。
(2)下载器downloader,提供专用的图片、文件下载器。
(3)数据后处理其ItemPipline.
scrapy标准的爬虫文件中有一个scrapy.Spider子类,与爬虫相关的类和方法包括Request,Response,parse,Item,ItemLoader,settings,pipeline。一个完整的爬虫流程如下:
from url import parse
final_url = parse.urljoin(response.url, url)
// 发送新的请求
yield Request(url=final, callback=self.parse_detail, meta={})
url可以通过parse可以处理那些无域名的相对路径
callback制定另一个类成员函数进行处理
meta可以将任意数据通过dict的形式传入response中,向下传递
通过yield Request可发送新的请求
自定义解析函数时,函数的参数签名为(self, response)
可以通过response.xpath() 、response.css()和response.re()手动获取页面内容。推荐的做法是使用scrapy提供的ItemLoader,ItemLoader会自动解析提取的值,而不用手动extrac()[0]。
与ItemLoader配合的是Item,Item定义时可以设置input_processor和output_processor。Item的定义只是通过Filed()进行声明即可。
// 预置processor的种类
from scrapy.loader.processors import Join, MapCompose, Compose,TakeFirst, Identity, MergeDict, SelectJmes
class JobboleItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
title = scrapy.Field(output_processor = TakeFirst()) # TakeFirst() 返回取第一个值的函数
post_time = scrapy.Field(output_processor = Compose(TakeFirst(), lambda x: x.strip([''])))
tags = scrapy.Field()
source_url = scrapy.Field(output_processor=TakeFirst())
upvote_count = scrapy.Field(output_processor=TakeFirst())
最后在爬虫类中通过yield item将item交给ItemPipeline处理,且在item传入pipeline做进一步处理之前,会读取settings中的设置。
(1)spider主要实现爬去任务的发送、接收response进行解析处理。主要集成scrapy.Spider去实现一些方法。
from scapy import Spider
from url import parse
from MyPackage import MyItem
class MySpider(Spider):
name = "myspider"
allow_domains = ['www.xxoo.com']
start_urls = ['www.xxoo.com']
def parse(self, response):
value = response.xpath('//div/text()').extract()[0]
next_url = response.xpath('//a[1]/text').extract()
yield Request(url=parse.urljoin(response.url, next_url),callback=parse_detail, meta={extradata:'xxx'})
def parse_detail(self, response):
value = response.meta.extradata; // 获取额外信息
value2 = reponse.xpath('//div//text').extract()[0]
// 不直接通过xpath的方式进行解析,而是通过ItemLoader自动解析
item_loader = ItemLoader(MyItem(), response=response);
item_loader.add_xpath('id', '//div')
item_loader.add_css('name', '.selector')
item_loader.add_value('age', response.url)
final_item = item_loader.load_item()
yield final_item
(2) Item是数据容器,类似于dict但不是dict类型(可通过dict(item)的形式转换为字典类型).Item的定义只需要通过scrapy.Field定义即可,必要时可以在Field定义是添加input_processor=,或者output_processor=,接收函数,用于处理传入字段的值,配合ItemLoader使用
from scrapy import Field,Item
// 内置的processor,用于配合ItemLoader
from scrapy.loader.processors import Join, MapCompose, Compose,TakeFirst, Identity, MergeDict, SelectJmes
def stripA(value):
return value.strip('A')
class MyItem(Item):
id = Field(output_processor=stripA)
name = Field(input_processor=TakeFirst())
age = Field(input_processor=TakeFirst(),
output_processor=Compose(stripA, int))
# Compose用于将函数组合起来,item中的值被compose中的函数逐一执行。
# TakeFirst()生成取首元素的函数
(3)ItemLoader用于处理response解析出的值,ItemLoader加载出来的值传入Item,会执行对应Field的output_processor/input_processor。实际上,在ItemLoader中也可以通过加入processor来对item值做处理,通常的做法是input_processor写在ItemLoader中,output_processor写在Item的Field中。
# 继承ItemLoader,重写部分方法
from scrapy.Loader import ItemLoader
class MyItemLoader(ItemLoader):
input_processor = TakeFirst()
# 通过以上的设置,在解析所有的Item Field前都会调用TakeFirst()
(4)Pipeline是数据最终被加工的场所,可以在Pipeline中完成数据的导出、数据库存储、图片/文件自动下载,Pipeline执行前会读取setting中相关设置。可以定义普通对象的Pipeline,也可以通过重写已有的Pipeline完成特定的功能。Pipeline有特定的生命周期,通过定义生命周期函数来进行处理。
class MyPipeline(object):
def from_settings(cls, settings):
# 通过settings获取settings.py中的设置
db = DbSettings.DbSettings
return cls(db) # 生成对象之前执行,对象的初始化参数为url
def __init__(self, db):
self.db = db
self.conn = pymysql.connect(db)
self.cursor = self.conn.cursor()
def open_spider(self,spider):
pass
def process_item(self, item, spider):
# 核心处理函数
pass
def close_spider(self, spider):
self.conn.commit()
self.cursor.close()
self.conn.close()
除了自定义的对象,scrapy提供了一系列的exporter将item导出成csv、json、xml等格式的文件,详见
from scrapy.exporters import JsonItemExporter
# 通过exporer可以导出文件,在pipeline中使用
除了手动编写自己的Pipeline进行处理,scrapy提供了一些列特殊功能的Pipeline,例如自动下载图片的ImagesPipeline和自动下载文件的FilePipeline。可以直接配置settings使用这些pipeline,也可以重写这些pipeline实现自己的需求
from scrapy.pipelines.images import ImagesPipeline
class DeriveImagesPipeline(ImagesPipeline):
def item_completed(self, results, item, info):
for status, value in results:
item['face_url_path'] = value["path"]
return super(DeriveImagesPipeline, self).item_completed(results, item, info) # python中调用父类方法的方式
# 在自定义Pipeline中只需要关注两个方法:
# get_media_requests(self, item, info),返回一个Request对象
# item_completed(self, results, item, info),当上门的Request下载完成后回调这个方法,然后填充files或images字段
(5)settings.py文件
Pipeline的使用必须在settings.py文件中配置.
# 可以配置多个pipeline,数字越小表示执行的优先级越高.
ITEM_PIPELINES = {
'jobbole.pipelines.JobbolePipeline': 300,
#'jobbole.pipelines.JsonExporterPipline': 200,
#'jobbole.pipelines.MyJsonPipeline': 200,
#'jobbole.pipelines.DeriveImagesPipeline': 1
#'jobbole.pipelines.MysqlPoolPipeline': 200
#'jobbole.pipelines.MySqlDbPipeline': 200
}
settings中有一些特殊的字段,有特殊的用途,例如设定
# 设定Item中face_url字段自动下载图片
# 这里需要注意的是图片下载的字段必须是一个列表形式,否则会报错
# 原因是ImagesPipeline中会对指定images_url_field的字段进行列表遍历,然后逐个下载.
IMAGES_URLS_FIELD = 'face_url'
# 设定下载图片的本地保存路径
IMAGES_STORE = save_path
# 设置文件下载的路径
file_urls =
# 更多的字段设置,可以打开ImagesPipeline源码,查看哪些字段被读取。