Scrapy1

Scrapy爬虫Section1

认识Scrapy爬虫

网络爬虫是指在互联网上自动爬取网站内容信息的程序,也被称作网络蜘蛛或网络机器人。大型的爬虫程序被广泛应用于搜索引擎、数据挖掘等领域,个人用户或企业也可以利用爬虫收集对自身有价值的数据。

网络爬虫的基本执行流程

  • 下载页面

    一个网页的内容本质上就是一个HTML文本,爬取一个网页内容之前,首先要根据网页的URL下载网页。

  • 提取页面数据

    当一个网页(HTML)下载完成后,对页面中的内容进行分析,并提取出我们感兴趣的数据,提取到的数据可以以多种形式保存起来,比如将数据以某种格式(CSV、JSON)写入文件中,或存储到数据库(MySQL、MongoDB)中。

  • 提取页面链接

    通常,我们想要获取的数据并不只在一个页面中,而是分布在多个页面中,这些页面彼此联系,一个页面中可能包含一个或多个到其他页面的链接,提取完当前页面中的数据后,还要**把页面中的某些链接也提取出来**,然后对链接页面进行爬取(循环1-3步骤)。

使用Python可以从头开始写一个爬虫程序,为了避免因制造轮子 而消耗大量时间,在实际应用中我们可以选择使用一些优秀的爬虫框 架,使用框架可以降低开发成本,提高程序质量。所以就有了**Scrapy**的出现。

Scrapy的安装

pip install scrapy

出现图示则代表安装成功。

编写Spider(Scrapy 爬虫程序中最核心的组件)

Scrapy框架结构及工作原理

对**各个组件**的介绍如下图所示

对框架中的**数据流**介绍如图所示

Request和Response是HTTP协议中的术语,即HTTP请求和 HTTP响应,Scrapy框架中定义了相应的Request和Response类,这里的Item代表Spider从页面中爬取的一项数据。

数据在框架中的流动过程

  • 当SPIDER要爬取某URL地址的页面时,需使用该URL构造一个Request对象,提交给ENGINE。

  • Request对象随后进入SCHEDULER按某种算法进行排队,之后的某个时刻SCHEDULER将其出队,送往DOWNLOADER。

  • DOWNLOADER根据Request对象中的URL地址发送一次HTTP请求 到网站服务器,之后用服务器返回的HTTP响应构造出一个Response对象,其中包含页面的HTML文本。

  • Response对象最终会被递送给SPIDER的页面解析函数(构造Request对象时指定)进行处理,页面解析函数从页面中提取数据,封装成Item后提交给ENGINE,Item之后被送往ITEM

    PIPELINES进行处理,最终可能由EXPORTER以某种数据格式写入文件(csv,json);另一方面,页面解析函数还从页面中提取链接(URL),构造出新的 Request对象提交给ENGINE。

Request对象

Request对象用来描述一个HTTP请求,下面是其构造器方法的参数列表:

Request(url[, callback, method=’GET’, headers, body, cookies, meta, encoding=’utf-8’, priority=0, dont_filter=False,errback,flags])

Request参数介绍:

  • url(必选)

    请求页面的url地址,bytes或str类型。

  • callback

    页面解析函数, Callable类型,Request对象请求的页面下载完成后,由该参数指定的页面解析函数被调用。如果未传递该参数,默认**调用Spider的parse**方法。

  • method

    HTTP请求的方法,默认为’GET。

虽然参数很多,但除了url参数外,其他都带有默认值。在构造Request对象时,通常我们只需传递一个url参数或再加一个callback参数,其他使用默认值即可。

