Python:Scrapy(一) 基础知识与实例
阅读原文时间:2023年07月08日阅读:7

学习自:

Scrapy爬虫框架教程(一)-- Scrapy入门 - 知乎

Scrapy爬虫框架,入门案例(非常详细)_ck784101777的博客-CSDN博客_scrapy爬虫案例

爬虫框架Scrapy个人总结(详细)熟悉 - 简书

本章介绍Scrapy使用时的基本要素构成。

1、简介

Scrapy最初是为了页面抓取/网络抓取设计的。Scrapy用途广泛,可以应用数据挖掘、监控、自动化测试等领域。

Scrapy吸引人的地方在于它是一个框架,任何人都可以根据需求方便地进行修改。它也提供了多种类型爬虫的基类,如BaseSpider、sitemap爬虫等,最新版本又提供了web2.0爬虫的支持。

2、各组件作用

Scrapy框架主要由五大组件组成,分别是Scrapy引擎(Scrapy Engine)、下载器(Downloader)、Spider、Item Pipeline、调度器(Scheduler)。

①Scrapy Engine

引擎是整个框架的核心,它用来控制、调度Scheduler、Downloader、Spider。

②调度器(Scheduler)

调度器可以视为一个想要进行抓取的URL队列,由它来决定下一个要抓取的网址是什么,同时可以去除重复的网址。用户可以根据自己的需求定制调度器。

③下载器(Downloader)

下载器负责下载页面数据并提供给引擎,而后提供给Spider。Scrapy的下载器代码并不复杂,但是效率很高,这是因为Scrapy下载器是建立在twisted这个高效的异步模型上的。

④Item Pipeline

Item Pipeline负责处理被Spider提取出来的Item。典型的处理有清理多余信息、验证Item有效性及持久化Item(例如存取到数据库中)。

当页面被爬虫解析所需的数据存入Item之后,将会被发送到Pipeline,并经过几个特定的次序处理数据,最后存入本地文件或者存入数据库。

⑤Spider

用户定制自己的爬虫(通过正则表达式等语法),用于从特定的网页中提取自己需要的信息,即Item。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面。

除了以上五种组件,还有另外三个概念(它们与前边五种组件是相互关联的)也需要了解:

①下载器中间件(Downloader middlewares)

下载器中间件是在引擎及下载器之间的特定钩子(Specific hook),处理Downloader传递给引擎的response。它提供了一个简便机制,通过插入自定义代码来扩展Scrapy功能

通过设置下载器中间件可以事先爬虫自动更换User-Agent、IP等功能。

②Spider中间件(Spider middlewares)

Spider中间件是在引擎及Spider之间的特定钩子(Specific hook),处理Spider的输入(Response)和输出(Item及Requests)。

③数据流(Data Flow)

1).Engine打开一个网站,找到处理该网站的Spider并向该Spider请求第一个要爬取的URL(s);

2).Engine从Spider中获取到第一个要爬取的URL,将其放入Scheduler的URL队列中;

3).Engine向Scheduler请求下一个要爬取的URL;

4).Scheduler返回下一个要爬取的URL给Engine,Engine将URL发送给Downloader;

5)、6).Downloader访问Internet,下载页面;

7).Downloader生成一个该页面的Response,并将其发送给Engine;

8).Engine将Response发送给Spider处理;

9).Spider处理Response并返回爬取到的Item及新URL给Engine,重复上述流程直到URL队列中没有多余URL;

10).Engine将爬取到的Item发送到Item Pipeline。

将整个流程可视化,可以得到如下流程图,该图是按照上文数据流中的顺序进行的。

官方架构图:图中的绿线是数据流向。

3、一些类、文件的作用

①类

1)Spider

class scrapy.spider.Spider

Spider是最简单的spider。每个其他的spider都必须继承自该类(包括Scrapy自带的其他spider以及我们自己编写的spider)。Spider没有提供其他特殊的功能,其作用为1)区分请求给定的start_urls/start_requests;2)根据response调用parse方法。

属性:

name:spider名字的字符串。spider的名字定义了Scrapy如何开始定位(并初始化)spider,所以其必须是唯一的。如果该spider爬取单个网站,一个常见的做法是以该网站名来命名spider。例如spider要爬取mywebsite.com,该spider通常会被命名为mywebsite。

allowed_domains:可选。包含了spider允许爬取的URL List。当OffsiteMiddleware启用时,不在List中指定URL及其子URL中的URL将不会被跟进(即爬取的URL必须与该List中的URL有相同的前缀)。

start_urls:URL List。当没有特定的URL时,spider将从该List中开始进行爬取。因此,第一个被获取到的页面的URL必将是该List其中之一,后续URL将会从获取到的数据中提取。

方法:

start_requests():该方法必须返回一个Iterable。该Iterable包含了spider用于爬取的第一个Request。当spider启动爬取且并未制定URL时,该方法将会被调用。该方法的默认实现是使用start_urls的URL来生成Request。如果我们想要修改最初爬取某个网站的Request对象,那么可以重写该方法。例如,如果我们需要在启动时以POST的方式登录某个网站,可以这么写

def start\_requests(self):  
    return \[scrapy.FormRequest('http://www.example.com/login',  
                               formdata={'user': 'john', 'pass': 'secret'},  
                               callback=self.logged\_in)\]  
def logged\_in(self,response):  
    pass

默认未被重写的情况下,该方法返回的Request对象中,parse()作为回调函数,dont_filter参数也被设置为开启。(详情见Reueqst)

make_requests_from_url(url):当spider启动且指定了URL时,该方法将会被调用,且仅仅会被调用一次,因此我们可以将之实现为Generator。该方法接受一个URL并返回用于爬取的Request对象。该方法在初始化Requests时被start_requests调用,也被用于转化URL为Request。

parse(response):当Response没有指定回调函数时,该方法是Scrapy处理下载的Response的默认方法。该方法负责处理Response并返回处理的数据以及跟进的URL。spider对其他的Request的回调函数也有相同的要求。关于返回值,该方法及其他Request回调函数必须返回一个包含Request或Item的Iterable。

参数:response:用于分析的Response

log(message [ ,level , commponent ] ):使用scrapy.log.msg()方法记录(log)message。log中自动带上该spider的name属性。更多数据可以参见Logging。

closed(reason):当spider关闭时,该方法被调用。该方法用于替代signals.connect()来监听spider_closed信号的快捷方式。

以上是最简单的Spider,此外还有其他Spider:Spiders — Scrapy 2.5.0 documentation

2)CrawlSpider

用于爬取URL具有一定规律的网页

3)Response

Scrapy中的response对象的属性、方法

属性

说明

类型

url

HTTP响应的URL

str

status

响应状态码

int

headers

响应头部,可以用get或getlist方法访问

类Dict

body

响应正文

bytes

text

文本形式的响应正文

response.text = response.body.decode(response.encoding)

str

encoding 

 响应正文的编码

 

request 

产生该响应的Request对象 

 

meta

即response.request.meta,在构造Request对象时,可以将传递给响应处理函函数的信息通过meta参数传入;响应处理函数处理相应时,通过response.meta将信息提取出来

 

selector

Selector对象用于在Response中提取数据 

 

方法

说明

xpath(query)

根据XPath路径表达式提取要素

css(query)

根据CSS语法提取要素

urljoin(url)

用于构造绝对url,当传入的url是一个相对地址时,根据response.url计算出相应的绝对url

②文件

通过指令scrapy startproject S创建项目后,会生成以下的文件结构:

S--->scrapy.cfg             #项目的配置文件
S-------->             #该项目的Python模块。之后你将在此加入代码
__init__.py     
items.py        #项目中的Item文件
pipelines.py      #Pipelines文件
settings.py      #设置文件
spiders--->      #放置Spider代码的目录
__init__.py
                   xxx.py #Spider代码

