Python爬虫学习笔记-第十八课(Scrapy入门)

Scrapy入门

  • 1. Scrapy简介及安装
  • 2. Scrapy的工作流程
  • 3. Scrapy的快速入门
    • 3.1 创建一个简单的scrapy工程
    • 3.2 程序目录结构
    • 3.3 豆瓣案例练习
    • 3.4 Pipelines管道保存数据

1. Scrapy简介及安装

为什么要学习Scrapy:可以大幅提升爬虫的效率。
什么是Scrapy:⼀个为了爬取网站数据,提取结构性数据而编写的应用框架。
Scrapy的优点:

  1. 可配置和扩展性高;
  2. 基于Twisted异步网络框架,只需要实现少量的代码,就能够快速的抓取。

同步与异步
Python爬虫学习笔记-第十八课(Scrapy入门)_第1张图片
异步:调用在发出之后,这个调用就直接返回,不管有无结果;
非阻塞:关注的是程序在等待调用结果时的状态,指在不能立刻得到结果之前,该调用不会阻塞当前线程。

Scrapy参考链接:
https://scrapy-chs.readthedocs.io/zh_CN/1.0/intro/overview.html
https://scrapy.org/

Scrapy的安装:
Python爬虫学习笔记-第十八课(Scrapy入门)_第2张图片
pip安装,采用清华园下载:

> pip install -i https://pypi.tuna.tsinghua.edu.cn/simple/ scrapy

在这里插入图片描述

2. Scrapy的工作流程

一种常见的多线程爬虫的流程图如下:
Python爬虫学习笔记-第十八课(Scrapy入门)_第3张图片
笔者之前写的多线程爬取王者荣耀高清壁纸采用的就是上述思路。

Scrapy框架中各组成部分如下:

  1. 引擎:整个框架的核心
  2. 调度器:接收从引擎发过来的url,并入列
  3. 下载器:下载网页源码,返回给爬虫程序
  4. 项目管道:数据处理
  5. 下载中间件 处理引擎与下载器之间的请求
  6. 爬虫中间件 处理爬虫程序响应和输出结果以及新的请求

下载中间件和爬虫中间件是负责引擎和下载器还有引擎和爬虫程序之间的数据传输。

给出百度上用到的流程图:
Python爬虫学习笔记-第十八课(Scrapy入门)_第4张图片
笔者课程所用图:
Python爬虫学习笔记-第十八课(Scrapy入门)_第5张图片
Scrapy模块的功能表格:

部件名称 功能作用 是否需手写
Engine(引擎) 总指挥,负责数据和信号的在不同模块间的传递 Scrapy已经实现
Scheduler(调度器) ⼀个队列,存放引擎发过来的request请求 Scrapy已经实现
Downloader(下载器) 下载把引擎发过来的requests请求,并返回给引擎 Scrapy已经实现
Spider(爬虫程序) 处理引擎发来的response,提取数据和url,并交给引擎 需要手写
Item Pipline(管道) 处理引擎传过来的数据,比如存储 需要手写
Downloader Middlewares(下载中间件) 可以⾃定义的下载扩展,比如设置代理 ⼀般不用手写
Spider Middlewares(爬虫中间件) 可以⾃定义requests请求和进行response过滤 ⼀般不用手写

3. Scrapy的快速入门

3.1 创建一个简单的scrapy工程

第一步,创建Scrapy项目。

> scrapy startproject project_name

第二步,创建爬虫的程序。

> cd project_name
> scrapy genspider example example.com

example指爬虫程序的名字,example.com指想爬取网站的域名(范围)。

创建爬虫程序后的文件目录结构如下:

Python爬虫学习笔记-第十八课(Scrapy入门)_第6张图片
第三步,获取到response后,使用xpath方法提取所需的数据。

第四步,编写piplines管道代码,用于保存数据。

3.2 程序目录结构

items.py:可用于封装数据,以字典的形式。

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

middlewares.py:下载中间件和爬虫中间件的代码。

