第154集 Scrapy框架基础

1 什么是Scrapy?

Scrapy是一个用于爬取网站数据、提取结构化信息的Python框架。它提供了一套强大的工具和机制,使得开发者能够快速、高效地编写网络爬虫。

Scrapy的主要特点:

  • 基于事件驱动的异步处理
  • 内置数据提取机制(XPath和CSS选择器)
  • 强大的扩展功能
  • 内置支持数据存储(JSON、CSV、XML等)
  • 自动处理请求并发
  • 内置反爬虫机制支持

2 Scrapy的安装

在使用Scrapy之前,我们需要先安装它。

pip install scrapy

3 Scrapy的基本架构

Scrapy采用了模块化的架构设计,主要包含以下组件:

  1. **引擎(Engine)**:负责控制数据流在各个组件之间的流动
  2. **调度器(Scheduler)**:管理待爬取的请求队列
  3. **下载器(Downloader)**:下载网页内容
  4. **爬虫(Spider)**:定义爬取规则和数据提取逻辑
  5. **项目管道(Item Pipeline)**:处理爬取到的数据
  6. **下载器中间件(Downloader Middleware)**:处理请求和响应
  7. **爬虫中间件(Spider Middleware)**:处理爬虫的输入(响应)和输出(请求和项目)

4 创建第一个Scrapy项目

4.1 创建项目

使用Scrapy命令行工具创建一个新的项目:

scrapy startproject myspider

这将创建一个名为myspider的目录,包含以下结构:

myspider/
├── scrapy.cfg          # 项目配置文件
└── myspider/          # 项目的Python模块
    ├── __init__.py
    ├── items.py      # 定义项目的数据模型
    ├── middlewares.py# 中间件定义
    ├── pipelines.py  # 项目管道
    ├── settings.py   # 项目设置
    └── spiders/      # 存储爬虫的目录
        └── __init__.py

4.2 创建爬虫

spiders目录下创建一个新的爬虫文件:

cd myspider
scrapy genspider example example.com

这将在spiders目录下创建一个名为example.py的爬虫文件,内容如下:

import scrapy

class ExampleSpider(scrapy.Spider):
    name = "example"
    allowed_domains = ["example.com"]
    start_urls = ["http://example.com/"]

    def parse(self, response):
        pass

5 Scrapy爬虫的基本结构

5.1 爬虫类定义

一个基本的Scrapy爬虫包含以下几个部分:

import scrapy

class MySpider(scrapy.Spider):
    # 爬虫名称,必须唯一
    name = "myspider"
    
    # 允许爬取的域名列表
    allowed_domains = ["example.com"]
    
    # 起始URL列表
    start_urls = ["http://example.com/page1", "http://example.com/page2"]
    
    # 解析响应的方法
    def parse(self, response):
        # 在这里编写解析逻辑
        pass

5.2 解析响应

Scrapy提供了两种主要的方式来解析响应内容:XPath和CSS选择器。

使用XPath

def parse(self, response):
    # 使用XPath选择器提取数据
    titles = response.xpath('//h1/text()').getall()
    for title in titles:
        print(title)
    
    # 提取单个元素
    single_title = response.xpath('//h1/text()').get()
    if single_title:
        print(f"单个标题: {single_title}")
    
    # 提取属性值
    links = response.xpath('//a/@href').getall()
    for link in links:
        print(f"链接: {link}")

使用CSS选择器

def parse(self, response):
    # 使用CSS选择器提取数据
    titles = response.css('h1::text').getall()
    for title in titles:
        print(title)
    
    # 提取单个元素
    single_title = response.css('h1::text').get()
    if single_title:
        print(f"单个标题: {single_title}")
    
    # 提取属性值
    links = response.css('a::attr(href)').getall()
    for link in links:
        print(f"链接: {link}")

6 提取数据并创建Item

6.1 定义Item

items.py文件中定义数据模型:

import scrapy

class MyspiderItem(scrapy.Item):
    # define the fields for your item here like:
    # name = scrapy.Field()
    title = scrapy.Field()
    link = scrapy.Field()
    description = scrapy.Field()

6.2 在爬虫中使用Item

import scrapy
from myspider.items import MyspiderItem

class ExampleSpider(scrapy.Spider):
    name = "example"
    allowed_domains = ["example.com"]
    start_urls = ["http://example.com/"]

    def parse(self, response):
        # 创建Item对象
        item = MyspiderItem()
        
        # 提取数据并填充Item
        item['title'] = response.css('h1::text').get()
        item['link'] = response.url
        item['description'] = response.css('meta[name="description"]::attr(content)').get()
        
        # 返回Item
        yield item
        
        # 或者返回多个Item
        for article in response.css('.article'):
            article_item = MyspiderItem()
            article_item['title'] = article.css('h2::text').get()
            article_item['link'] = article.css('a::attr(href)').get()
            article_item['description'] = article.css('p::text').get()
            yield article_item

7 跟进链接

Scrapy允许我们在解析响应时提取新的链接,并跟进这些链接继续爬取。

def parse(self, response):
    # 解析当前页面的数据
    # ...
    
    # 提取下一页链接并跟进
    next_page = response.css('a.next-page::attr(href)').get()
    if next_page:
        # 绝对URL
        if next_page.startswith('http'):
            next_url = next_page
        # 相对URL,转换为绝对URL
        else:
            next_url = response.urljoin(next_page)
        
        # 发送新的请求并指定回调函数
        yield scrapy.Request(next_url, callback=self.parse)
    
    # 或者使用更简洁的方式
    for href in response.css('a.article-link::attr(href)'):
        yield response.follow(href, callback=self.parse_article)
    
# 处理文章详情页的回调函数
def parse_article(self, response):
    item = MyspiderItem()
    item['title'] = response.css('h1.article-title::text').get()
    item['content'] = response.css('div.article-content::text').getall()
    yield item

8 运行Scrapy爬虫

8.1 使用命令行运行爬虫

# 进入项目目录
cd myspider

# 运行爬虫
scrapy crawl example

8.2 保存爬取结果

# 保存为JSON格式
scrapy crawl example -o items.json

# 保存为CSV格式
scrapy crawl example -o items.csv

# 保存为XML格式
scrapy crawl example -o items.xml

9 Scrapy设置

9.1 基本设置

settings.py文件中可以配置Scrapy的各种参数:

# 爬虫名称
BOT_NAME = 'myspider'

# 爬虫模块
SPIDER_MODULES = ['myspider.spiders']
NEWSPIDER_MODULE = 'myspider.spiders'

# 遵循robots.txt规则
ROBOTSTXT_OBEY = True

# 并发请求数
CONCURRENT_REQUESTS = 32

# 下载延迟
DOWNLOAD_DELAY = 1

# 禁用cookies
COOKIES_ENABLED = False

# 用户代理
USER_AGENT = 'myspider (+http://www.example.com)'

# 项目管道
ITEM_PIPELINES = {
    'myspider.pipelines.MyspiderPipeline': 300,
}

10 项目管道

项目管道用于处理爬取到的数据,例如清理、验证、存储等操作。

10.1 定义管道

pipelines.py文件中定义管道:

class MyspiderPipeline:
    def process_item(self, item, spider):
        # 处理数据
        if item['title']:
            item['title'] = item['title'].strip()
        
        # 返回处理后的item
        return item

10.2 启用管道

settings.py中启用管道:

ITEM_PIPELINES = {
    'myspider.pipelines.MyspiderPipeline': 300,
}

数字表示管道的执行顺序,数值越小,执行优先级越高。

11 实际案例:爬取书籍信息

11.1 创建项目和爬虫

# 创建项目
scrapy startproject bookspider

# 进入项目目录
cd bookspider

# 创建爬虫
scrapy genspider books example.com

11.2 定义Item

items.py中定义数据模型:

import scrapy

class BookspiderItem(scrapy.Item):
    # 书籍标题
    title = scrapy.Field()
    # 书籍价格
    price = scrapy.Field()
    # 书籍评分
    rating = scrapy.Field()
    # 书籍链接
    link = scrapy.Field()

11.3 编写爬虫

spiders/books.py中编写爬虫逻辑:

import scrapy
from bookspider.items import BookspiderItem

class BooksSpider(scrapy.Spider):
    name = "books"
    allowed_domains = ["books.toscrape.com"]
    start_urls = ["http://books.toscrape.com/"]

    def parse(self, response):
        # 提取所有书籍信息
        for book in response.css('.product_pod'):
            item = BookspiderItem()
            
            # 提取书籍标题
            item['title'] = book.css('h3 a::attr(title)').get()
            
            # 提取书籍价格
            item['price'] = book.css('.price_color::text').get()
            
            # 提取书籍评分
            rating_classes = book.css('.star-rating::attr(class)').get()
            rating = rating_classes.split()[-1] if rating_classes else 'No rating'
            item['rating'] = rating
            
            # 提取书籍链接
            item['link'] = response.urljoin(book.css('h3 a::attr(href)').get())
            
            yield item
        
        # 提取下一页链接并跟进
        next_page = response.css('li.next a::attr(href)').get()
        if next_page:
            yield response.follow(next_page, callback=self.parse)

11.4 运行爬虫

scrapy crawl books -o books.json

12 总结

通过本集的学习,我们掌握了Scrapy框架的基础知识,包括:

  1. Scrapy的基本概念和架构
  2. 创建和运行Scrapy项目
  3. 编写基本的爬虫
  4. 使用XPath和CSS选择器提取数据
  5. 定义和使用Item
  6. 跟进链接继续爬取
  7. 配置Scrapy设置
  8. 使用项目管道处理数据
  9. 实际案例:爬取书籍信息

Scrapy是一个功能强大的爬虫框架,它提供了完整的爬取流程支持,使得我们能够快速、高效地开发各种网络爬虫。下一集我们将深入学习Scrapy爬虫的编写技巧。

« 上一篇 BeautifulSoup高级用法 下一篇 » Scrapy爬虫编写