百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

从原理到实战,一份详实的 Scrapy 爬虫教程

zhezhongyun 2024-12-28 16:48 113 浏览

来源:早起Python

作者:饮马长江

大家好,我是早起。

之前分享了很多 requests 、selenium 的 Python 爬虫文章,本文将从原理到实战带领大家入门另一个强大的框架 Scrapy。如果对Scrapy感兴趣的话,不妨跟随本文动手做一遍!

一、Scrapy框架简介

Scrapy是:由Python语言开发的一个快速、高层次的屏幕抓取和web抓取框架,用于抓取web站点并从页面中提取结构化的数据,只需要实现少量的代码,就能够快速的抓取。

二、运行原理

Scrapy框架的运行原理看下面一张图就够了(事实上原理是比较复杂的,也不是三言两语能够说清楚的,因此感兴趣的读者可以进一步阅读更多的相关文章来了解,本文不做过多讲解

Scrapy主要包括了以下组件:

  • 引擎(Scrapy Engine)
  • Item 项目
  • 调度器(Scheduler)
  • 下载器(Downloader)
  • 爬虫(Spiders)
  • 项目管道(Pipeline)
  • 下载器中间件(Downloader Middlewares)
  • 爬虫中间件(Spider Middlewares)
  • 调度中间件(Scheduler Middewares)

三. 入门

3.1安装

第一种:在命令行模式下使用pip命令即可安装:

$ pip install scrapy

第二种:首先下载,然后再安装:

$ pip download scrapy -d ./
# 通过指定国内镜像源下载 
$pip download  -i https://pypi.tuna.tsinghua.edu.cn/simple scrapy -d ./

进入下载目录后执行下面命令安装:

$ pip install Scrapy-1.5.0-py2.py3-none-any.whl

3.2使用

使用大概分为下面四步 1 创建一个scrapy项目

scrapy startproject mySpider

2 生成一个爬虫

scrapy genspider demo "demo.cn"

3 提取数据

完善spider 使用xpath等

4 保存数据

pipeline中保存数据

3.3 程序运行

在命令中运行爬虫

scrapy crawl qb     # qb爬虫的名字

在pycharm中运行爬虫

from scrapy import cmdline
cmdline.execute("scrapy crawl qb".split())

四、基本步骤

Scrapy 爬虫框架的具体使用步骤如下:

选择目标网站定义要抓取的数据(通过Scrapy Items来完成的)编写提取数据的spider执行spider,获取数据数据存储

五. 目录文件说明

当我们创建了一个scrapy项目后,继续创建了一个spider,目录结构是这样的:

下面来简单介绍一下各个主要文件的作用:

scrapy.cfg :项目的配置文件

mySpider/ :项目的Python模块,将会从这里引用代码

mySpider/items.py :项目的目标文件

mySpider/pipelines.py :项目的管道文件

mySpider/settings.py :项目的设置文件

mySpider/spiders/ :存储爬虫代码目录

5.1 scrapy.cfg文件

项目配置文件。这个是文件的内容:

# Automatically created by: scrapy startproject
#
# For more information about the [deploy] section see:
# https://scrapyd.readthedocs.io/en/latest/deploy.html

[settings]
default = mySpider.settings

[deploy]
#url = http://localhost:6800/
project = mySpider

5.2 mySpider**/**

项目的Python模块,将会从这里引用代码

5.3 mySpider/items.py

项目的目标文件

# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html

import scrapy

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

定义scrapy items的模块,示例: name = scrapy.Field()

5.4 mySpider/pipelines.py

项目的管道文件

# Define your item pipelines here
#
# Don't forget to add your pipeline to the ITEM_PIPELINES setting
# See: https://docs.scrapy.org/en/latest/topics/item-pipeline.html


# useful for handling different item types with a single interface
from itemadapter import ItemAdapter


class MyspiderPipeline:
    def process_item(self, item, spider):
        return item

这个文件也就是我们说的管道,当Item在Spider中被收集之后,它将会被传递到Item Pipeline(管道),这些Item Pipeline组件按定义的顺序处理Item。每个Item Pipeline都是实现了简单方法的Python类,比如决定此Item是丢弃而存储。以下是item pipeline的一些典型应用:

  • 验证爬取的数据(检查item包含某些字段,比如说name字段)
  • 查重(并丢弃)
  • 将爬取结果保存到文件或者数据库中

5.5 mySpider/settings.py

项目的设置文件

# Scrapy settings for mySpider project
...

BOT_NAME = 'mySpider' # scrapy项目名

SPIDER_MODULES = ['mySpider.spiders']
NEWSPIDER_MODULE = 'mySpider.spiders'
.......

# Obey robots.txt rules
ROBOTSTXT_OBEY = False # 是否遵守协议,一般给位false,但是创建完项目是是True,我们把它改为False

# Configure maximum concurrent requests performed by Scrapy (default: 16)
#CONCURRENT_REQUESTS = 32 # 最大并发量 默认16
......
#DOWNLOAD_DELAY = 3 # 下载延迟 3秒

# Override the default request headers: # 请求报头,我们打开
DEFAULT_REQUEST_HEADERS = {
  'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
  'Accept-Language': 'en',
}
# 爬虫中间件
#SPIDER_MIDDLEWARES = {
#    'mySpider.middlewares.MyspiderSpiderMiddleware': 543,
#}

# 下载中间件
#DOWNLOADER_MIDDLEWARES = {
#    'mySpider.middlewares.MyspiderDownloaderMiddleware': 543,
#}
......
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
#ITEM_PIPELINES = {
#    'mySpider.pipelines.MyspiderPipeline': 300, # 管道
#}
.......

省略号省略代码,一般重要点,给了注释

6.mySpider/spiders/ :存储爬虫代码目录

import scrapy

class DbSpider(scrapy.Spider):
    name = 'db'
    allowed_domains = ['douban.com'] # 可以修改
    start_urls = ['http://douban.com/'] # 开始的url也可以修改

    def parse(self, response):
        # pass

六. Scrapy shell

Scrapy终端是一个交互终端,我们可以在未启动spider的情况下尝试及调试代码,也可以用来测试XPath或CSS表达式,查看他们的工作方式,方便我们爬取的网页中提取的数据,但是一般使用的不多。感兴趣的查看官方文档:

官方文档

http://scrapy-chs.readthedocs.io/zh_CN/latest/topics/shell.html

Scrapy Shell根据下载的页面会自动创建一些方便使用的对象,例如 Response 对象,以及 Selector 对象 (对HTML及XML内容)

  • 当shell载入后,将得到一个包含response数据的本地 response 变量,输入 response.body将输出response的包体,输出 response.headers 可以看到response的包头。
  • 输入 response.selector 时, 将获取到一个response 初始化的类 Selector 的对象,此时可以通过使用 response.selector.xpath()response.selector.css() 来对 response 进行查询。
  • Scrapy也提供了一些快捷方式, 例如 response.xpath()response.css()同样可以生效(如之前的案例)。

Selectors选择器

Scrapy Selectors 内置 XPathCSS Selector 表达式机制

Selector有四个基本的方法,最常用的还是xpath:

  • xpath(): 传入xpath表达式,返回该表达式所对应的所有节点的selector list列表
  • extract(): 序列化该节点为字符串并返回list
  • css(): 传入CSS表达式,返回该表达式所对应的所有节点的selector list列表,语法同 BeautifulSoup4
  • re(): 根据传入的正则表达式对数据进行提取,返回字符串list列表

七、案例实战

本节,我将使用Scrapy爬取站酷数据作为示例

7.1 案例说明

既然已经初步了解了scrapy的工作流程以及原理,我们来做一个入门的小案例,爬取站酷首页推荐的item信息。如下图所示,一个小方框就是一个item信息。我们要提取每一个item的六个组成部分:

  1. imgLink(封面图片链接);
  2. title(标题);
  3. types(类型);
  4. vistor(人气);
  5. comment(评论数);
  6. likes(推荐人数)

然后只是一个页面的item,我们还要通过翻页实现批量数据采集。

7.2 文件配置

目录结构

在上一篇中我们说明了新建scrapy项目(zcool)和spider项目(zc),这里不再赘述,然后得到我们的目录结构如下图所示:

start.py文件

然后为了方便运行,在zcool目录下新建start文件。并进行初始化设置。

from scrapy import cmdline
cmdline.execute('scrapy crawl zc'.split())

settings.py文件

在这个文件里我们需要做几样设置

避免在程序运行的时候打印log日志信息

  LOG_LEVEL = 'WARNING' 
 ROBOTSTXT_OBEY = False 

添加请求头:

打开管道:

item.py文件

import scrapy

class ZcoolItem(scrapy.Item):
    # define the fields for your item here like:
    imgLink = scrapy.Field() # 封面图片链接
    title = scrapy.Field() # 标题
    types = scrapy.Field() # 类型
    vistor = scrapy.Field() # 人气
    comment = scrapy.Field() # 评论数
    likes = scrapy.Field() # 推荐人数

7.3 页面数据提取

首先我们在站酷页面使用xpath-helper测试一下:

然后zc.py文件里面初步测试一下:

def parse(self, response):
    divList = response.xpath('//div[@class="work-list-box"]/div')
    print(len(divList))

运行结果如下图所示:

没有问题,然后我们对各种信息分别解析提取,

def parse(self, response):
    divList = response.xpath('//div[@class="work-list-box"]/div')
    for div in divList:
        imgLink = div.xpath("./div[1]/a/img/@src").extract()[0] # 1.封面图片链接
  ...  2.title(标题);3 types(类型);4vistor(人气);5comment(评论数)  ....
        likes = div.xpath("./div[2]/p[3]/span[3]/@title").extract_first() # 6likes(推荐人数)

        item = ZcoolItem(imgLink=imgLink,title=title,types=types,vistor=vistor,comment=comment,likes=likes)

        yield item

解释: xpath提取数据方法:

S.N.方法 & 描述extract()返回的是符合要求的所有的数据,存在一个列表里。extract_first()返回的hrefs 列表里的第一个数据。get()和extract_first()方法返回的是一样的,都是列表里的第一个数据。getall()和extract()方法一样,返回的都是符合要求的所有的数据,存在一个列表里。

注意:

get() 、getall() 方法是新的方法,extract() 、extract_first()方法是旧的方法。extract() 、extract_first()方法取不到就返回None。get() 、getall() 方法取不到就raise一个错误。

item实例创建(yield上面一行代码)

这里我们之前在目录文件配置的item文件中已经进行了设置,对于数据存储,我们在爬虫文件中开头要导入这个类:

from zcool.items import ZcoolItem

然后使用yield返回数据。

为什么使用yield而不是return

不能使用return这个无容置疑,因为要翻页,使用return直接退出函数;而对于yield:在调用for的时候,函数内部不会立即执行,只是返回了一个生成器对象。在迭代的时候函数会开始执行,当在yield的时候,会返回当前值(i)。之后的这个函数会在循环中进行,直到没有下一个值。

7.4 翻页实现批量数据采集

通过上面的代码已经可以初步实现数据采集,只不过只有第一页的,如下图所示:

但是我们的目标是100个页面的批量数据采集,所以代码还需要修改。针对翻页这里介绍两种方式:

方式一:我们首先在页面中定位到下一页的按钮,如下图所示:

然后编写如下代码,在for循环完毕后。

next_href = response.xpath("//a[@class='laypage_next']/@href").extract_first()
if next_href:
    next_url = response.urljoin(next_href)
    print('*' * 60)
    print(next_url)
    print('*' * 60)
    request = scrapy.Request(next_url)
    yield request

scrapy.Request(): 把下一页的url传递给Request函数,进行翻页循环数据采集。

https://www.cnblogs.com/heymonkey/p/11818495.html # scrapy.Request()参考链接

注意方式一只有下一页按钮它的href对应属性值和下一页的url一致才行。

方式二:定义一个全局变量count = 0,每爬取一页数据,令其加一,构建新的url,再使用scrapy.Request() 发起请求。

如下图所示:

count = 1

class ZcSpider(scrapy.Spider):
    name = 'zc'
    allowed_domains = ['zcool.com.cn']
    start_urls = ['https://www.zcool.com.cn/home?p=1#tab_anchor'] # 第一页的url

    def parse(self, response):
        global count
        count += 1
        
        for div in divList:
    # ...xxx...
            yield item

        next_url = 'https://www.kuaikanmanhua.com/tag/0?state=1&sort=1&page={}'.format(count)
        yield scrapy.Request(next_url)

这两种方式在实际案例中择机采用。

7.5 数据存储

数据存储是在pipline.py中进行的,代码如下:

from itemadapter import ItemAdapter
import csv

class ZcoolPipeline:
    def __init__(self): 
        self.f = open('Zcool.csv','w',encoding='utf-8',newline='')       # line1
        self.file_name = ['imgLink', 'title','types','vistor','comment','likes']  # line2
        self.writer = csv.DictWriter(self.f, fieldnames=self.file_name)     # line3
        self.writer.writeheader()              # line4

    def process_item(self, item, spider):
        self.writer.writerow(dict(item))              # line5
        print(item)
        return item                  # line6 

    def close_spider(self,spider):
        self.f.close()

解释:

  • line1: 打开文件,指定方式为写,利用第3个参数把csv写数据时产生的空行消除
  • line2: 设置文件第一行的字段名,注意要跟spider传过来的字典key名称相同
  • line3: 指定文件的写入方式为csv字典写入,参数1为指定具体文件,参数2为指定字段名
  • line4: 写入第一行字段名,因为只要写入一次,所以文件放在__init__里面
  • line5: 写入spider传过来的具体数值,注意在spider文件中yield的item,是一个由类创建的实例对象,我们写入数据时,写入的是 字典,所以这里还要转化一下。
  • line6: 写入完返回

7.6 程序运行

因为之前创建了start.py文件,并且对它就行了初始化设置,现在运行爬虫程序不需要在控制台中输入命令:

scrapy crawl zc(爬虫项目名)

直运行start.py文件:得到如下结果:

对应于页面:

打开csv文件如下图所示:(由于csv文件在word中乱码了,此处我是用Notepad++打开)

没有问题,数据采集完毕。

7.7. 总结

入门案例,需要细心,主要是基础知识的巩固,以便于为进阶学习做好准备。

相关推荐

Python入门学习记录之一:变量_python怎么用变量

写这个,主要是对自己学习python知识的一个总结,也是加深自己的印象。变量(英文:variable),也叫标识符。在python中,变量的命名规则有以下三点:>变量名只能包含字母、数字和下划线...

python变量命名规则——来自小白的总结

python是一个动态编译类编程语言,所以程序在运行前不需要如C语言的先行编译动作,因此也只有在程序运行过程中才能发现程序的问题。基于此,python的变量就有一定的命名规范。python作为当前热门...

Python入门学习教程:第 2 章 变量与数据类型

2.1什么是变量?在编程中,变量就像一个存放数据的容器,它可以存储各种信息,并且这些信息可以被读取和修改。想象一下,变量就如同我们生活中的盒子,你可以把东西放进去,也可以随时拿出来看看,甚至可以换成...

绘制学术论文中的“三线表”具体指导

在科研过程中,大家用到最多的可能就是“三线表”。“三线表”,一般主要由三条横线构成,当然在变量名栏里也可以拆分单元格,出现更多的线。更重要的是,“三线表”也是一种数据记录规范,以“三线表”形式记录的数...

Python基础语法知识--变量和数据类型

学习Python中的变量和数据类型至关重要,因为它们构成了Python编程的基石。以下是帮助您了解Python中的变量和数据类型的分步指南:1.变量:变量在Python中用于存储数据值。它们充...

一文搞懂 Python 中的所有标点符号

反引号`无任何作用。传说Python3中它被移除是因为和单引号字符'太相似。波浪号~(按位取反符号)~被称为取反或补码运算符。它放在我们想要取反的对象前面。如果放在一个整数n...

Python变量类型和运算符_python中变量的含义

别再被小名词坑哭了:Python新手常犯的那些隐蔽错误,我用同事的真实bug拆给你看我记得有一次和同事张姐一起追查一个看似随机崩溃的脚本,最后发现罪魁祸首竟然是她把变量命名成了list。说实话...

从零开始:深入剖析 Spring Boot3 中配置文件的加载顺序

在当今的互联网软件开发领域,SpringBoot无疑是最为热门和广泛应用的框架之一。它以其强大的功能、便捷的开发体验,极大地提升了开发效率,成为众多开发者构建Web应用程序的首选。而在Spr...

Python中下划线 ‘_’ 的用法,你知道几种

Python中下划线()是一个有特殊含义和用途的符号,它可以用来表示以下几种情况:1在解释器中,下划线(_)表示上一个表达式的值,可以用来进行快速计算或测试。例如:>>>2+...

解锁Shell编程:变量_shell $变量

引言:开启Shell编程大门Shell作为用户与Linux内核之间的桥梁,为我们提供了强大的命令行交互方式。它不仅能执行简单的文件操作、进程管理,还能通过编写脚本实现复杂的自动化任务。无论是...

一文学会Python的变量命名规则!_python的变量命名有哪些要求

目录1.变量的命名原则3.内置函数尽量不要做变量4.删除变量和垃圾回收机制5.结语1.变量的命名原则①由英文字母、_(下划线)、或中文开头②变量名称只能由英文字母、数字、下画线或中文字所组成。③英文字...

更可靠的Rust-语法篇-区分语句/表达式,略览if/loop/while/for

src/main.rs://函数定义fnadd(a:i32,b:i32)->i32{a+b//末尾表达式}fnmain(){leta:i3...

C++第五课:变量的命名规则_c++中变量的命名规则

变量的命名不是想怎么起就怎么起的,而是有一套固定的规则的。具体规则:1.名字要合法:变量名必须是由字母、数字或下划线组成。例如:a,a1,a_1。2.开头不能是数字。例如:可以a1,但不能起1a。3....

Rust编程-核心篇-不安全编程_rust安全性

Unsafe的必要性Rust的所有权系统和类型系统为我们提供了强大的安全保障,但在某些情况下,我们需要突破这些限制来:与C代码交互实现底层系统编程优化性能关键代码实现某些编译器无法验证的安全操作Rus...

探秘 Python 内存管理:背后的神奇机制

在编程的世界里,内存管理就如同幕后的精密操控者,确保程序的高效运行。Python作为一种广泛使用的编程语言,其内存管理机制既巧妙又复杂,为开发者们提供了便利的同时,也展现了强大的底层控制能力。一、P...