from scrapy import signals
# useful for handling different item types with a single interface
from itemadapter import is_item, ItemAdapter

# 这里为方便显示,先将内部代码设为pass
class FirstscrapySpiderMiddleware:
   	pass

class FirstscrapyDownloaderMiddleware:
    pass

piplines.py:管道,使用时不要忘记打开管道设置。

from itemadapter import ItemAdapter
class FirstscrapyPipeline:
    def process_item(self, item, spider):
        return item

settings.py:存储全局配置,这里给出常用的一些配置:

# 是否遵守robots协议,一般为False
ROBOTSTXT_OBEY = False

# 设置日志等级
LOG_LEVEL = 'WARNING'

# 最大并发量,默认为16
# Configure maximum concurrent requests performed by Scrapy (default: 16)
CONCURRENT_REQUESTS = 16

# 下载延迟为3s,默认注释,即没有下载延迟
DOWNLOAD_DELAY = 3

# 请求报头,可以添加user_agent等
DEFAULT_REQUEST_HEADERS = {
     
    'User_Agent': 'Mozilla/5.0 ****',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en',
}

# 爬虫中间件
SPIDER_MIDDLEWARES = {
     
   'firstscrapy.middlewares.FirstscrapySpiderMiddleware': 543,
}

# 下载中间件
DOWNLOADER_MIDDLEWARES = {
     
   'firstscrapy.middlewares.FirstscrapyDownloaderMiddleware': 543,
}

# 设置管道
ITEM_PIPELINES = {
     
   'firstscrapy.pipelines.FirstscrapyPipeline': 300,
}

3.3 豆瓣案例练习

代码需求:爬取豆瓣网页的一些文字标签。
数据标签分布图:
Python爬虫学习笔记-第十八课(Scrapy入门)_第7张图片
本案例中,我们所需要爬取的数据在class属性为"side-links nav-anon"div/li标签下。

在正式编写代码前,笔者先梳理一下必要的准备工作。
首先,配置settings.py的文件内容如下:

BOT_NAME = 'firstscrapy'

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

# Obey robots.txt rules
ROBOTSTXT_OBEY = False

# 下载延迟为1秒
DOWNLOAD_DELAY = 1

# 请求报头# 请求报头
DEFAULT_REQUEST_HEADERS = {
     
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36',
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en',
}

# 管道
ITEM_PIPELINES = {
     
   'firstscrapy.pipelines.FirstscrapyPipeline': 300,
}

其次,Scrapy框架为用户自动生成的基础爬虫爬虫程序如下:

import scrapy

class DbSpider(scrapy.Spider):
    name = 'db'
    allowed_domains = ['douban.com']
    start_urls = ['http://douban.com/']

    def parse(self, response):
        pass

按照之前提及的Scrapy工作流程,爬虫程序向引擎提交url后,引擎发送给调度器进行入列,然后会给下载器,对目标url访问获得响应(网页源码),其返回的结果就是上述代码中的response。在拿到response后,我们尝试解析这个数据,笔者在程序开头导入如下变量(导入该变量不会影响程序,实际使用也不需要该语句,这里只是为了方便演示):

from scrapy.http.response.html import HtmlResponse

点击HtmlResponse,查看其源码,可以看到它里面有xpath、css等方法,如下图(如果不知道具体模块,也可以直接打印其数据类型进行查找):
Python爬虫学习笔记-第十八课(Scrapy入门)_第8张图片
有了xpath方法后,在程序中就可以向之前一样使用xpath获取想要的标签内容或属性。

先写一个简单的代码,获取到response后用xpath语句,提取上图想要标签的内容:

import scrapy

class DbSpider(scrapy.Spider):
    name = 'db'
    allowed_domains = ['douban.com']
    start_urls = ['http://douban.com/']

    def parse(self, response):
        item={
     }
        text_list = response.xpath('//div[@class="side-links nav-anon"]/ul/li')
        print(type(response)) # 打印response的数据类型
        print(type(text_list[0])) # 打印列表中元素的数据类型
        for text in text_list:
            item['name'] = text.xpath('a/em/text()').extract_first()
            if item['name'] == None:
                item['name'] = text.xpath('a/text()').extract_first()
                print(item['name'])

