Scrapy入门(2):编写spider.py

简介

spider.py: 引用items.py定义的item类,编写爬取程序提取item,最后返回item数据。

引用Spider类

为了创建一个spider,我们需要继承scrapy中的Spider类,并定义相应的属性和方法。

除了Spider这个最基础的类以外,scrapy还包含了CrawlSpider,XMLFeedSpider,CSVFeedSpider,SitemapSpider等几种爬虫类。

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
# 引用Spider类
from scrapy.spiders import Spider
# 引用item.py中的item类
from test0.items import productItem
# 其余需要使用的模块
from scrapy import Request
import json
import re
# 编写爬虫
class freepeopleSpider(Spider):
# name:Spider的名字,必须唯一,用来区别不同的Spider
name = 'freepeople'
# allowed_domains:限定Spider抓取数据时初始URL和后续URL所在的域名
allowed_domains = ["freepeople.com"]
# start_urls:Spider启动时,进行爬取的初始url列表
start_urls = ["https://www.freepeople.com/fp-movement/"]
# parse方法,继承自Spider类,每个初始URL(start_urls)完成下载后返回的response对象将会作为唯一的参数传递给该方法。
# 通过使用parse方法对response对象进行解析,我们可以提取到需要的item数据,或者生成需要进一步处理的URL。
# 以爬取商品信息为例
# 如果初始URL直接就是商品详情页,那么通过parse方法就可以直接提取item数据。但在实际的爬取过程中,我们很少可以直接拿到商品详情页的URL。
# 往往我们的初始URL都是商品列表页,而列表页中虽然包含了商品的部分数据,但肯定不全。所有我们就需要通过parse方法对列表页进行解析,提取商品详情页的URL做进一步的处理。
def parse(self, response):
selector = Selector(response)
# extract返回结果是一个list,所以要先用[0]取出数组内的元素
itemListElement = selector.xpath("//script[@type='application/ld+json']/text()").extract()[0]
itemListElement = json.loads(itemListElement)['itemListElement']
# print(itemListElement)
for item in itemListElement:
# print(item)
yield Request(item['url'],callback=self.parse_item)
# 上述parse方法中,我们针对初始URL的返回结果(response)使用了Selector选择器,从而提取商品详情页的URL。
# 然后针对商品详情页的URL使用了Request函数,并将Request的请求结果丢给了parse_item方法,以便进一步提取商品的详细数据

使用Selector选择器

为了从抓取回来的网页中提取数据,我们需要使用scrapy的Selector选择器,针对Selector选择器使用Xpath语法可以简单快速的提取数据。

Chrome中的XPath Helper拓展程序非常、非常、非常好用!!!

构造选择器

1
2
from scrapy.selector import Selector
selector = Selector(response)

xpath()

通过使用Selector的xpath()方法,传入xpath表达式,提取所需数据。
常用的xpath语法:

  • //div : 选择所有的div标签
  • //div[@type=’name’] : 选择所有type属性等于name的div标签
  • //div[@type] : 提取所有div标签的type属性值
  • //div/text() : 提取所有div标签的文本

extract(),re()

xpath()返回的结果仍然是selector list列表,我们需要将其转化为字符串list列表。
extract():不对提取结果做任何的处理,直接返回字符串列表;
re():针对提取结果传入正则表达式,只返回符合正则规则的字符串列表。

1
2
3
4
5
6
# 以parse方法为例
# 它首先使用Selector方法将response转化为Selector实例,然后使用selector的xpath方法传入xpath表达式,提取相应数据
selector = Selector(response)
# 此处xpath表达式的含义:选择type属性等于'application/ld+json'的<script>标签,然后提取它包含的文本
# 最后通过extract方法将提取到的值转化为字符串list
itemListElement = selector.xpath("/script[@type='application/ld+json']/text()").extract()[0]

进一步提取item数据

在引用Spider类的时候,我们已经定义了一个parse方法,并通过parse方法解析出了商品详情页的URL,并将URL的Request结果(也是response对象)丢给了我们自定义的parse_item方法。那么现在我们就需要通过parse_item方法进一步提取完整的item数据。

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
def parse_item(self, response):
selector = Selector(response)
# 此处将不同颜色的商品视为不同的item,所以定义了一个items列表,用以返回多个item。
items = []
# selector选择器查找当前商品的所有颜色,然后for循环处理
for colourSelector in selector.xpath("//div[@class='swatches']/img"):
# 实例一个productItem()类
item = productItem()
# 提取商品名称
item['itemName'] = selector.xpath("//h1[@itemprop='name']/text()").extract()[0]
# 提取商品描述
item['description'] = selector.xpath("//div[@itemprop='description']//p[2]/text()").extract()[0]
# 提取商品价格
item['price'] = selector.xpath("//h3[@itemprop='price']/text()").extract()[0]
# 提起商品当前颜色的名称
item['colourName'] = colourSelector.xpath("@data-color-name").extract()[0]
# 提起商品当前颜色的颜色代码
color_code = colourSelector.xpath("@data-color-code").extract()[0]
# 提起商品当前颜色的详情页URL
item['sourceWebURL'] = re.sub('\d*$', color_code, response.url)
item['sourceProductId'] = color_code
# 提起商品当前颜色的模特图片
item['imgList'] = colourSelector.xpath("@data-view-code").extract()[0]
item['imgList'] = item['imgList'].split(',')
for i in range(len(item['imgList'])):
item['imgList'][i] = "https://img1.fpassets.com/is/image/FreePeople/"\
+ item['sourceWebSKU'] + "_"\
+ color_code + "_" + str(item['imgList'][i])
# 提取商品的尺寸,及对应的库存
item['havaStockSize'] = []
item['noneStockSize'] = []
sizeList = selector.xpath("//div[@data-color-code='" + color_code + "']/button[@data-product-size]")
for sizeSelect in sizeList:
size = sizeSelect.xpath("@data-product-size").extract()[0]
ifHaveStock = re.search('is-disabled',sizeSelect.xpath("@class").extract()[0])
if ifHaveStock is None:
item['havaStockSize'].append(size)
else:
item['noneStockSize'].append(size)
items.append(item)
# 返回item数据
return items

心得

编写Spider程序最重要的是先梳理好网站结构和规则。
如果说spider语法是死的,那么网站结构和规则就是活的。有的网站是静态网页,有的是动态网页,而且每一个item数据的抓取规则也千差万别,我们需要细心耐心的去研究才能保证抓取过程的方便快速,抓取结果的准确。


That’s all.
Happy writing!