这一次了将重新回顾spider文件
scrapy spider工作流程:
1以初始的URL初始化Request,设置回调函数。response,并作为参数传给该回调函数。
初始的request是通过调用 start_requests() 来获取的。读取 start_urls 中的URL, 并以 parse 为回调函数生成 Request
一般情况下start_requests()方法是不需要重写的,除非要对start_url进行处理:诸如循环产生批量的初始的接口,或者初次请求为post请求,否则,在命令行运行项目底端是默认走这个方法的
2在回调函数内解析返回的(网页)内容:返回 Item 对象、dict、 Request 或者一个包括三者的可迭代容器。
下载之后,并调用设置的callback函数(向callback指定的函数传递,也可以用mate指定一个数据字典来传递)
3回调函数,使用 选择器(Selectors) (您也可以使用BeautifulSoup, lxml)
来解析网页内容,并根据分析的数据生成item
4由spider返回的item将被存到数据库(由 Item Pipeline 处理)或使用 Feed exports 存入到文件中。
spider的类及其属性和方法:
class scrapy.spiders.Spider
每个spider都是继承这个类其仅仅请求给定的 start_urls/start_requests ,
并根据返回的结果(resulting responses)调用spider的 parse
name#爬虫名称,str,必须
allowed_domains#默认跟踪的url必须在这个域中,下面这个方法解决长因此的问题
禁用OffsiteMiddleware的时候可以使不在allowed_domains的URL也可以跟进
start_urls#url列表
custom_settings#dict,覆盖settings文件中的设置,上篇文章已经写了,[文章开始已介绍](https://blog.csdn.net/wc199422/article/details/82432752)
crawler#这个参数建议看后面的
初始化class后,由类方法 from_crawler() 设置, 并且链接了本spider实例对应的 Crawler 对象
settings#上面你这个链接中也有用法
logger#设置相关log配置的信息,会有专门的介绍
from_crawler(crawler, *args, **kwargs)#在类的底层默认初始化了
crawler (Crawler instance)#将绑定到爬虫的属性
args (list)
kwargs (dict)
start_requests()#
未指定URL启用,使用start_url中url跑默认一次,可定制初次发送的请求比如post,
指定URL时默认make_request_from_url()被调用创建request对象(一次性创建)
def start_requests(self):##标准写法
return [scrapy.FormRequest("http://www.example.com/login",formdata={'user': 'john', 'pass': 'secret'},callback=self.logged_in)]
make_requests_from_url(url)#有start_request()就覆盖了他
接收URL返回request对象其中包含parse()作为回调函数dont_filter自动过滤属性默认开启,默认被start_requests()调用
parse(response)#不指定回调函数,默认被指定,负责处理response并返回处理的数据以及(/或)跟进的URL,必须返回一个包含 Request 及(或) Item 的可迭代的对象
log(message[, level, component])#这个可以在setting中设置,同时他是封装了spider的logger属性实现的
使用 scrapy.log.msg() 方法记录(log)message记录logging
closed(reason)#这个方法最好写一写,不然在数据量大的可能会出现资源占用的问题
提供了一个替代调用signals.connect()来监听 spider_closed 信号的快捷方式。关闭爬虫
样例:代码写的啰嗦,这里展示了使用start_request()方法覆盖start_url
import scrapy
from myproject.items import MyItem
class MySpider(scrapy.Spider):
name = 'example.com'
allowed_domains = ['example.com']
def start_requests(self):
yield scrapy.Request('http://www.example.com/1.html', self.parse)
yield scrapy.Request('http://www.example.com/2.html', self.parse)
yield scrapy.Request('http://www.example.com/3.html', self.parse)
def parse(self, response):
for h3 in response.xpath('//h3').extract():
yield MyItem(title=h3)
for url in response.xpath('//a/@href').extract():
yield scrapy.Request(url, callback=self.parse)
scrapy也支持使用-a的方式在命令行上去为爬虫添加属性:
scrapy crawl myspider -a category=electronics
并在底层源码中去构造:
import scrapy
class MySpider(scrapy.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]
scrapy同时也提供了几种常用的爬虫类:#文档中那一段下面都有不用看
CrawlSpider#我通常给他的别名叫深度爬虫类,必且下面两种导包在0.24与1.0文档中有点小区别:
##实测都有效,跳进源码,貌似contrib中是爬虫初始化但没有东西,因该属于兼容,影响不大,还是使用最新的吧
0.24: class scrapy.contrib.spiders.CrawlSpider
1.0:class scrapy.spiders.CrawlSpider
class scrapy.spiders.CrawlSpider#定义了一些规则(rule)来提供跟进link的方便的机制
rules#多个rule规则匹配了相同的链接,则根据他们在本属性中被定义的顺序,第一个会被使用。按顺序匹配
parse_start_url(response)#该方法分析最初的返回值并必须返回一个 Item 对象或者 一个 Request 对象或者 一个可迭代的包含二者对象。类似parse
爬取规则(Crawling rules)
class scrapy.spiders.Rule(link_extractor, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None)
link_extractor#定义了如何从爬取到的页面提取链接
callback#每从link_extractor取出一条该函数被调用,此时回调函数命名应避开parse,应为crawlspider使用parse实现的
cb_kwargs 包含传递给回调函数的参数(keyword argument)的字典。#类似mate
follow 如果callback为None, follow 默认设置为 True ,否则默认为 False
process_links#功能类似于dont_filter参数#是一个callable或string(该spider中同名的函数将会被调用)。 从link_extractor中获取到链接列表时将会调用该函数。该方法主要用来过滤url
process_request 是一个callable或string(该spider中同名的函数将会被调用)。 该规则提取到每个request时都会调用该函数。该函数必须返回一个request或者None。 (用来过滤request)
样例##process_links和process_request想改的话是要复写的
import scrapy
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
class MySpider(CrawlSpider):
name = 'example.com'
allowed_domains = ['example.com']
start_urls = ['http://www.example.com']
rules = (
# 提取匹配 'category.php' (但不匹配 'subsection.php') 的链接并跟进链接(没有callback意味着follow默认为True)
Rule(LinkExtractor(allow=('category\.php', ), deny=('subsection\.php', ))),
# 提取匹配 'item.php' 的链接并使用spider的parse_item方法进行分析
Rule(LinkExtractor(allow=('item\.php', )), callback='parse_item'),
)
def parse_item(self, response):
self.logger.info('Hi, this is an item page! %s', response.url)
item = scrapy.Item()
item['id'] = response.xpath('//td[@id="item_id"]/text()').re(r'ID: (\d+)')
item['name'] = response.xpath('//td[@id="item_name"]/text()').extract()
item['description'] = response.xpath('//td[@id="item_description"]/text()').extract()
return item
XMLFeedSpider#xml反馈的爬虫
class scrapy.spiders.XMLFeedSpider
三个可供选择的迭代器:iternodes,html,xml
xml,html需先读取所有的dom在分析(性能问题),html能有效的应对错误的xml
iterator:迭代器选择#属性,默认为iternodes,(string)
iternodes:基于正则
html:dom全文解析
xml:dom全文解析
itertag:包含开始迭代的节点名的string#itertag = 'product'
namepaces;由(prefix, url)组成的list,定义会被spider处理的命名的空间namespace(会被register_namespace()调用生成)可以通过itertag指定namespances
class YourSpider(XMLFeedSpider):##实际上类似与start_url,只是这里是itertag中的参数是为命名空间例的内容指定使用用处
namespaces = [('n', 'http://www.sitemaps.org/schemas/sitemap/0.9')]
itertag = 'n:url'
adapt_response(response)
可以在response被分析之前使用该函数来修改内容(body)。 该方法接受一个response并返回一个response(可以相同也可以不同)
parse_node(response, selector)#解析通过命名空间限定请求的url返回的response
当节点符合提供的标签名时(itertag)该方法被调用。 接收到的response以及相应的 Selector 作为参数传递给该方法,#有点类似parse,参数类多一个处理方法selector
process_results(response, results)#为item做一个类似于标记的东西
当spider返回结果(item或request)时该方法被调用。 设定该方法的目的是在结果返回给框架核心(framework core)之前做最后的处理, 例如设定item的ID。其接受一个结果的列表(list of results)及对应的response。 其结果必须返回一个结果的列表(list of results)(包含Item或者Request对象)。
例子:
from scrapy import log
from scrapy.contrib.spiders import XMLFeedSpider
from myproject.items import TestItem
class MySpider(XMLFeedSpider):#继承这一步
name = 'example.com'
allowed_domains = ['example.com']
start_urls = ['http://www.example.com/feed.xml']
iterator = 'iternodes' # 这是没必要的,它是默认值,改其他可以设置
itertag = 'item'
def parse_node(self, response, node):##就像为什么一定用parse写
log.msg('Hi, this is a <%s> node!: %s' % (self.itertag, ''.join(node.extract())))
item = TestItem()
item['id'] = node.xpath('@id').extract()
CSVFeedSpider
class scrapy.contrib.spiders.CSVFeedSpider
spider除了其按行遍历每次迭代时调用的是 parse_row() 。
delimiter在CSV文件中用于区分字段的分隔符。类型为string。 默认为 ',' (逗号)
quotechar在csv文件中CSV文件中用于引用每个记录的字符串,默认为' " '
headers在CSV文件中包含的用来提取字段的行的列表,(对应字段)
parse_row(response, row)#row,对应的是csv的字段集合header。
该方法接收一个response对象及一个以提供或检测出来的header为键的字典(代表每行)。
#也接受改写在下载时候或者是item返回爬虫是。该spider中,您也可以覆盖 adapt_response 及 process_results 方法来进行预处理(pre-processing)及后(post-processing)处理。
例子:
rom scrapy.spiders import CSVFeedSpider
from myproject.items import TestItem
class MySpider(CSVFeedSpider):
name = 'example.com'
allowed_domains = ['example.com']
start_urls = ['http://www.example.com/feed.csv']
delimiter = ';'
quotechar = "'"
headers = ['id', 'name', 'description']
def parse_row(self, response, row):
self.logger.info('Hi, this is a row!: %r', row)
item = TestItem()
item['id'] = row['id']
item['name'] = row['name']
item['description'] = row['description']
return item
SitemapSpider##从roobt协议中提取接口并追踪
class scrapy.spiders.SitemapSpider
爬取网站时可以通过 Sitemaps 来发现爬取的URL。
其支持嵌套的sitemap,并能从 robots.txt 中获取sitemap的url
sitemap_urls#包含您要爬取的url的sitemap的url列表(list)。
可以指定为一个 robots.txt ,spider会从中分析并提取url。
sitemap_rules一个包含 (regex, callback) 元组的列表(list):
regex 是一个用于匹配从sitemap提供的url的正则表达式。 regex 可以是一个字符串或者编译的正则对象(compiled regex object)。
callback指定了匹配正则表达式的url的处理函数。 callback 可以是一个字符串(spider中方法的名字)或者是callable。
例:
sitemap_rules = [('/product/', 'parse_product')]#类似cwalerspider中的rules
规则按顺序进行匹配,之后第一个匹配才会被应用。如果您忽略该属性,sitemap中发现的所有url将会被 parse 函数处理。
sitemap_follow:(跟进站点的列表,默认跟进所有)一个用于匹配要跟进的sitemap的正则表达式的列表(list)。其仅仅被应用在 使用 Sitemap首页文件来指向其他sitemap文件的站点。默认情况下所有的sitemap都会被跟进。
sitemap_alternate_links:指定当一个 url 有可选的链接时,是否跟进。
例:
http://example.com/
当 sitemap_alternate_links 设置时,两个URL都会被获取。 当 sitemap_alternate_links 关闭时,只有 http://example.com/ 会被获取。默认 sitemap_alternate_links 关闭。
例子:
用特定的函数处理某些url,其他的使用另外的callback:
from scrapy.spiders import SitemapSpide
class MySpider(SitemapSpider):
sitemap_urls = ['http://www.example.com/sitemap.xml']
sitemap_rules = [
('/product/', 'parse_product'),
('/category/', 'parse_category'),
]
def parse_product(self, response):
pass # ... scrape product ...
def parse_category(self, response):
pass # ... scrape category ...
跟进 robots.txt 文件定义的sitemap并只跟进包含有 ..sitemap_shop 的url:
from scrapy.spiders import SitemapSpider
class MySpider(SitemapSpider):
sitemap_urls = ['http://www.example.com/robots.txt']
sitemap_rules = [
('/shop/', 'parse_shop'),
]
sitemap_follow = ['/sitemap_shops']
def parse_shop(self, response):
pass # ... scrape shop here ...
在SitemapSpider中使用其他url:
from scrapy.spiders import SitemapSpider
class MySpider(SitemapSpider):
sitemap_urls = ['http://www.example.com/robots.txt']
sitemap_rules = [
('/shop/', 'parse_shop'),
]
other_urls = ['http://www.example.com/about']
def start_requests(self):
requests = list(super(MySpider, self).start_requests())
requests += [scrapy.Request(x, self.parse_other) for x in self.other_urls]
return requests
def parse_shop(self, response):
pass # ... scrape shop here ...
def parse_other(self, response):
pass # ... scrape other here ...