如何运行Scrapy爬虫程序

  1. 可以直接在命令中运行爬虫:
scrapy crawl spider_name
  1. 如果希望通过Python脚本运行爬虫程序,先创建一个start.py脚本,其内容如下:
from scrapy import cmdline
cmdline.execute("scrapy crawl spider_name".split()) # 第一种写法
cmdline.execute(['scrapy','crawl','spider_name']) # 第二种写法

运行结果:
Python爬虫学习笔记-第十八课(Scrapy入门)_第9张图片
上面的截图中最上方显示Spider opened,说明开始执行自定义的爬虫程序。上文已分析过,下载器返回的response对象可以使用xpath方法,其返回的是一个Selector对象列表。我们可以继续对Selector对象使用xpath方法,如果想进一步提取Selector对象中的数据,有如下方法:

  • extract_first()和get()方法都是显示第一条数据,extract()和getall()方法都是显示多条数据;
  • 两者的区别在于extract()和extract_first()是旧方法,如果取不到想要的数据就返回None,而get()和getall()是新方法,如果取不到数据就返回一个错误。

上述代码中笔者使用的是extract_first()方法,也可以使用get()方法。
代码中还有一个小细节,在上述代码中先找'a/em/text()', 若判断其为空才找'a/text()',是为了跳过含有em标签的a标签,确保所要寻找数据的一致性,如下图:

Python爬虫学习笔记-第十八课(Scrapy入门)_第10张图片

3.4 Pipelines管道保存数据

我们在爬取到数据后,需要处理或者保存数据,可以交给piplines管道执行,这也是scrapy框架设定的管道职责。

如何将爬取到的数据交给piplines:

  1. 首先要在settings文件里一定要开启管道;
# 设置管道
ITEM_PIPELINES = {
     
   'firstscrapy.pipelines.FirstscrapyPipeline': 300,
}
  1. 在爬虫文件中是通过yield关键字把数据给管道。
def parse(self, response):
	xxxxxx
    yield item          

爬虫程序代码:

import scrapy

class DbSpider(scrapy.Spider):
    name = 'db'
    allowed_domains = ['douban.com']
    start_urls = ['http://douban.com/']

    def parse(self, response):
        item={
     }
        text_list = response.xpath('//div[@class="side-links nav-anon"]/ul/li')
        for text in text_list:
            item['name'] = text.xpath('a/em/text()').extract_first()
            if item['name'] == None:
                item['name'] = text.xpath('a/text()').extract_first()
            yield item

piplines管道代码:

class FirstscrapyPipeline:
    def process_item(self, item, spider):
        print(item)
        return item

运行结果:
Python爬虫学习笔记-第十八课(Scrapy入门)_第11张图片
小拓展——open_spider()/close_spider()

  1. 其分别表示爬虫开始/结束的方法,且必须书写一个形参(可以不使用)。
  2. 方法的名字不可以更改。
  3. 方法可用于定义一些爬虫开始或结束时希望执行的操作。

修改后的piplines代码:

import json

class FirstscrapyPipeline:
    # 注意方法名不能写错,要传递一个形参
    def open_spider(self, item):
        print('This is open_spider method.')

    def process_item(self, item, spider):
        with open('result.json', 'a', encoding='utf-8') as fobj:
            # 传递关键字参数ensure_ascii=False,确保数据正确性
            item_json = json.dumps(item, ensure_ascii=False)
            # 每行写一个元素
            fobj.write(item_json + '\n')
        return item

    def close_spider(self, item):
        print('This is close_spider method.')

运行结果:
Python爬虫学习笔记-第十八课(Scrapy入门)_第12张图片
Python爬虫学习笔记-第十八课(Scrapy入门)_第13张图片
保存到本地的json文件:
Python爬虫学习笔记-第十八课(Scrapy入门)_第14张图片

你可能感兴趣的:(学习笔记,python,爬虫)