文章目录
1. 认识 Scrapy
Scrapy爬虫框架的优势:
- 用户只需要定制开发几个模块, 就可以轻松实现爬虫,用来抓取网页内容和图片, 非常方便。
- Scrapy 使用了 Twisted 异步网络框架来处理网络通讯, 加快网页下载速度, 不需要自己实现异步框架和多线程等, 并且包含了各种中间件接口, 灵活完成各种需求。
Scrapy架构流程:
只有当调度器中不存在任何request时,整个程序才会停止。(注:对于下载失败的URL,
Scrapy也会重新下载.。)
Scrapy主要包括了以下组件:
- 引擎(Scrapy):用来处理整个系统的数据流,触发事务(框架核心)
- 调度器(Scheduler):用来接受引擎发过来的请求,压入队列中,并在引擎再次请求的时候返回。可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列,由它来决定下一个要抓取的网址是什么,同时去除重复的网址
- 下载器(Downloader):用于下载网页内容,并将网页内容返回给蜘蛛(Scrapy下载器是建立在 twisted 这个高效的异步模型上的)
安装 Scrapy
pip install scrapy
Scrapy 爬虫步骤:
- 新建爬虫项目 ScrapyProject
scrapy startproject ScrapyProject
- 明确抓取目标 —> 编写
item.py
- 制作spider,爬取页面 ---->
spiders/xxspider.py
# 创建一个名为book的spider用于爬取网络小说,所爬取的网址暂定为book.com,后续可修改
scrapy genspider book book.com
- 存储爬虫,设置管道存储爬取内容 —>
pipelines.py
- 项目编写完成后,启动爬虫功能 book
scrapy crawl book
项目结构大体如下图所示:
2. Scrapy 项目——四大名著爬取
Scrapy 项目介绍并分析:
- 爬取网站http://shicimingju.com下的四大名著
- 分析后可知,四大名著的 url 信息分别为
‘http://shicimingju.com/book/sanguoyanyi.html’,
‘http://shicimingju.com/book/hongloumeng.html’,
‘http://shicimingju.com/book/shuihuzhuan.html’,
‘http://shicimingju.com/book/xiyouji.html’, - 在对每一本书对应的页面信息进行爬取时发现,要获取书籍内容必须要继续对页面下的章节url 进行数据爬取,分析如下图所示:
- 继续对章节页面分析
爬虫流程:
- 确定 start_url
- 引擎将起始的 url 交给调度器(存储到队列、去重)
- 调度器将 url 发送给 Downloader,Downloader 发起 Requests 从互联网上下载页面信息(Response)
- 将下载的内容交给 Spider,进行解析(parse函数),yield数据
- 将处理好的数据(items)
2.1 items
抓取的主要目的是从非结构化源(通常是网页)中提取结构化数据。 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())
2.2 spiders
BookSpider(如下2.4节示例代码) 是你定义的类,是Scrapy用于从网站(或一组网站)中获取信息的,必须继承 Spider 的子类 (scrapy.Spider)
,并定义要发出的初始请求,可以选择如何跟随页面中的链接,以及如何解析 (parse)
下载的页面内容以提取数据。
Spider 子类 scrapy.Spider
中定义了一些属性和方法:
-
name
:标识 spider,它在一个项目中必须是唯一的,即不能为不同的Spider设置相同的名称。 -
start_requests()
:必须返回一个可迭代的请求(可以返回请求列表或编写一个生成器函数),Spider将从此开始。 随后的请求将根据这些初始请求连续生成。 -
parse()
:调用该方法来处理为每个请求下载的响应。 response 参数是 TextResponse 的一个实例,该实例保存页面内容并具有其他有用的方法来处理它。
parse()
方法通常解析响应,提取数据并生成一个字典,并且能够查找要遵循的新URL从中创建新请求(Request)。
2.3 Scrapy shell
Scrapy shell是一个交互式shell,在其中运行可以非常快速的调试爬虫代码,而不必运行Spider。 它是用于测试代码提取数据,但是实际上也可以将其用于测试任何类型的代码,因为它也是常规的 Python shell。
Scrapy shell 主要用于测试 XPath 或 CSS 表达式,并查看它们的工作方式以及从要抓取的网页中提取数据。 可以在编写 spider 程序时以交互方式测试你的表达式,而不必运行spider 程序来测试所有更改。
运行:
scrapy shell url
2.4 Item Loaders
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
2.5 pipelines
spider 抓取了一个项目后,将其发送到项目管道(piplines),该管道通过依次执行的几个组件对其进行处理。
每个项目管道组件(有时仅称为“项目管道”)都是一个实现简单方法的 Python 类。 他们接收到一个项目并对其执行操作,还决定该项目是否应该继续通过管道或被删除并不再处理。
piplines 的典型用途是:
- 清理HTML数据
- 验证抓取的数据(检查项目是否包含某些字段)
- 检查重复项(并将其删除)
- 将 scrapy 的 items 存储在数据库中
组件 process_item(self, item, spider)
:
每个项目管道组件均调用此方法。 process_item()
必须执行以下操作之一:返回带有数据的字典;返回Item(或任何后代类)对象;返回Deferred或引发DropItem异常。
删除的项目不再由其他管道组件处理。
参数:
- item (Item object or a dict) – the item scraped
- spider (Spider object) – the spider which scraped the item
代码示例:
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
2.6 settings
项目的设置文件 settings.py
最后,执行 scrapy crael book
后可以获得: