Scrapy爬虫框架(1)一个简单的可用的爬虫

很久没写爬虫了,又重新开始使用 scrapy,之前学习的内容基本上都忘了,重新复习一遍,发现对它的理解又加深了一些。

本文将初级知识点简单梳理,实现了一个 HelloWorld 级别的 Scrapy 爬虫。

本文适用于 Scrapy 1.6.0,结合了自己的理解,可能理解有错误,欢迎在下面评论区指出。

不包含安装教程。

参考链接

Scrapy 是啥

先看看Scrapy-百度百科的解释:

Scrapy 是一个为爬取网站数据、提取结构性数据而设计的应用程序框架,它可以应用在广泛领域:Scrapy 常应用在包括数据挖掘,信息处理或存储历史数据等一系列的程序中。通常我们可以很简单的通过 Scrapy 框架实现一个爬虫,抓取指定网站的内容或图片。

它是一个框架,帮你解决写爬虫的过程中遇到的一些问题,简化你写爬虫的过程。对于一些简单的爬虫,你不需要自己来写重复的代码,它将重复的代码都隐藏起来,你只需要写一些与你需要爬取的网站相关的东西就可以了。

例如,爬虫需要发送请求和获取响应,scrapy 有个专门的调度器来帮你解决这个问题,你不需要自己来调度,你只需要使用它的下载器传给你的响应对象 Response 来进行解析即可,解析好的数据你也可以打包成它的一个名为 Item 的类的对象中,更方便地进行处理。方便很多。

Scrapy 的组成

下面这个图片来自于Python 爬虫-scrapy 介绍及使用

借用知乎找来的图片

  • 调度器(Scheduler)选择合适的时机发送 Request(请求)给下载器;
  • 下载器(Downloader)处理 Request(响应),即发送请求并获取响应 Response,将 Response 传给爬虫;
  • 爬虫(Spider)主要做两类事情:
    1. 提取当前 Response 中的数据,打包成 Item(或者是 dict),将它们发送给管道
    2. 获取 Response 中下一个 Request 的 url(比如你第一个 Response 爬取的是目录页,那么就是获取目录项对应的 url)从而构造下一个 Request,再将这个 Request 发送给调度器
  • 管道(Item Pipeline)处理 Item 中的数据
  • 中间件(Middleware)分为下载中间件和爬虫中间件,用来在传送 Request 和 Response 过程中做一些额外的处理
  • 引擎(Engine)用于将以上模块都连接起来,其他模块都直接与引擎交互,数据等由引擎进行转发

一般我们需要编写的,就是爬虫和管道,也就是解析数据和处理数据。

过程

创建项目

首先在命令行创建项目

1
$scrapy startproject 项目名称

会生成一个以你项目名称命名的文件夹,里面就是你的项目文件

再次借用一下知乎那篇文章的图片

创建爬虫

1
$scrapy genspider 爬虫名 爬取的域名

它的作用是在 spider 目录下按照模板创建一个以你爬虫为名的 py 文件,当然你也可以手动创建,只要你的文件符合 scrapy 的要求就行,最好用命令。

记得先切换到你项目目录。

编写爬虫

我要练习爬取的是Scrapy 中文网提供的爬虫实验室

爬虫命名为 lab,创建好的初始爬虫文件lab.py是这样的

1
2
3
4
5
6
7
8
9
10
11
# -*- coding: utf-8 -*-
import scrapy

class LabSpider(scrapy.Spider):
name = 'lab'
allowed_domains = ['lab.scrapyd.cn']
start_urls = ['http://lab.scrapyd.cn']

def parse(self, response):
pass

  • name:爬虫名
  • allowed_domains:允许访问的域名,注意是域名,而不是要爬取的 url,别写成http://lab.scrapyd.cn之类的
  • start_urls:初始 url
  • parse(self,response):解析函数,传入的参数就是 Response 响应,你可以用这个引用来获取网页内容,从而进行处理

运行过程理解

按照我的理解,当启动这个爬虫时:

  • scrapy 会将 start_urls 这个列表里面的 url 都生成对应的 Request 发给调度器
  • 然后调度器将 Request 通过引擎转发给下载器
  • 下载器再将下载好的 Response 发给引擎,引擎调用该爬虫的 parse 方法,将这个 Response 传入作为参数
  • 引擎获取 parse 的返回值
    • 如果是 Request(即新的请求),就发送给调度器
    • 如果是 item 或者 dict,就发送给管道
  • 当调度器中没有新的 Request 了,scrapy 停止。

调试解析

1
$scrapy shell 你要爬取的url

此处,我要爬取的就是http://lab.scrapyd.cn

