Scrapy 基础入门

0x01 框架概述

Scrapy 是基于 Python 的一个非常流行的网络爬虫框架,可以用来抓取 Web 站点并从页面中提取结构化的数据

(1)核心组件

  • 引擎:用来控制整个系统的数据处理流程
  • 调度器:从引擎接收请求并排序列入队列,并在引擎发出请求后返还
  • 下载器:抓取网页并将网页内容返还给蜘蛛
  • 蜘蛛程序:蜘蛛是用户自定义的用来解析网页并抓取特定 URL 的类
  • 数据管道:处理蜘蛛从网页中抽取的数据条目
  • 中间件:介于引擎和其他组件的钩子框架,提供自定义的代码来拓展 Scrapy 的功能

(2)工作流程

  1. 引擎问询蜘蛛需要处理的网站,并让蜘蛛交出第一个需要处理的 URL
  2. 引擎让调度器将需要处理的 URL 放入队列
  3. 引擎从调度中获取需要开始爬取的页面
  4. 调度将下一个需要爬取的 URL 返回到引擎,引擎将该 URL 通过下载中间件发送到下载器
  5. 当网页被下载器下载完成后,响应内容通过下载中间件发送到引擎;下载失败则引擎会通知调度记录并稍后重新下载该 URL
  6. 引擎收到下载器的响应并通过蜘蛛中间件发送到蜘蛛进行处理
  7. 蜘蛛处理响应并返回爬取到的数据条目,此外还要将需要跟进的新的 URL 发送给引擎
  8. 引擎将抓取到的数据条目送入数据管道,把新的 URL 发送给调度器放入队列中

0x02 第一个程序

(1)创建项目

a. 命令行

  1. 使用命令 pip install scrapy 安装 Scrapy

    cssselect == 1.1.0

    parsel == 1.6.0

  2. 使用命令 scrapy startproject demo 创建项目 Demo

  3. 在项目目录下,使用命令 scrapy genspider douban movie.douban.com 创建蜘蛛程序

  4. 项目目录如下:

    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-

posted @ 2023-08-26 09:31  SRIGT  阅读(70)  评论(0编辑  收藏  举报