Scrapy 基础入门
0x01 框架概述
Scrapy 是基于 Python 的一个非常流行的网络爬虫框架,可以用来抓取 Web 站点并从页面中提取结构化的数据
(1)核心组件
- 引擎:用来控制整个系统的数据处理流程
- 调度器:从引擎接收请求并排序列入队列,并在引擎发出请求后返还
- 下载器:抓取网页并将网页内容返还给蜘蛛
- 蜘蛛程序:蜘蛛是用户自定义的用来解析网页并抓取特定 URL 的类
- 数据管道:处理蜘蛛从网页中抽取的数据条目
- 中间件:介于引擎和其他组件的钩子框架,提供自定义的代码来拓展 Scrapy 的功能
(2)工作流程
- 引擎问询蜘蛛需要处理的网站,并让蜘蛛交出第一个需要处理的 URL
- 引擎让调度器将需要处理的 URL 放入队列
- 引擎从调度中获取需要开始爬取的页面
- 调度将下一个需要爬取的 URL 返回到引擎,引擎将该 URL 通过下载中间件发送到下载器
- 当网页被下载器下载完成后,响应内容通过下载中间件发送到引擎;下载失败则引擎会通知调度记录并稍后重新下载该 URL
- 引擎收到下载器的响应并通过蜘蛛中间件发送到蜘蛛进行处理
- 蜘蛛处理响应并返回爬取到的数据条目,此外还要将需要跟进的新的 URL 发送给引擎
- 引擎将抓取到的数据条目送入数据管道,把新的 URL 发送给调度器放入队列中
0x02 第一个程序
(1)创建项目
a. 命令行
-
使用命令
pip install scrapy
安装 Scrapycssselect == 1.1.0
parsel == 1.6.0
-
使用命令
scrapy startproject demo
创建项目 Demo -
在项目目录下,使用命令
scrapy genspider douban movie.douban.com
创建蜘蛛程序 -
项目目录如下:
demo │ scrapy.cfg │ └─demo │ items.py │ middlewares.py │ pipelines.py │ settings.py │ __init__.py │ └─spiders │ douban.py └─ __init__.py
b. PyCharm
- 在设置中添加 Scrapy 包
(2)编写和运行程序
-
提供种子 URL并解析页面:douban.py
import scrapy from ..items import MovieItem class DoubanSpider(scrapy.Spider): name = "douban" allowed_domains = ["movie.douban.com"] start_urls = ["https://movie.douban.com/top250"] def parse(self, response: scrapy.http.HtmlResponse, **kwargs): select = scrapy.Selector(response) list_items = select.css('#content > div > div.article > ol > li') for item in list_items: movie_item = MovieItem() movie_item['title'] = item.css('span.title::text').extract_first() movie_item['rate'] = item.css('span.rating_num::text').extract_first() movie_item['inq'] = item.css('span.inq::text').extract_first() yield movie_item
-
数据组成 Item 对象:items.py
class MovieItem(scrapy.Item): title = scrapy.Field() rate = scrapy.Field() inq = scrapy.Field()
-
修改请求头信息:settings.py
# 遵循爬虫协议 ROBOTSTXT_OBEY = True # 用户代理 USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.131 Safari/537.36 SLBrowser/8.0.1.4031 SLBChan/103" # 并发请求数 CONCURRENT_REQUESTS = 32 # 下载延迟 DOWNLOAD_DELAY = 3 # 随机化下载延迟 RANDOMIZE_DELAY = True # 配置数据管道 ITEM_PIPELINES = { "demo.pipelines.DemoPipeline": 300, }
-
使用命令
scrapy crawl douban -o douban.csv
运行爬虫程序并将结果输出到 douban.csv 中-o
改为-O
表示覆写 douban.csv
(3)多页面爬取
-
在 douban.py 中修改,将
start_urls = ["https://movie.douban.com/top250"]
替换为以下内容:def start_requests(self): for page in range(10): yield Request(url=f"https://movie.douban.com/top250?start={page*25}&filter=")
0x03 数据管道
- 使用命令
pip freeze > requirements.txt
生成依赖清单- 使用命令
pip install -r requirements.txt
自动添加缺少的依赖
(1)Excel
-
添加依赖 openpyxl
-
在 pipelines.py 中添加管道
import openpyxl class DemoPipeline: def __init__(self): self.workbook = None self.worksheet = None # 开启蜘蛛程序时执行以下函数 def open_spider(self, spider): # 创建工作簿 self.workbook = openpyxl.Workbook() # 获取默认工作表 self.worksheet = self.workbook.active # 修改工作表标题 self.worksheet.title = "Top250" # 设置表头 self.worksheet.append(('标题', '评分', '评价')) # 关闭蜘蛛程序时执行以下函数 def close_spider(self, spider): # 保存数据 self.workbook.save('top250.xlsx') # 数据处理 def process_item(self, item, spider): title = item.get('title', '') rate = item.get('rate', 0) inq = item.get('inq', '') self.worksheet.append((title, rate, inq)) return item
-
在 settings.py 中配置管道
ITEM_PIPELINES = { "demo.pipelines.DemoPipeline": 300, }
- 值越小,优先级越高
(2)MySQL
创建数据库
create schema if not exists scrapy; use scrapy; drop table if exists top; create table top ( id int auto_increment comment '编号', title varchar(50) not null comment '标题', rate decimal(3, 1) not null comment '评分', inq varchar(255) default '' comment '评论', primary key (id) ) engine=innodb comment='top250';
a. 单次写入
-
添加依赖 pymysql
-
在 pipelines.py 中添加管道
import pymysql class DemoPipeline: def __init__(self): self.connection = None self.cursor = None # 开启蜘蛛程序时执行以下函数 def open_spider(self, spider): # 连接数据库 self.connection = pymysql.connect( host='localhost', port=3306, user='root', password='root', database='scrapy' ) # 设置游标 self.cursor = self.connection.cursor() # 关闭蜘蛛程序时执行以下函数 def close_spider(self, spider): # 提交修改 self.connection.commit() # 关闭连接 self.connection.close() # 数据处理 def process_item(self, item, spider): title = item.get('title', '') rate = item.get('rate', 0) inq = item.get('inq', '') # SQL 插入操作 self.cursor.execute('INSERT INTO top (title, rate, inq) values (%s, %s, %s)', (title, rate, inq)) return item
b. 批量写入
-
修改 pipelines.py
class DemoPipeline: def __init__(self): self.connection = None self.cursor = None self.data = [] # ... # 数据处理 def process_item(self, item, spider): title = item.get('title', '') rate = item.get('rate', 0) inq = item.get('inq', '') self.data.append((title, rate, inq)) # SQL 插入操作 if len(self.data) >= 50: self.cursor.executemany('INSERT INTO top (title, rate, inq) values (%s, %s, %s)', self.data) self.connection.commit() self.data.clear() return item
0x04 中间件
(1)下载中间件
DownloaderMiddleware
-
下载中间件是一种拦截器,可以拦截请求和响应
-
在 middlewares.py 中设置下载中间件
def process_request(self, request, spider): cookies = '' cookies_dict = {} for item in cookies.split('; '): key, value = item.split('=', maxsplit=1) cookies_dict[key] = value request.cookies = cookies_dict return None
-
在 settings.py 中配置下载中间件
DOWNLOADER_MIDDLEWARES = { "demo.middlewares.DemoDownloaderMiddleware": 543, }
(2)蜘蛛中间件
SpiderMiddleware
-End-