这个命令可以打开交互式调试命令行,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$scrapy shell http://lab.scrapyd.cn
#……省略一大堆日志信息
[s] Available Scrapy objects:
[s] scrapy scrapy module (contains scrapy.Request, scrapy.Selector, etc)
[s] crawler <scrapy.crawler.Crawler object at 0x0000017F01C9A748>
[s] item {}
[s] request <GET http://lab.scrapyd.cn>
[s] response <200 http://lab.scrapyd.cn>
[s] settings <scrapy.settings.Settings object at 0x0000017F031BCBE0>
[s] spider <LabSpider 'lab' at 0x17f034c2da0>
[s] Useful shortcuts:
[s] fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed)
[s] fetch(req) Fetch a scrapy.Request and update local objects
[s] shelp() Shell help (print this help)
[s] view(response) View response in a browser
In [1]:

scrapy 这时已经将 response 给你了,你可以使用这些命令来进行调试。

没错,就是给 parse 函数传的那个 response 参数。

你可以使用:

1
In[1]:response.text

来获取得到的 html 字符串,以确定是否成功获取到自己想要的网页。

先去那个网站按 f12 查看一下它的元素:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<div class="col-mb-12 col-8" id="main" role="main">
<div class="quote post">
<span class="text"
>看官,此页面只为爬虫练习使用,都是残卷,若喜欢可以去找点高清版!</span
>
<span
>作者:<small class="author">中国传世名画</small>
<a href="http://lab.scrapyd.cn/archives/57.html">【详情】</a>
</span>
<p></p>
<div class="tags">
标签:
<a class="tag" href="http://lab.scrapyd.cn/tag/%E8%89%BA%E6%9C%AF/"
>艺术</a
><a class="tag" href="http://lab.scrapyd.cn/tag/%E5%90%8D%E7%94%BB/"
>名画</a
>
</div>
</div>

<div class="quote post">
<span class="text">下面每一幅都是上亿?你造几?</span>
<span
>作者:<small class="author">天价世界名画</small>
<a href="http://lab.scrapyd.cn/archives/55.html">【详情】</a>
</span>
<p></p>
<div class="tags">
标签:
<a class="tag" href="http://lab.scrapyd.cn/tag/%E8%89%BA%E6%9C%AF/"
>艺术</a
><a class="tag" href="http://lab.scrapyd.cn/tag/%E5%90%8D%E7%94%BB/"
>名画</a
>
</div>
</div>
</div>

我们需要获取到的,是以下内容:

1
2
[{'text': '看官,此页面只为爬虫练习使用,都是残卷,若喜欢可以去找点高清版!', 'tag': ['艺术', '名画']},
{'text': '下面每一幅都是上亿?你造几?', 'tag': ['艺术', '名画']}, ]

这个 response 拥有几种解析方法,你可以使用 xpath,也可以用 css。

xpath 教程:

比如使用 xpath:

1
2
3
4
5
6
7
In [2]: response.xpath('//div[contains(@class,"quote")]')
Out[2]:
[<Selector xpath='//div[contains(@class,"quote")]' data='<div class="quote post">\n\t <span '>,
<Selector xpath='//div[contains(@class,"quote")]' data='<div class="quote post">\n\t <span '>,
<Selector xpath='//div[contains(@class,"quote")]' data='<div class="quote post">\n\t <span '>,
<Selector xpath='//div[contains(@class,"quote")]' data='<div class="quote post">\n\t <span '>,
<Selector xpath='//div[contains(@class,"quote")]' data='<div class="quote post">\n\t <span '>]

返回的将是 Selector 的列表,Selector 的具体用法也不在本文范围内。

其实response.xpath()只是方便使用,它调用了response.selector.xpath(),也就是说 xpath 和 css 实际上是 Selector 的方法。说这个的原因在于告诉你,对于这个列表里面每一个 Selector,你都可以使用同样的方法来进行解析。

接着你就可以利用这个调试 shell 来调整你的 xpath 字符串或者 css 字符串了。

编写 Item 域

items.py 里面有着你可以用的 item 类,根据你确定需要获取的字段(Field)来给它添加:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# -*- coding: utf-8 -*-

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

import scrapy


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

解析代码

我用的是 css 选择器。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# -*- coding: utf-8 -*-
import scrapy

class LabSpider(scrapy.Spider):
name = 'lab'
allowed_domains = ['lab.scrapyd.cn']
start_urls = ['http://lab.scrapyd.cn']

def parse(self, response):
quotes= response.css('div.quote')
for quote in quotes:
item = items.TutorialItem()
text = quote.css('.text::text')
tag = quote.css('.tag::text')
item['text'] = text.get()
item['tag'] = tag.getall()
yield item

开始爬取

1
$scrapy crawl 需要启动的爬虫名 -o 输出文件名(比如test.json)

scrapy 会自动将得到的 item 保存到输出文件

解决导入 items 模块的问题

1
2
3
4
5
6
7
import sys
import os
path = os.path.dirname(__file__)
parent_path = os.path.dirname(path)
sys.path.append(parent_path)

import items

Scrapy爬虫框架(1)一个简单的可用的爬虫

https://yxchangingself.xyz/posts/Scrapy_spider_1/

作者

憧憬少

发布于

2020-02-18

更新于

2020-02-18

许可协议