目录:
Scrapy 是一个爬虫框架,用于从网站中抓取结构化数据。
核心概念:
引擎是整个框架的核心,相当于系统的“CPU”。它负责控制所有组件之间的数据流,协调爬虫的启动与停止,并管理请求的发送与响应的传递。
调度器接收引擎传递过来的请求(URL),并将其组织成一个优先队列进行管理。它负责决定下一个要处理的请求是什么,并自动去除重复的 URL,避免无效工作。
下载器负责根据引擎传递的请求,从互联网上下载网页内容,并将获取到的响应(Response)交还给引擎。它建立在高效的异步模型之上,负担最重但性能出色。
爬虫是用户编写自定义抓取逻辑的核心部分。它负责处理下载器返回的响应,从中解析并提取所需的结构化数据(Item),同时还可以从中发现新的 URL 并提交给引擎,以便进行后续抓取。
项目管道负责处理爬虫提取出来的数据项(Item)。它可以对数据进行清洗、验证、去重,并最终存储到数据库、文件或其他持久化介质中。
中间件是位于引擎与其他组件之间的钩子(Hook),提供了一种灵活的扩展机制。主要分为两类:
安装依赖:
$ pip install scrapy
$ scrapy startproject myproject
上述命令会在当前目录下初始化 myproject 爬虫项目,目录结构如下:
|-- myproject
|-- myproject
|-- spiders # 存放所有爬虫文件的目录
|-- items.py # 定义要抓取的数据结构
|-- middlewares.py # 定义中间件
|-- pipelines.py # 定义数据处理管道
|-- settings.py # 项目设置文件, 如并发数、下载延迟、管道启用等
|-- scrapy.cfg # 项目部署配置文件
使用如下命令会在 spiders 目录下创建一个爬虫模版:
$ scrapy genspider example example.com
在 spiders 目录下会自动创建 example.py 文件,内容如下:
import scrapy
class ExampleSpider(scrapy.Spider):
name = "example"
allowed_domains = ["example.com"]
start_urls = ["https://example.com"]
def parse(self, response):
pass
目标网站: https://blog.dkvirus.com/ (我自己博客网站),抓取文章标题,文章分类以及文章创建时间。
$ scrapy startproject myblog
$ cd myblog
在 items.py 中,定义要抓取的数据字段:
import scrapy
class MyblogItem(scrapy.Item):
title = scrapy.Field()
category = scrapy.Field()
create_time = scrapy.Field()
在 spiders/myblog_spider.py 中编写爬虫:
import scrapy
from myblog.items import MyblogItem # 导入自定义的 Item
class MyBlogSpider(scrapy.Spider):
name = "myblog" # 爬虫的唯一标识
allowed_domains = ["blog.dkvirus.com"] # 允许爬取的域名
start_urls = ["https://blog.dkvirus.com/"] # 起始 URL 列表
def parse(self, response):
"""
解析响应,提取数据并生成新的请求。
"""
# 使用 CSS 选择器选取页面上文章列表
li_list = response.css('.list-group-item')
for li in li_list:
# 实例化一个Item对象
item = MyblogItem()
# 提取文章标题(::text获取文本,extract_first()取第一个结果)
item['title'] = li.css('a[target="_self"]::text').extract_first()
# 提取文章分类
item['category'] = li.css('ul.tags a::text').extract_first()
# 提取文章创建时间
item['create_time'] = li.css('.date::text').extract_first()
# 将 Item 返回给引擎,交给管道处理
yield item
# 处理分页:查找“下一页”的链接
next_page_url = response.css('a.next::attr(href)').extract_first()
if next_page_url:
# response.urljoin() 将相对 URL 转换为绝对 URL
next_page_absolute_url = response.urljoin(next_page_url)
# 生成一个新的 Request 对象,并指定回调函数为 parse 本身,实现递归抓取
yield scrapy.Request(url=next_page_absolute_url, callback=self.parse)
在 pipelines.py 中,可以编写管道来处理爬取的数据。例如,将数据保存为 JSON 格式。
import json
class MyblogPipeline:
def open_spider(self, spider):
"""爬虫启动时执行"""
self.file = open('blogs.json', 'w', encoding='utf-8')
def process_item(self, item, spider):
"""每个item都会经过此方法处理"""
line = json.dumps(dict(item), ensure_ascii=False) + "\n"
self.file.write(line)
return item # 必须返回item,以便被后续管道处理
def close_spider(self, spider):
"""爬虫关闭时执行"""
self.file.close()
在 settings.py 中启用该管道,并设置其优先级,数值越小优先级越高。
ITEM_PIPELINES = {
'myproject.pipelines.MyblogPipeline': 300,
}
在项目根目录下,执行以下命令启动名为 myblog (spiders/myblog_spider.py 文件中类的 name 属性值) 的爬虫。
$ scrapy crawl myblog
运行结束后会在当前目录下生成 blogs.json 文件,里面是博客的所有文章信息。
↶ 返回首页 ↶