除了目录名S和Spider文件名是我们自定义的外,其他文件都是自动生成的,其中最关键的是pipelines.py和settings.py

1)items.py

存放需要提取元素名。

2)settings.py

BOT_NAME:项目名。

USER_AGENT:该项默认是注释掉的,但是由于现在的网站访问时都需要User-Agent,所以需要我们手动填充该项。

ROBOTSTXT_OBEY:是否遵循机器人协议,默认True,需要修改为False,否则很多东西爬不了(该项待验证)。

CONCURRENT_REQUESTS:最大并发数;同时允许开启多少个爬虫线程。

DOWNLOAD_DELAY:下载延迟时间;单位s,默认3s,即爬一个停3s。有的网站可能会对下载过快的情况进行侦测,从而判断出是爬虫并加以拒绝。

COOKIES_ENABLED:是否保存COOKIES;默认关,开启后可以记录爬取过程中的COOKIE。

DEFAULT_REQUEST_HEADERS:默认请求头;之前我们写了一个USER_AGENT,其实该项就是放在请求头中的。

ITEM_PIPELINES:项目管道;300为优先级,越低爬取的优先级越高。比如我的pipelines.py中写了两个pipeline,一个爬取网页的管道,另一个存数据库的管道,我调整了它们的优先级,如果有爬虫数据,则优先执行库存操作(如果items.py中有需要运行的代码,则该项必须声明)

ITEM_PIPELINES = {
'S.pipelines.BaiduPipeline' : 300,
'S.pipelines.BaiduMysqlPipeline':200
}

该Dict中的两个Key需要均是在要在pipelines.py事先定义好的两个类:

class BaiduPipeline(object):
#爬取网页
def process_item(self,item,spider):
return item

class BaiduMysqlPipeline(object):
#将爬取内容存入数据库
def process_item(self,item,spider):
return item

LOG_LEVEL:日志等级;日志登记分级见下表

DEBUG

调试信息

INFO

一般信息

WARNING

警告

ERROR

普通错误

CRITICAL

严重错误

默认为'DEBUG',即所有信息都会被记录进日志中,如果设置为WARNING,则只会记录WARNING、ERROR和CRITICAL

LOG_FILE:日志文件名;格式为 '日志名.log'

3.5、建立Scrapy爬虫项目的流程

创建爬虫和运行爬虫时,要在cmd中用到三段指令:

scrapy startproject 项目名
scrapy genspider 爬虫名 域名
scrapy crawl 爬虫名

其中前两句分别是创建项目和创建爬虫,而创建爬虫也可以通过第②部分中的中的name和start_urls进行创建。

第三句话是爬虫构建好之后,运行爬虫的命令。

①创建项目

在开始爬取之前,需要建立一个新的Scrapy项目。

进入你打算存储代码的目录中,运行以下命令

scrapy startproject 项目名
cd 项目名
scrapy genspider 爬虫名 URL

②编写爬虫代码

Spider是用户编写的用于从网站爬取数据的类。

其包含了一个用于下载初始URL,如何跟进网页中的链接及如何分析页面中的内容,提取生成Item的方法。

为了创建一个Spider,你必须继承scrapy.Spider类,且定义以下三个属性及方法

name:用于区别不同的Spider。

start_urls:包含了Spider在启动时进行爬取的URL列表。因此,第一个被获取到的页面将是其中之一。后续的URL则从初始URL获取到的数据中心提取。

parse():Spider的一个方法。被调用时,每个初始URL完成下载后生成的Response对象将作为唯一的参数传递给该函数。该方法负责解析返回的数据(response data),提取数据(生成Item)以及生成需要进一步处理的URL的Request对象。

③启动爬虫

打开终端进入项目所在文件夹中,运行命令:

scrapy crawl woodenrobo

