摘要:本文介绍了Scrapy的基础爬取流程,也是最重要的部分html
Scrapy的爬取流程能够归纳为一个方程式:UR2IM,其含义以下图所示python
URL:Scrapy的运行就从那个你想要爬取的网站地址开始,当你想要验证用xpath或其余解析器来解析这个网页时,可使用Scrapy shell工具来进行分析,譬如git
$ scrapy shell http://web:9312/properties/property_000000.html
如今你就能够开始验证了github
Request和Response:在上面使用Scrapy shell的过程当中能够发现,只要咱们输入了一个URL,它就能够自动发送一个GET请求并获取返回结果。request是一个把url封装好的对象,response则是一个把网页返回结果封装好的对象,response.body的值是网页的源代码,response.url是网页的url地址,还有更多相关的属性web
Items:咱们要爬取一个网页的时候并非只把源代码下载下来就完事了,还须要提取网页中的相关信息,譬如网页的标题,网页的发布时间等等内容,而这些内容使用面向对象的技术,封装成一个Item对象,而后从网页中提取信息来填充这个Itemshell
首先新建一个名为properties的Scrapy工程数据库
$ scrapy startproject properties $ cd properties $ tree . ├── properties │ ├── __init__.py │ ├── items.py │ ├── pipelines.py │ ├── settings.py │ └── spiders │ └── __init__.py └── scrapy.cfg 2 directories, 6 files
注意,本系列文章的源代码均可以从github上下载express
编辑items.py文件,在该文件中定义的item并非必定要在每个spider中填充,也不是要所有同时使用的,你能够随意添加字段,而且在任什么时候候填充。json
from scrapy.item import Item, Field class PropertiesItem(Item): # Primary fields title = Field() price = Field() description = Field() address = Field() image_urls = Field() # Calculated fields,这些字段须要运算后才获得,后面的文章会解析,暂时不用管 images = Field() location = Field() # Housekeeping fields,这些字段用来在调试时显示相关信息 url = Field() project = Field() spider = Field() server = Field() date = Field()
在项目的根目录下根据basic模板建立一个名为basic的spider,后面的web指的是spider的可运行的域名数组
scrapy genspider –t basic basic web
固然能够本身手写一个spider,可是从模板里建立能够省去很多的时间和减小出错机率,查看其余模板的命令:
scrapy genspider -l
由模板建立的basic.py文件的代码以下
# -*- coding: utf-8 -*- import scrapy class BasicSpider(scrapy.Spider): name = "basic" allowed_domains = ["web"] start_urls = ( 'http://www.web/', ) def parse(self, response): pass
把抓取到的网页存入item中(文件名:basic.py)
import scrapy from properties.items import PropertiesItem class BasicSpider(scrapy.Spider): name = "basic" allowed_domains = ["web"] start_urls = ( 'http://web:9312/properties/property_000000.html', ) def parse(self, response): item = PropertiesItem() item['title'] = response.xpath( '//*[@itemprop="name"][1]/text()').extract() item['price'] = response.xpath( '//*[@itemprop="price"][1]/text()').re('[.0-9]+') item['description'] = response.xpath( '//*[@itemprop="description"][1]/text()').extract() item['address'] = response.xpath( '//*[@itemtype="http://schema.org/' 'Place"][1]/text()').extract() item['image_urls'] = response.xpath( '//*[@itemprop="image"][1]/@src').extract() return item
启动爬虫后,看到控制台输出以下信息,说明爬取成功
$scrapy crawl basic DEBUG: Scraped from <200 http://...000.html> {'address': [u'Angel, London'], 'description': [u'website ... offered'], 'image_urls': [u'../images/i01.jpg'], 'price': [u'334.39'], 'title': [u'set unique family well']}
将上面的输出保持到各类文件中:
scrapy crawl basic –o item.json scrapy crawl basic –o item.jl #json格式的文件会把整个json对象保存在一个巨大的数组里,意味着若是你要保存的数据量有1GB,那么在你解析这些数据以前,就必须用1GB的内存来保存整个文件。而jl格式会在每一行上放一个json对象,因此读取起来效率会更高 scrapy crawl basic –o item.xml scrapy crawl basic –o item.csv scrapy crawl basic –o ftp://user:pass@ftp.scrapybook.com/items.json 直接保存到ftp上
对于上面混乱且难看的parse函数,可使用Item Loader来处理,而且Item Loader提供更多的功能(http://doc.scrapy.org/en/latest/topics/loaders.html),最重要的是可以对经过xpath提取出来的信息进行处理,譬如去掉空格和替换字符等,而后将清洗后的数据再写入item中。对数据的清洗是经过processor来实现的,很经常使用的一个processor就是MapCompose()函数,该函数将python函数或者lambda表达式做为参数(参数个数无限制),而后按顺序执行这些函数来产生最终的结果。譬如MapCompose(unicode.strip, float)首先将xpath提取的信息去掉空格,再将其转换为float格式
basic.py源代码文件:
修改上面的basic.py文件,使得代码更加简洁和一目了然
import datetime import urlparse import socket import scrapy from scrapy.loader.processors import MapCompose, Join from scrapy.loader import ItemLoader from properties.items import PropertiesItem class BasicSpider(scrapy.Spider): name = "basic" allowed_domains = ["web"] # Start on a property page start_urls = ( 'http://web:9312/properties/property_000000.html', ) def parse(self, response): """ This function parses a property page. @url http://web:9312/properties/property_000000.html @returns items 1 @scrapes title price description address image_urls @scrapes url project spider server date """ # Create the loader using the response l = ItemLoader(item=PropertiesItem(), response=response) # Load fields using XPath expressions l.add_xpath('title', '//*[@itemprop="name"][1]/text()', MapCompose(unicode.strip, unicode.title)) l.add_xpath('price', './/*[@itemprop="price"][1]/text()', MapCompose(lambda i: i.replace(',', ''), float), re='[,.0-9]+') l.add_xpath('description', '//*[@itemprop="description"][1]/text()', MapCompose(unicode.strip), Join()) l.add_xpath('address', '//*[@itemtype="http://schema.org/Place"][1]/text()', MapCompose(unicode.strip)) l.add_xpath('image_urls', '//*[@itemprop="image"][1]/@src', MapCompose(lambda i: urlparse.urljoin(response.url, i))) # Housekeeping fields 能够经过add_value函数直接向item填充数据 l.add_value('url', response.url) l.add_value('project', self.settings.get('BOT_NAME')) l.add_value('spider', self.name) l.add_value('server', socket.gethostname()) l.add_value('date', datetime.datetime.now()) return l.load_item()
注意在上面的parse函数中有这样的一段注释
""" This function parses a property page. @url http://web:9312/properties/property_000000.html @returns items 1 @scrapes title price description address image_urls @scrapes url project spider server date """
这些以@开头的称为contract,相似于单元测试,假如你在一个月前写了这个spider,如今想测试这个spider是否仍然正确运行,就可使用这些contract。上面的这些contract的意思是:检查该url,你应该获得一个包含了下面列出来的字段的item
运行scrapy check来检查contract
$ scrapy check basic ---------------------------------------------------------------- Ran 3 contracts in 1.640s OK
若是spider的代码出错了,或者xpath表达式已通过期了(网站发生了更新),那么就会获得测试失败的结果,虽然出错信息并不详尽,但这是最快的检查手段
在上面用模板定义的spider十一个用来爬取特定网页的类,包括了如何执行爬取动做(譬如如何跟踪超连接)和如何从页面中提取信息。换句话说,spider就是你用来定义对某个特定网站的爬取动做的工具,他的爬取循环相似于这样:
一、 首先要将你指定的初始URL封装成Request对象,而且要指定在网页返回该请求的内容后应该用哪一个函数来处理网页的内容。
默认状况下,会调用start_requests()函数,对start_urls中的URL分别生成一个Request对象,而且指定parse()函数做为回调函数(回调函数指的是callback变量指定的函数)
二、 在回调函数中,能够处理response变量,而后返回一个已经提取好数据的字典或者是一个Item对象,或者是Request对象(在这个Request对象中,也能够指定一个回调函数,一样地,处理完这个Request以后生成的response就会传送到回调函数中处理)
三、 在回调函数中,也能够提取网页内容,一般使用Selector(也可使用BeautifulSoup,lxml或者其余你熟悉的机制)来生成包含了解析数据的item
四、 最后,这些从spider中返回的item一般会存入到数据库中,或者写入到文件中
即便上述流程适用于大部分的spider,可是仍然有不一样的spider运行不一样的默认流程,更多的信息就查阅这里: