Spider设置:
在该目录运行下面命令
E:\py\Quotetutorial\Quotetutorial>scrapy3 genspider zhihu www.zhihu.com 生成zhihu.py,并且域名为www.zhihu.com
E:\py\Quotetutorial\Quotetutorial>scrapy3 crawl zhihu 运行zhihu.py
start_requests
(
)
该方法必须返回一个可迭代对象(iterable)。该对象包含了spider用于爬取的第一个Request。
当spider启动爬取并且未制定URL时,该方法被调用。 当指定了URL时,make_requests_from_url()
将被调用来创建Request对象。 该方法仅仅会被Scrapy调用一次,因此您可以将其实现为生成器。
该方法的默认实现是使用 start_urls
的url生成Request。
如果您想要修改最初爬取某个网站的Request对象,您可以重写(override)该方法。 例如,如果您需要在启动时以POST登录某个网站,你可以这么写:
def start_requests(self):
return [scrapy.FormRequest("http://www.example.com/login",
formdata={'user': 'john', 'pass': 'secret'},
callback=self.logged_in)]
def logged_in(self, response):
# here you would extract links to follow and return Requests for
# each of them, with another callback
pass
make_requests_from_url
(
url
)
该方法接受一个URL并返回用于爬取的 Request
对象。 该方法在初始化request时被 start_requests()
调用,也被用于转化url为request。
默认未被复写(overridden)的情况下,该方法返回的Request对象中, parse()
作为回调函数,dont_filter参数也被设置为开启。 (详情参见 Request
).
Spiders
Spider类定义了如何爬取某个(或某些)网站。包括了爬取的动作(例如:是否跟进链接)以及如何从网页的内容中提取结构化数据(爬取item)。 换句话说,Spider就是您定义爬取的动作及分析某个网页(或者是有些网页)的地方。
对spider来说,爬取的循环类似下文:
以初始的URL初始化Request,并设置回调函数。 当该request下载完毕并返回时,将生成response,并作为参数传给该回调函数。
spider中初始的request是通过调用
start_requests()
来获取的。start_requests()
读取start_urls
中的URL, 并以parse
为回调函数生成Request
。在回调函数内分析返回的(网页)内容,返回
Item
对象或者Request
或者一个包括二者的可迭代容器。 返回的Request对象之后会经过Scrapy处理,下载相应的内容,并调用设置的callback函数(函数可相同)。在回调函数内,您可以使用 选择器(Selectors) (您也可以使用BeautifulSoup, lxml 或者您想用的任何解析器) 来分析网页内容,并根据分析的数据生成item。
最后,由spider返回的item将被存到数据库(由某些 Item Pipeline 处理)或使用 Feed exports 存入到文件中。
虽然该循环对任何类型的spider都(多少)适用,但Scrapy仍然为了不同的需求提供了多种默认spider。 之后将讨论这些spider。
Spider参数
Spider可以通过接受参数来修改其功能。 spider参数一般用来定义初始URL或者指定限制爬取网站的部分。 您也可以使用其来配置spider的任何功能。
在运行 crawl
时添加 -a
可以传递Spider参数:
scrapy crawl myspider -a category=electronics
Spider在构造器(constructor)中获取参数:
import scrapy
class MySpider(Spider):
name = 'myspider'
def __init__(self, category=None, *args, **kwargs):
super(MySpider, self).__init__(*args, **kwargs)
self.start_urls = ['http://www.example.com/categories/%s' % category]
# ...
Spider参数也可以通过Scrapyd的 schedule.json
API来传递。 参见 Scrapyd documentation.
编写你自己的item pipeline
编写你自己的item pipeline很简单,每个item pipeline组件是一个独立的Python类,同时必须实现以下方法:
-
process_item
( self, item, spider ) -
每个item pipeline组件都需要调用该方法,这个方法必须返回一个
Item
(或任何继承类)对象, 或是抛出DropItem
异常,被丢弃的item将不会被之后的pipeline组件所处理。参数:
此外,他们也可以实现以下方法:
-
open_spider
( self, spider ) -
当spider被开启时,这个方法被调用。
参数: spider ( Spider
对象) – 被开启的spider
-
close_spider
( spider ) -
当spider被关闭时,这个方法被调用
参数: spider ( Spider
对象) – 被关闭的spider
-
from_crawler
( cls, crawler ) -
If present, this classmethod is called to create a pipeline instance from a
Crawler
. It must return a new instance of the pipeline. Crawler object provides access to all Scrapy core components like settings and signals; it is a way for pipeline to access them and hook its functionality into Scrapy.参数: crawler ( Crawler
object) – crawler that uses this pipeline
Item pipeline 样例
验证价格,同时丢弃没有价格的item
让我们来看一下以下这个假设的pipeline,它为那些不含税(price_excludes_vat
属性)的item调整了 price
属性,同时丢弃了那些没有价格的item:
from scrapy.exceptions import DropItem
class PricePipeline(object):
vat_factor = 1.15
def process_item(self, item, spider):
if item['price']:
if item['price_excludes_vat']:
item['price'] = item['price'] * self.vat_factor
return item
else:
raise DropItem("Missing price in %s" % item)
将item写入JSON文件
以下pipeline将所有(从所有spider中)爬取到的item,存储到一个独立地 items.jl
文件,每行包含一个序列化为JSON格式的item:
import json
class JsonWriterPipeline(object):
def __init__(self):
self.file = open('items.jl', 'wb')
def process_item(self, item, spider):
line = json.dumps(dict(item)) + "\n"
self.file.write(line)
return item
注解
JsonWriterPipeline的目的只是为了介绍怎样编写item pipeline,如果你想要将所有爬取的item都保存到同一个JSON文件, 你需要使用 Feed exports 。
Write items to MongoDB
In this example we’ll write items to MongoDB using pymongo. MongoDB address and database name are specified in Scrapy settings; MongoDB collection is named after item class.
The main point of this example is to show how to use from_crawler()
method and how to clean up the resources properly.
注解
Previous example (JsonWriterPipeline) doesn’t clean up resources properly. Fixing it is left as an exercise for the reader.
import pymongo
class MongoPipeline(object):
def __init__(self, mongo_uri, mongo_db):
self.mongo_uri = mongo_uri
self.mongo_db = mongo_db
@classmethod
def from_crawler(cls, crawler):
return cls(
mongo_uri=crawler.settings.get('MONGO_URI'),
mongo_db=crawler.settings.get('MONGO_DATABASE', 'items')
)
def open_spider(self, spider):
self.client = pymongo.MongoClient(self.mongo_uri)
self.db = self.client[self.mongo_db]
def close_spider(self, spider):
self.client.close()
def process_item(self, item, spider):
collection_name = item.__class__.__name__
self.db[collection_name].insert(dict(item))
return item
去重
一个用于去重的过滤器,丢弃那些已经被处理过的item。让我们假设我们的item有一个唯一的id,但是我们spider返回的多个item中包含有相同的id:
from scrapy.exceptions import DropItem
class DuplicatesPipeline(object):
def __init__(self):
self.ids_seen = set()
def process_item(self, item, spider):
if item['id'] in self.ids_seen:
raise DropItem("Duplicate item found: %s" % item)
else:
self.ids_seen.add(item['id'])
return item
启用一个Item Pipeline组件
为了启用一个Item Pipeline组件,你必须将它的类添加到 ITEM_PIPELINES
配置,就像下面这个例子:
ITEM_PIPELINES = {
'myproject.pipelines.PricePipeline': 300,
'myproject.pipelines.JsonWriterPipeline': 800,
}
分配给每个类的整型值,确定了他们运行的顺序,item按数字从低到高的顺序,通过pipeline,通常将这些数字定义在0-1000范围内。
下载器中间件(Downloader Middleware)
下载器中间件是介于Scrapy的request/response处理的钩子框架。 是用于全局修改Scrapy request和response的一个轻量、底层的系统。
编写您自己的下载器中间件
编写下载器中间件十分简单。每个中间件组件是一个定义了以下一个或多个方法的Python类:
-
class
scrapy.contrib.downloadermiddleware.
DownloaderMiddleware
-
-
process_request
( request, spider ) -
当每个request通过下载中间件时,该方法被调用。
process_request()
必须返回其中之一: 返回None
、返回一个Response
对象、返回一个Request
对象或raiseIgnoreRequest
。如果其返回
None
,Scrapy将继续处理该request,执行其他的中间件的相应方法,直到合适的下载器处理函数(download handler)被调用, 该request被执行(其response被下载)。如果其返回
Response
对象,Scrapy将不会调用 任何 其他的process_request()
或process_exception()
方法,或相应地下载函数; 其将返回该response。 已安装的中间件的process_response()
方法则会在每个response返回时被调用。如果其返回
Request
对象,Scrapy则停止调用 process_request方法并重新调度返回的request。当新返回的request被执行后, 相应地中间件链将会根据下载的response被调用。如果其raise一个
IgnoreRequest
异常,则安装的下载中间件的process_exception()
方法会被调用。如果没有任何一个方法处理该异常, 则request的errback(Request.errback
)方法会被调用。如果没有代码处理抛出的异常, 则该异常被忽略且不记录(不同于其他异常那样)。参数:
-
process_response
( request, response, spider ) -
process_request()
必须返回以下之一: 返回一个Response
对象、 返回一个Request
对象或raise一个IgnoreRequest
异常。如果其返回一个
Response
(可以与传入的response相同,也可以是全新的对象), 该response会被在链中的其他中间件的process_response()
方法处理。如果其返回一个
Request
对象,则中间件链停止, 返回的request会被重新调度下载。处理类似于process_request()
返回request所做的那样。如果其抛出一个
IgnoreRequest
异常,则调用request的errback(Request.errback
)。 如果没有代码处理抛出的异常,则该异常被忽略且不记录(不同于其他异常那样)。参数:
-
process_exception
( request, exception, spider ) -
当下载处理器(download handler)或
process_request()
(下载中间件)抛出异常(包括IgnoreRequest
异常)时, Scrapy调用process_exception()
。process_exception()
应该返回以下之一: 返回None
、 一个Response
对象、或者一个Request
对象。如果其返回
None
,Scrapy将会继续处理该异常,接着调用已安装的其他中间件的process_exception()
方法,直到所有中间件都被调用完毕,则调用默认的异常处理。如果其返回一个
Response
对象,则已安装的中间件链的process_response()
方法被调用。Scrapy将不会调用任何其他中间件的process_exception()
方法。如果其返回一个
Request
对象, 则返回的request将会被重新调用下载。这将停止中间件的process_exception()
方法执行,就如返回一个response的那样。参数:
-
参考:
http://scrapy-chs.readthedocs.io/zh_CN/latest/topics/spiders.html