Scrapy 1.5.0发布日期是2017.12.29
书写一个自己的教程
1. Scrapy是什么
1.1 Scrapy的定义
Scrapy是一个用来爬取网页并抽取结构化数据的应用框架,从而实现数据挖掘、信息处理或历史备份。
个人总结:Scrapy最坑的地方就是xpath()解析,没有之一
1.2 Scrapy的框架
Scrapy包括五大组件,Engine,Scheduler,Downloader,Spider,Item Pipeline
1. Engine 引擎
2. Scheduler 调度器
3. Downloader 下载器
4. Spider 爬虫
5. Item Pipeline 管道
1.3 Scrapy的项目结构
待补充
1.4 Scrapy的工作原理
待补充
1.5 临时存放点
当你运行命令scrapy runspider quotes_spider.py
,scrapy会通过引擎来运行这个文件,首先将start_urls
属性中定义的URLs包装成requests,然后调用默认的回调方法parse()
,并将response对象作为参数传递给它。在parse
回调中,先通过使用CSS选择器来循环遍历quote元素,生成Python字典(包含结构化的quote text和author),并寻找下一页的链接,然后调度另一个请求同样使用parse来作为回调。
scrapy的一个优点就是:异步地调度和处理。这就意味着Scrapy不需要等待一个request完成处理,它可以同时发送另一个请求或者做别的事情。这也意味着如果一些request失败了或者在处理的时候发生了异常,其他的requests可以继续执行。
2. Item类
写一个Scrapy爬虫项目的第一步就是定义好Item类。Item类相当于javabean,它定义了爬取的数据的类型和所拥有的属性。所以在写Scrapy的时候首先要明确爬取的结果是什么,明确需求。Scrapy要求我们把它定义在Item中,爬取数据对象的每一个属性都通过Scrapy.Field()来定义。
import scrapy
class MyscrapyItem(scrapy.Item): # 自定义的Item类必须继承Scrapy.Item
student_id = scrapy.Field()
student_name = scrapy.Field()
3. Spider类
在Scrapy框架中,Engine是核心,但是它的任务已经由Scrapy框架完成了。在实际项目中,我们的核心是怎么实现Spider类。
Spider类对应于五大组件中的爬虫,它需要指明三个属性和一个方法:
属性/方法 | 解释 |
---|---|
name | 指明当前爬虫的名字,通过命令行运行爬虫时可以直接使用名字 |
allowed_domain | (可省略)指明爬虫活动的域名范围,如果爬取的时候超出了范围,则停止继续深入 |
start_urls | 以列表的形式定义初始的URLs,它其实就已经充当了Scheduler的URL初步队列了 |
parse(response) | 解析网页并将需要保存的数据包装成Item对象给Pipeline,或者解析出下一个需要爬取的网页的url给调度器 |
3.1 定义name和allowed_domain
通过name属性来定义爬虫名字,通过allowed_domain属性来定义爬虫爬取的域名范围
3.2 定义初始url
(1) 通过start_urls属性来定义初始url列表
start_urls = ['http://quotes.toscrape.com/tag/humor/',]
(2) 通过start_requests(self)方法来生成初始url
scrapy调度爬虫类中start_requests
方法返回的scrapy.Request
对象,每当收到一个request的response,scrapy就会实例化Response对象并调用回调方法
def start_requests(self): # 必须返回一个Requests的迭代器(返回一个列表或者一个生成器函数)
urls = [
'http://quotes.toscrape.com/page/1/',
'http://quotes.toscrape.com/page/2/',
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse)
# 简便写法
# yield scrapy.Request(url, self.parse)
3.3 parse()
parse()有两个任务,解析数据给pipelines用于存储,解析下一个要爬取的网页的url生成request给Scheduler调度。
Engine从Downloader获取得到的每一个response都是交给parse()来处理的,这个是默认设置好的。爬取页面解析出的新request可以指定自定义parse()方法来解析。详见本节任务二。
展示一个简单的例子:
import scarpy # 导入scrapy包
class QuotesSpider(scrapy.Spider): # 定义一个爬虫类,必须继承于scrapy中的Spider类
name = 'quotes' # 给爬虫类取个名字,用于区分其他的爬虫类
start_urls = ['http://quotes.toscrape.com/tag/humor/',] # 给爬虫类定义一个初始的url列表
def parse(self, response):
for quote in response.css('div.quote'):
yield { # 生成一个字典并返回
'text': quote.css('span.text::text').extract_first()
'author': quote.xpath('span/small/text()').extract_first(),
}
next_page = response.css('li.next a::attr("href")').extract_first()
if next_page is not None:
yield response.follow(next_page, self.parse)
任务一:解析页面数据——xpath()解析和css()解析
两种解析方式返回值的类型是SelectorList(Selector对象组成的列表)
# 两个例子展示css解析
response.css('title::text').extract_first()
response.css('title::text')[0].extract()
response.css('title::text').re(r'Q\w+')
response.xpath('//title/text()').extract_first()
title::text表示我们只取title标签的text属性,否则就直接获取整个标签,为[Quotes to Scrape]
re()和extract()都可以实现转码的作用
任务二:生成下一个request
next_page = response.css('li.next a::attr(href)').extract_first()
if next_page is not None:
# urljoin()将当前response.url和next_page拼接在一起
next_page = response.urljoin(next_page)
yield scrapy.Request(next_page, callback=self.parse)
next_page是下一个页面的Url,Request()将这个url封装成request交给Scheduler,等request发送完毕后,获取的response将按照call_back定义的parse()方法来执行解析。
上述方法的简便方法:
next_page = response.css('li.next a::attr(href)').extract_first()
if next_page is not None:
yield scrapy.follow(next_page, call_back=self.parse)
这里follow相当于采用相对路径,直接当前response.url和next_page作拼接,便省去了urljoin()
自定义parse()
class AuthorSpider(scrapy.Spider):
name = 'author'
start_urls = ['http://quotes.toscrape.com/']
# 解析下一页
def parse(self, response):
# 将call_back设计成位置参数可以减少代码长度,对scrapy.Request()同样适用
for href in response.css('.author + a::attr(href)'):
yield response.follow(href, self.parse_author)
for href in response.css('li.next a::attr(href)'):
yield response.follow(href, self.parse)
# 解析作者信息,并将作者信息传递给pipeline
def parse_author(self, response):
def extract_with_css(query):
return response.css(query).extract_first().strip()
yield {
'name': extract_with_css('h3.author-title::text'),
'brithdate': extract_with_css('h3.author-born-date::text'),
'bio': extract_with_css('h3.author-description::text'),
}
3.4 爬虫参数
设置爬虫参数,就可以在通过命令运行爬虫的时候,传递参数了。
class QuotesSpider(scrapy.Spider):
# 其余部分省略
def start_requests(self):
url = 'http://quotes.toscrape.com/'
tag = getattr(self, 'tag', self.parse)
if tag is not None:
url = url + 'tag/' + tag
yield scrapy.Request(url, self.parse)
运行爬虫时 scrapy crawl quotes -o quotes-humor.json -a tag=humor
,这时爬虫就只会访问 http://quotes.toscrape.com/tag/humor 了
4. Pipelines类
Pipelines是Scrapy中专门负责数据存储的部分,如果存储操作很简单,在spider中就可以直接存储,甚至都无须使用Item类。
Pipelines使用前注意:要在settings里面将Pipelines的注释反注释一下(后面的那个数字代表优先级,仅有一个Pipeline的时候不用管它是多少)
4.1 init(self) = start_spider(self, spider)
添加在爬虫执行前的一些存储准备工作,比如打开文件
def __init__(self):
self.file = open(filename, 'w', encoding='utf8')
4.2 process_item(self, item, spider)
定义收到item后如何处理
def process_item(self, item, spider):
# ... 存储的方式
return item # 返回给engine告诉它存储完毕,然后继续调用parse()
4.3 close_spider(self, spider)
定义爬虫结束的收尾工作,比如关闭文件
def close_spider(self, spider)(self):
self.file.close()
5. 命令行工具
命令包括scrapy全局命令和project命令,使用格式为:
scrapy [option] [args]
还可以通过在命令后面加-h来查看该命令的使用帮助
scrapy -h
5.1 scrapy全局命令
bench 测试
scrapy bench
测试当前机器的性能
fetch 获取response
用法:通过Downloader获取的response来测试url是否正确
scrapy fetch [option]
Options:
-spider=SPIDER
使用SPIDER爬虫类
genspider 创建爬虫
scrapy genspider [options]
Options:
–list, -l 列表显示可用的模板
–template=TEMPLATE, -t TEMPLATE 使用某个模板(basic, crawl, csvfeed, xmlfeed)
runspider 运行爬虫
在没有创建project的情况下指定一个爬虫文件并运行
scrapy runspider [options]
Options:
–output=FILE, -o FILE 指定输出文件
举例:scrapy runspider quotes_spider.py -o quotes.json
settings 设置
查看某个设置的值
scrapy settings [options]
shell
可以用来测试xpath()
scrapy shell [url|file]
注意:要设置好header,(windows下)url必须用双引号括起来
version 查看版本
scrapy version
view
垃圾功能,不用
5.2 project命令
crawl
运行一个spider
scrapy crawl [options]
Options:
--output=FILE, -o FILE
定义输出文件