启动爬虫后就可以看到打印出来当前页所有的文章标题了。

可以把爬取到的内容导出为JSON或者CSV格式,方法是在执行爬虫指令时加入参数-o:

scrapy crawl 爬虫名 -o 文件名.csv
scrapy crawl 爬虫名 -o 文件名.json

对于JSON文件,需要在setting.py文件中设置编码格式,否则会乱码:

FEED_EXPORT_ENCODING = 'UTF-8'

启动爬虫时的程序运行流程:

Scrapy为Spider的start_urls属性中的每个URL创建了Request对象,并将parse方法作为回调函数赋值给requests,而requests对象经过Scheduler的调度,执行生成response对象并送回给parse()方法进行解析,所以url的改变是靠回调函数实现的。

yield scrapy.Request(self.url,callback=self.parse)

4、实例

①提取百度首页的标题title

现在看第一个Spider代码,保存位置为S2\Spiders目录下,名字取为baidu.py,目的是使用XPath提取百度首页的标题title

import scrapy

class BaiduSpider(scrapy.Spider):
name = 'baidu'
allowed_domains = ['www.baidu.com']
start_urls = ['http://www.baidu.com/']

def parse(self, response):  
    tile=response.xpath('//html/head/title/text()')  
    print(tile)

打开cmd终端,输入

scrapy crawl baidu

就可以看到一大堆输出信息,其中就包含了我们需要的内容。

然而,使用终端运行太麻烦了,而且不能提取数据,我们写一个运行文件作为程序的入口:

from scrapy import cmdline

cmdline.execute('scrapy crawl baidu'.split())

这段话可以写在另一个py文件或者spider.py文件中,直接运行这句话所在的py文件就可以了。

②爬取腾讯视频中的电影信息

1)

创建项目,将爬虫项目命名为TX;

创建爬虫,爬虫命名为txms,爬虫的start_url设置为v.qq.com

scrapy startproject TX
cd TX
scrapy genspider txs v.qq.com

2)修改setting

修改三项:

ROBOTSTXT_OBEY:机器人项

DOWNLOAD_DELAY:下载间隙

DEFAULT_REQUEST_HEADERS:请求头,需要添加一个User-Agent

ROBOTSTXT_OBEY = False
DOWNLOAD_DELAY = 1
DEFAULT_REQUEST_HEADERS = {
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
'User-Agent': 'Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/27.0.1453.94 Safari/537.36'
}

3)确认要提取的数据,Item项

Item定义你要提取的内容(定义数据结构),比如我提取的内容为电影名和电影描述,我就创建两个变量(每有一个需要提取的内容,就要在items.py中新建一个变量)。Field方法实际上是创建一个Dict,给Dict添加一个Key,暂时不赋值,等待提取数据后再赋值。

下面的Item结构可以表示为:{ 'name':'' ; 'description':'' }

#items.py

import scrapy

class TXItem(scrapy.Item):
name=scrapy.Field()
description=scrapy.Field()

4)写爬虫程序

我们要写的部分是parse方法中的内容,重点在于如何写XPath。

我们要爬取的网页的URL为https://v.qq.com/channel/movie,页面如下:

from scrapy import Request
from scrapy import Spider
from TX.items import TXItem

class TxsSpider(Spider):
name = 'txs'
allowed_domains = ['v.qq.com']
start_urls = ['https://v.qq.com/channel/movie']

def parse(self, response):  
    item=TXItem()  
    movies=response.xpath('//div\[@id="movie\_v3\_new"\]//div\[@class="list\_item "\]')  
    for movie in movies:  
        item\['name'\]=movie.xpath('.//div/a/text()').extract()\[0\]  
        item\['description'\]=movie.xpath('.//div/div/text()').extract()\[0\]  
        yield item

5)、运行爬虫,保存爬取到的数据

scrapy crawl tms -o TX.csv

这一部分代码的解释,请看Scrapy(二)最后一部分