第四章——scrapy爬虫
来自Scrapy 网络爬虫实战的阅读笔记
更多细节可看官网
编写爬虫
scrapy基本类组件说明
scrapy中的Selector选择器
Scrapy通用爬虫介绍与使用
Scrapy爬虫的主要实现
Scrapy主要通过Spider类来实现爬虫的相关功能,通俗来讲,Spider类定义了爬取某个或某些网站的规则,包括爬取数据和提取数据。
Spider循环爬取步骤如下:
1.通过start_requests()以start_urls中的URL初始化Request,下载完毕后返回Response,作为参数传给回调函数parse。
2.使用parse函数分析Response,可以返回Item对象、dict、Request或一个包含三者的可迭代容器。其中,Request可以经过Scrapy继续下载内容,调用设置的回调函数。
3.在parse函数内使用Selector分析Response,提取相应的数据。
scrapy.Spider爬虫基本类
编写爬虫的脚本(spiders文件夹下)是通过继承scrapy.Spide类来实现的,每个其他的Spider必须继承自该类(包括Scrapy自带的其他Spider以及自定义编写的Spider)。Spider类没有提供什么特殊的功能,仅仅提供了start_requests()的默认实现,读取并请求Spider属性中的start_urls,并根据返回的结果(Resulting Responses)调用Spider的parse方法。
Spider类常用属性(也就是spiders文件夹下的爬虫脚本)
name
name属性是必须且唯一的,其定义了Spider的名字,而Scrapy通过Spider的名字来定位并初始化Spider
allow_domain
该属性可选,其包含允许Spider爬取的域名列表。当中间件OffsiteMiddleWare启用时,将不会跟进不再列表中的域名
start_urls
是一个URL列表列表,当没有指定特定URL时,Spider将从该列表中开始获取页面数据,后续的URL将从获取的数据中提取。
custom_settings
该属性可选,是一个dict。当Spider启动时,会覆盖项目的设置,由于设置必须在初始化前被更新,因此必须设定为class属性。(疑惑)反正这个不常用
实例spider脚本文件(现在就是弄不懂script的终端的那一堆输出)
# import scrapy
from scrapy.spiders import CrawlSpider, Rule
from scrapy.linkextractors import LinkExtractor
class Quotes(CrawlSpider):
# 爬虫名称
name = "get_quotes"
allow_domain = ['quotes.toscrape.com']
start_urls = ['http://quotes.toscrape.com/']
# 设定规则
rules = (
# 对于quotes内容页URL,调用parse_quotes处理,
# 并以此规则跟进获取的链接;前面是对应url规则,后面是提取返回响应的相关数据
# 这些爬取的都是根路由之后的规则
Rule(LinkExtractor(allow=r'/page/\d+'), callback='parse_quotes', follow=True),
# 对于author内容页URL,调用parse_author处理,提取数据
Rule(LinkExtractor(allow=r'/author/\w+'), callback='parse_author')
)
# 提取内容页数据方法
def parse_quotes(self, response):
for quote in response.css(".quote"):
# 这里使用extract_first()就是确保返回的是字符串把
yield {'content': quote.css('.text::text').extract_first(), # 返回第一条数据内容
'author': quote.css('.author::text').extract_first(), # 返回第一条该作者
'tags': quote.css('.tag::text').extract() # 返回该标签
}
# 获取作者数据方法
def parse_author(self, response):
name = response.css('.author-title::text').extract_first()
author_born_date = response.css('.author-born-date::text').extract_first()
author_bron_location = response.css('.author-born-location::text').extract_first()
author_description = response.css('.author-description::text').extract_first()
return ({'name': name,
'author_bron_date': author_born_date,
'author_bron_location': author_bron_location,
'author_description': author_description
})
从上面实例中我们可以提取出来的东西
parse(response)方法的基本写法;也就是上方的parse_quotes()和parse_author()等函数;这个名字可以自行取,但是去了之后一定要在rules中的callback=xxx来调用它
parse(response)方法也是Scrapy默认回调方法,若我们并没有在生成的Request之后指定回调函数,他也会默认调用框架自带的parse()函数(虽然我不知道该函数在框架中的源码位置)
该函数主要负责处理Response,返回抓取的数据,或者跟进的URL。该方法必须返回可迭代的Request、dist或Item对象
基本写法如下
def parse(self, response):
for quote in response.css(".quote"):
yield {'content': quote.css('.text::text').extract_first(),
'author': quote.css('.author::text').extract_first(),
'tags': quote.css('.tag::text').extract()
}
Selector选择器
Scrapy通过实现一套构建于lxml库上名为选择器(Selector)的机制来提取数据,主要通过特定的Xpath或者css表达式来选择html文件中的某个指定部分。
选择器列表不一定是列表,还有可能时字符串等数据格式
1.XPath:传入XPath表达式,返回表达式对应节点的选择器列表
2.css:传入CSS表达式,但会表达式对应节点的选择器列表(这里注意获取文本和属性使用的与常用的css表达式不太相同
a::text()/加了括号就必须是键值对形式的数据; a::attr())** 这种加了::的方式就是提取相应的属性**
3.extract:以列表形式返回被选择元素的Unicode字符串。通常被用来提取数据,extract_first()返回第一条数据
4.re():返回通过正则表达式提取的Unicode字符串列表。re_first('re正则表达式')返回第一条数据
通用爬虫
CrawlSpider
是抓取网站常用的Spider,他提供了一个通过指定一些规则来达到跟进链接的方便机制。
这些规则也就是rules属性,他是一个或一组Rule对象,必须写成tuple形式。
每一个Rule对象定义了对目标网站的爬取行为,如果有多个Rule对象匹配了同一个链接,就说明第一个Rule会失效。
爬行规则
classscrapy.spiders.Rule(link_extractor=None, callback=None, cb_kwargs=None, follow=None, process_links=None, process_request=None, errback=None)[源代码]¶
link_extractor 是一种 Link Extractor 对象,该对象定义如何从每个爬网页面提取链接。每个生成的链接将用于生成 Request 对象,该对象将在其 meta 词典(在 link_text 密钥)。如果省略,将使用不带参数创建的默认链接提取器,从而提取所有链接。
callback 是要为使用指定链接提取器提取的每个链接调用的可调用或字符串(在这种情况下,将使用来自具有该名称的爬行器对象的方法)。此回调接收一个 Response 作为其第一个参数,并且必须返回单个实例或 item objects 和/或 Request 对象(或其任何子类)。如上所述,收到的 Response 对象将包含生成 Request 在ITS中 meta 词典(在 link_text 密钥)
cb_kwargs 是包含要传递给回调函数的关键字参数的dict。
follow 是一个布尔值,用于指定是否从使用此规则提取的每个响应中遵循链接。如果 callback 没有 follow 默认为 True ,否则默认为 False .
process_links 是一个可调用的,或一个字符串(在这种情况下,将使用具有该名称的蜘蛛对象中的方法),对于使用指定的 link_extractor . 这主要用于过滤目的。
process_request 是一个可调用的(或字符串,在这种情况下,将使用来自具有该名称的爬行器对象的方法),它将在 Request 按此规则提取。此可调用对象应将上述请求作为第一个参数,并且 Response 作为第二个参数从其发出请求。它必须返回一个 Request 对象或 None (用过滤发出请求)。
errback 在处理规则生成的请求时引发任何异常时要调用的可调用或字符串(在这种情况下,将使用来自spider对象的具有该名称的方法)。它收到一个 Twisted Failure 实例作为第一个参数。
XMLFeedSpider
主要用于RSS源订阅内容的抓取。RSS源是基于XML的一种信息聚合技术。在XML文件中,item称为一个node节点。
迭代器可以从以下选项中选择: iternodes , xml 和 html . 建议使用 iternodes 由于性能原因,迭代器 xml 和 html 迭代器一次生成整个DOM以便解析它。然而,使用 html 因为迭代器在分析带有错误标记的XML时可能很有用。
要设置迭代器和标记名,必须定义以下类属性:
XMLFeedSpider中的一些属性
iterator:指定迭代器,迭代器主要用于分析数据RSS订阅源。
可用的迭代器有:
iternodes:性能高,基于正则表达式,是默认的迭代器,在属性中不指定的话就默认是他
html:使用Selector加载所有DOM结构进行分析,当数据量大是就会产生性能问题,有点事处理不合理标签是比较有用。
xml:同html一样使用Selector进行分析,同样有性能问题
itertag:指定需要迭代的节点
namespaces:以元组形式组成的列表,定义了Spider处理文档时可用的命名空间。(这个属性有啥用)
XMLFeedspider同样具有可复写的方法
adapt_response(response):此方法在处理分析Response之前被调用,可用于修改Response的内容。此方法返回类型为Response。
parse_node(response,selector):当匹配到节点时,调用此方法进行数据处理。很重要的一点就是此方法必须复写,否则爬虫不会正常工作。该方法必须返回一个Item、Request,或者一个包含Item或Request的迭代器。
process_result(response,result):当爬虫返回抓取结果时调用此方法。多用于在抓取结果传递给框架核心处理前做最好的修改。该方法必须接收一个结果列表和产生这些结果的Response,返回一个包含Item或Request的就跟列表。
实例
1.使用XMLFeedSpider模板创建爬虫(这是创建爬虫文件,之间爬虫项目已经创建)
scrapy genspider -t xmlfeed jobbole jobbole.com
2.使用Item收集数据,修改items.py文件
import scrapy
class JobboleItem(scrapy.Item):
# define the fields for your item here like:
# name = scrapy.Field()
# 文章标题
title = scrapy.Field()
# 发表日期
public_date = scrapy.Field()
# 文章链接
link = scrapy.Field()
3.jobbole.py文件编写(具体爬虫文件)
# -*- coding: utf-8 -*-
from scrapy.spiders import XMLFeedSpider
# 导入item
from xmlfeedspider.items import JobboleItem
class JobboleSpider(XMLFeedSpider):
name = 'jobbole'
allowed_domains = ['jobbole.com']
start_urls = ['http://top.jobbole.com/feed/']
iterator = 'iternodes' # 迭代器,不指定的话默认是iternodes
itertag = 'item' # 抓取item节点
def parse_node(self, response, selector):
item = JobboleItem() # items.py中的类实例化
# 字典的值均在JobboleItem()类中定义,大部分类型为scrapy.Field()
item['title'] = selector.css('title::text').extract_first()
item['public_date'] = selector.css('pubDate::text').extract_first()
item['link'] = selector.css('link::text').extract_first()
return item
4.修改settings.py的配置
ROBOTSTXT_OBEY = False
CSVFeedSpider
他是每行迭代,而XMLfeedSpider是根据节点来迭代数据。类似的,每行迭代调用的是parse_row()方法。
常用的属性方法如下
delimiter:字段分隔符,默认是英文逗号','
quotechar:CSV字段中如果包含回车、引号、逗号,那么此字段必须用双引号引起来,默认是半角双引号。
headers:CSV文件的标题头,该属性是一个列表
parse_row(response,row):对每一行数据进行处理,接收一个由Response、一个文件标题头组成的字典。
实例
使用Item收集数据,修改items.py文件
import scrapy
class CsvspiderItem(scrapy.Item):
# define the fields for your item here like:
# 姓名
name = scrapy.Field()
# 研究领域
SearchField = scrapy.Field()
# 服务分类
Service = scrapy.Field()
# 专业特长
Specialty = scrapy.Field()
具体爬虫文件
在控制台中yield输出之前都有[scrapy.core.scraper]
# -*- coding: utf-8 -*-
from scrapy.spiders import CSVFeedSpider
from csvfeedspider.items import CsvspiderItem
class CsvparseSpider(CSVFeedSpider):
name = 'csvdata'
allowed_domains = ['gzdata.gov.cn']
start_urls = ['http://gzopen.oss-cn-guizhou-a.aliyuncs.com/科技特派员.csv']
headers = ['name', 'SearchField', 'Service', 'Specialty']
delimiter = ','
quotechar = "\n"
# Do any adaptations you need here
def adapt_response(self, response): # 特定的编码处理的函数
print('我也没看过return怎么返回的', response.body.decode('gb18030'))
return response.body.decode('gb18030') # 编码处理
def parse_row(self, response, row):
i = CsvspiderItem()
try:
i['name'] = row['name']
i['SearchField'] = row['SearchField']
i['Service'] = row['Service']
i['Specialty'] = row['Specialty']
except:
pass
# 在控制台中yield输出之前都有[scrapy.core.scraper]
yield i # yield是输出,return我没看出打印在了控制台
SitemapSpider
允许通过Sitemap发现URL链接来爬取一个网站。
而Sitemap是包含网站所有网址以及每个网址的其他元数据,包括上次更新的时间、更改的频率以及相对于网站上其他网址的重要程度为何等。
常用属性
sitemap_urls:一个包含待爬取url的sitemap列表,也可以指定rebots.txt,表示从rebots.txt中提取url.
sitemap_rules:一个元组列表,形如(regex,callback),其中:
reqex:表示需要从sitemap中提取的url的正则表达式,可以是一个字符串或者正则表达式对象
callback:式处理对应的url的回调方法,提取到对应链接时调用parse函数处理。需要注意的是,相同的链接只会调用第一个方法处理,并且如果此属性并未指定,那所有的链接默认使用parse方法处理。
sitemap_follow:一个指定需要跟进的sitemap的正则表达式列表。当使用Sitemap index files来指向其他sitemap文件的站点时此属性有效。默认情况下,所有的sitemap都会被根进。
sitemap_alternate_links:指定当一个url有可选的链接时是否跟进,有些网站url块内会提供备用网址。
实例
使用Item收集数据,修改items.py文件
# -*- coding: utf-8 -*-
# Define here the models for your scraped items
#
# See documentation in:
# https://doc.scrapy.org/en/latest/topics/items.html
import scrapy
class CnblogsItem(scrapy.Item):
# define the fields for your item here like:
# 文章标题
title = scrapy.Field()
# 文章url
url = scrapy.Field()
# 文章作者
author = scrapy.Field()
编写爬虫脚本文件
from scrapy.spiders import SitemapSpider
from cnblogs.items import CnblogsItem
class MySpider(SitemapSpider):
name = 'articles'
# Sitemap 地址
sitemap_urls = ['http://www.cnblogs.com/sitemap.xml']
# 从Sitemap中提取url的规则,并指定回调方法
sitemap_rules = [
# 抓取 ***/cate/python/**的url,调用parse_python处理
('/cate/python/','parse_python')
]
# 回调方法
def parse_python(self,response):
articles = response.css('.post_item')
for article in articles:
item = CnblogsItem()
# 文章标题
item['title'] = article.css('.titlelnk::text').extract_first()
# 文章url
item['url'] = article.css('.titlelnk::attr(href)').extract_first()
# 文章作者
item['author'] = article.css('.lightblue::text').extract_first()
yield item
来自2021/12/13都此篇感受
这书上的没点基础可以说是完全会看不懂的,实例的举例太少了,还不如官网,虽然翻译成中文很撇脚。