>>> request = scrapy.Request(‘http://books.toscrape.com/')

Response对象

Response对象用来描述一个HTTP响应,Response只是一个基类,根据响应内容的不同有如下子类:

  • TextResponse
  • HtmlResponse
  • XmlResponse

当一个页面下载完成时,下载器依据HTTP响应头部中的 Content-Type信息创建某个Response的子类对象。我们通常爬取的网页,其内容是HTML文本,创建的便是HtmlResponse对象,其 中**HtmlResponse和XmlResponse是TextResponse的子类**。实际上,这3个子类只有细微的差别

HtmlResponse对象的属性与方法

Spider开发流程

爬虫开发的逻辑:

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import scrapy
from ..items import BookItem
from scrapy.linkextractors import LinkExtractor
class BooksSpider(scrapy.Spider):
# 爬虫名,每一个爬虫的唯一标识
name = 'books'
# 第 1 章 1.3.4 实现 Spider
# 定义爬虫爬取的起始点,起始点可以是多个,这里只有一个。
#start_urls = ['http://books.toscrape.com/']

# 第 2 章 2.3.3 设定起始爬取点
# 实现 start_resquests 方法代替start_urls类属性
def start_requests(self):
yield scrapy.Request('http://books.toscrape.com/',
callback=self.parse,
headers={'User-Agent':'Mozill/5.0'},
dont_filter=True)

def parse(self, response):
# 提取数据
# 重写
for sel in response.css('article.product_pod'):
book = BookItem() # from ..items import BookItem 类
book['name'] = sel.xpath('./h3/a/@title').extract_first()
book['price'] = sel.css('p.price_color::text').extract_first()
yield book

# 第 1 章 1.3.4 实现 Spider
# 以下代码值直接保存,没有通过 Item 封装数据
# for book in response.css('article.product_pod'):
# name = book.xpath('./h3/a/@title').extract_first()
# price = book.css('p.price_color::text').extract_first()
# yield {
# 'name': name,
# 'price': price
# }

# 第 1 章 1.3.4 实现 Spider
# 直接用 Selector 提取下一页链接 (没有用 LinkExtractor 提取链接)
# 下一页链接在 ul.pager > li.next > a 里
'''
<ul class="pager">
<li class="current"> Page 1 of 50 </li>
<li class="next"><a href="catalogue/page-2.html">next</a></li>
</ul>
#default > div > div > div > div > section > div:nth-child(2) > div > ul.pager > li.next > a
'''
# next_url = response.css('ul.pager li.next a::attr(href)').extract_first()
# if next_url:
# next_url = response.urljoin(next_url)
# yield scrapy.Request(next_url, callback=self.parse)

# 第6章 使用 LinkExtractor 提取链接
# 提取下一页链接重写(用 LinkExtractor提取)
# 说明 from scrapy.linkextractors import LinkExtractor
le = LinkExtractor(restrict_css='ul.pager li.next')
links = le.extract_links(response)
# print(type(links),links)
if links:
# 用links[0]获取的Link对象属性是绝对链接地址,无需要用response.urljoin拼接
next_url = links[0].url
yield scrapy.Request(next_url, callback=self.parse)

继承scrapy.Spider

Scrapy框架提供了一个Spider基类,我们编写的Spider需要继承它

import scrapy

class BooksSpider(scrapy.Spider):

这个Spider基类实现了以下内容:

  • 供Scrapy引擎调用的接口,例如用来创建Spider实例的类方法from_crawler。

● 供用户使用的实用工具函数,例如可以调用log方法将调试信息输出到日志。

● 供用户访问的属性,例如可以通过settings属性访问配置文件中的配置。

为Spider命名

在一个Scrapy项目中可以实现多个Spider,每个Spider需要有一个能够区分彼此的唯一标识,Spider的类属性name便是这个唯一标识。

1
2
3
class BooksSpider(scrapy.Spider): 
name = "books"
...

执行scrapy crawl命令时就用到了这个标识,告诉Scrapy使用哪个Spider进行爬取。

设定起始爬取点

Spider必然要从某个或某些页面开始爬取,我们称这些页面为**起始爬取点**,可以通过类属性start_urls来设定起始爬取点:

1
2
3
4
class BooksSpider(scrapy.Spider): 
...
start_urls = ['http://books.toscrape.com/']
...

start_urls通常被实现成一个列表,其中放入所有起始爬取点的url。

请求页 面下载一定要提交Request对象,这时候,我们仅仅定义了URL列表,Spider基类的start_requests方法帮助我们基于URL构造并提交了Request对象。

1
2
3
4
5
6
7
8
9
class Spider(object_ref):
...
def start_requests(self):
for url in self.start_urls:
yield self.make_requests_from_url(url)
def make_requests_from_url(self, url):
return Request(url, dont_filter=True)
def parse(self, response):
raise NotImplementedError ..

由于起始爬取点的下载请求是由引擎调用Spider对象的 start_requests方法产生的,因此我们也可以在BooksSpider中实现start_requests方法(**覆盖基类Spider的start_requests方法**),直接构造并提交起始爬取点的Request对象。在某些场景下使用这种方式更加灵活,例如有时想为Request添加特定的HTTP请求头部,或想为Request指定特定的页面解析函数

实现页面解析函数

页面解析函数也就是构造Request对象时**通过callback参数指定** 的回调函数(或默认的parse方法)。页面解析函数是实现Spider中最核心的部分,它需要完成以下两项工作:

一个页面中可能包含多项数据以及多个链接,因此页面解析函数被要求返回一个可迭代对象(通常被实现成一个生成器函数),每次迭代返回一项数据(Item或字典)或一个Request对象。

谢谢你的支持哦,继续加油.