Scrapy爬虫框架的优势:
Scrapy架构流程:
只有当调度器中不存在任何request时,整个程序才会停止。(注:对于下载失败的URL,
Scrapy也会重新下载.。)
Scrapy主要包括了以下组件:
安装 Scrapy
pip install scrapy
Scrapy 爬虫步骤:
scrapy startproject ScrapyProject
item.py
spiders/xxspider.py
# 创建一个名为book的spider用于爬取网络小说,所爬取的网址暂定为book.com,后续可修改
scrapy genspider book book.com
pipelines.py
scrapy crawl book
Scrapy 项目介绍并分析:
爬虫流程:
抓取的主要目的是从非结构化源(通常是网页)中提取结构化数据。 Scrapy Spider可以将提取的数据作为 Python 字典返回。 Python 字典虽然方便且熟悉,但缺乏结构容易在字段名称中输入错误或返回不一致的数据,尤其是在具有许多 spider 的大型项目中。
为了定义常见的输出数据格式,Scrapy提供了Item
类。 项目对象是用于收集抓取数据的简单容器。 它们提供了类似于字典的API,并带有方便的语法来声明其可用字段。
示例:
# ScrapyProject/items.py
import scrapy
from scrapy.loader.processors import TakeFirst
class BookItem(scrapy.Item):
"""
定义Item类:
1. 继承scrapy.Item类
2. 每个属性指定为scrapy.Field(不管是什么类型)
"""
# name = scrapy.Field()
# content = scrapy.Field()
# bookname = scrapy.Field()
name = scrapy.Field(output_processor=TakeFirst())
content = scrapy.Field(output_processor=TakeFirst())
bookname = scrapy.Field(output_processor=TakeFirst())
BookSpider(如下2.4节示例代码) 是你定义的类,是Scrapy用于从网站(或一组网站)中获取信息的,必须继承 Spider 的子类 (scrapy.Spider)
,并定义要发出的初始请求,可以选择如何跟随页面中的链接,以及如何解析 (parse)
下载的页面内容以提取数据。
Spider 子类 scrapy.Spider
中定义了一些属性和方法:
name
:标识 spider,它在一个项目中必须是唯一的,即不能为不同的Spider设置相同的名称。
start_requests()
:必须返回一个可迭代的请求(可以返回请求列表或编写一个生成器函数),Spider将从此开始。 随后的请求将根据这些初始请求连续生成。
parse()
:调用该方法来处理为每个请求下载的响应。 response 参数是 TextResponse 的一个实例,该实例保存页面内容并具有其他有用的方法来处理它。
parse()
方法通常解析响应,提取数据并生成一个字典,并且能够查找要遵循的新URL从中创建新请求(Request)。
Scrapy shell是一个交互式shell,在其中运行可以非常快速的调试爬虫代码,而不必运行Spider。 它是用于测试代码提取数据,但是实际上也可以将其用于测试任何类型的代码,因为它也是常规的 Python shell。
Scrapy shell 主要用于测试 XPath 或 CSS 表达式,并查看它们的工作方式以及从要抓取的网页中提取数据。 可以在编写 spider 程序时以交互方式测试你的表达式,而不必运行spider 程序来测试所有更改。
运行:
scrapy shell url
Item Loaders 提供了一种方便的机制来填充爬取的 Items。 虽然,可使用它们自己的类似于字典的API填充Item,但 Item Loader 提供了更方便的 API,用于在 scrapy 过程中将数据填充到item对象中,自动执行一些常见任务(例如在分配原始数据之前解析原始提取的数据)。
换句话说,项目提供了已抓取数据的容器,而项目 Item Loaders 则提供了填充该容器的机制。
Item Loaders 旨在提供一种灵活,高效且容易的机制,以扩展或覆盖不同的字段解析规则方便后续的维护。
基于 2.2、2.3、2.4的介绍,spider 的代码示例(请结合对页面的分析食用)如下所示:
# ScrapyProject/spiders/book.py
import scrapy
from scrapy import Request
from scrapy.loader import ItemLoader
from ScrapyProject.items import BookItem
class BookSpider(scrapy.Spider):
# 爬虫名称必须唯一
name = 'book'
base_url = "http://shicimingju.com"
# 起始的url地址,可以指定多个,有两种方式确定
# 1. start_urls=[] 属性设置
# 2. 通过 start_requests 方法生成起始url地址 --> 使用方法详见https://doc.scrapy.org/en/latest/intro/tutorial.html#our-first-spider
start_urls = [
'http://shicimingju.com/book/sanguoyanyi.html',
'http://shicimingju.com/book/hongloumeng.html',
'http://shicimingju.com/book/shuihuzhuan.html',
'http://shicimingju.com/book/xiyouji.html',
]
def parse(self, response):
"""
图书详情页解析
1). 如何编写好的解析代码?可以使用 Scrapy 的交互式工具 scrapy shell url
2). 如何处理解析后的数据? 通过 yield 返回解析数据的字典格式
3). 如何获取下载小数章节详情页的链接并下载到本地
:param response:
:return:
"""
# 1). 获取所有章节的li标签
chapters = response.xpath('//div[@class="book-mulu"]/ul/li')
# 2). 遍历每一个li标签, 提取章节的详细网址和章节名称
for chapter in chapters:
# -). 创建ItemLoader对象, 将item对象和selector/response关联
l = ItemLoader(item=BookItem(), selector=chapter)
# -). 根据xpath进行提取数据信息并填充到item对象的name属性中
l.add_xpath('name', './a/text()')
# -). 将数据信息(书籍名称)填充到item对象的bookname属性中
l.add_value('bookname', response.url.split('/')[-1].strip('.html'))
detail_url = chapter.xpath('./a/@href').extract_first() # ....extract_first()获得列表第一个值并转换为字符串
# 将章节详情页url提交到调度队列,通过Downloader下载器下载并提交给self.parse_chater_detail解析器进行解析处理数据
yield Request(url=self.base_url + detail_url,
callback=self.parse_chater_detail,
# meta={"name": name, "bookname": bookname}
# -). load_item获取item对象
meta={'item': l.load_item()}
)
def parse_chater_detail(self, response):
"""章节详情页解析"""
# 1. .xpath('string(.)') 获取该标签及子孙标签所有的文本信息
# 2. 如何将对象转换为字符串?
# - ....extract_first()/get() 获得列表中第一个值并转换为字符串
# - ....extract()/get_all() 获得列表中所有对象并将其转换为字符串
item = response.meta['item']
content = response.xpath(".//div[@class='chapter_content']")[0].xpath('string(.)').get()
item['content'] = content
yield item
spider 抓取了一个项目后,将其发送到项目管道(piplines),该管道通过依次执行的几个组件对其进行处理。
每个项目管道组件(有时仅称为“项目管道”)都是一个实现简单方法的 Python 类。 他们接收到一个项目并对其执行操作,还决定该项目是否应该继续通过管道或被删除并不再处理。
piplines 的典型用途是:
组件 process_item(self, item, spider)
:
每个项目管道组件均调用此方法。 process_item()
必须执行以下操作之一:返回带有数据的字典;返回Item(或任何后代类)对象;返回Deferred或引发DropItem异常。
删除的项目不再由其他管道组件处理。
参数:
代码示例:
import os
class ScrapyprojectPipeline(object):
"""设置管道存储爬取内容"""
def process_item(self, item, spider):
# books/xiyouji
dirname = os.path.join("books", item["bookname"])
if not os.path.exists(dirname):
os.makedirs(dirname) # 递归创建目录
name = item["name"]
# 文件名的相对路径用join方法拼接:linux路径拼接符是/;windows是\
filename = os.path.join(dirname, name)
# 写入文件以 w 方式打开,并指定编码格式为 utf-8 写入中文
with open(filename, "w", encoding="utf-8") as f:
f.write(item["content"])
# print("写入文件%s成功" % name)
return item