返回介绍

8.3 编码实现

发布于 2024-02-05 21:13:20 字数 5837 浏览 0 评论 0 收藏 0

首先创建一个Scrapy项目,取名为toscrape_book。

$ scrapy startproject toscrape_book

通常,我们不需要手工创建Spider文件以及Spider类,可以使用scrapy genspider<SPIDER_NAME> <DOMAIN>命令生成(根据模板)它们,该命令的两个参数分别是Spider的名字和所要爬取的域(网站):

$ cd toscrape_book
$ scrapy genspider books books.toscrape.com

运行后,scrapy genspider命令创建了文件toscrape_book/spiders/books.py,并在其中创建了一个BooksSpider类,代码如下:

# -*- coding: utf-8 -*-
import scrapy

class BooksSpider(scrapy.Spider):
 name = "books"
 allowed_domains = ["books.toscrape.com"]
 start_urls = ['http://books.toscrape.com/']

 def parse(self, response):
   pass

实现Spider之前,先定义封装书籍信息的Item类,在toscrape_book/items.py中添加如下代码:

class BookItem(scrapy.Item):
 name = scrapy.Field()     # 书名
 price = scrapy.Field()    # 价格
 review_rating = scrapy.Field()  # 评价等级,1~5 星
 review_num = scrapy.Field()  # 评价数量
 upc = scrapy.Field()    # 产品编码
 stock = scrapy.Field()    # 库存量

接下来,按以下5步完成BooksSpider。

步骤 01 继承Spider创建BooksSpider类(已完成)。

步骤 02 为Spider取名(已完成)。

步骤 03 指定起始爬取点(已完成)。

步骤 04 实现书籍列表页面的解析函数。

步骤 05 实现书籍页面的解析函数。

其中前3步已经由scrapy genspider命令帮我们完成,不需做任何修改。

第4步和第5步的工作是实现两个页面解析函数,因为起始爬取点是一个书籍列表页面,我们就将parse方法作为书籍列表页面的解析函数,另外,还需要添加一个parse_book方法作为书籍页面的解析函数,代码如下:

class BooksSpider(scrapy.Spider):
 name = "books"
 allowed_domains = ["books.toscrape.com"]
 start_urls = ['http://books.toscrape.com/']

 # 书籍列表页面的解析函数
 def parse(self, response):
  pass

 # 书籍页面的解析函数
 def parse_book(self, reponse):
  pass

先来完成第4步,实现书籍列表页面的解析函数(parse方法),需要完成以下两个任务:

(1)提取页面中每一个书籍页面的链接,用它们构造Request对象并提交。

(2)提取页面中下一个书籍列表页面的链接,用其构造Request对象并提交。

提取链接的具体细节在页面分析时已经讨论过,实现代码如下:

class BooksSpider(scrapy.Spider):
 name = "books"
 allowed_domains = ["books.toscrape.com"]
 start_urls = ['http://books.toscrape.com/']

 # 书籍列表页面的解析函数
 def parse(self, response):
 # 提取书籍列表页面中每本书的链接
 le = LinkExtractor(restrict_css='article.product_pod h3')
 for link in le.extract_links(response):
  yield scrapy.Request(link.url, callback=self.parse_book)

 # 提取"下一页"的链接
 le = LinkExtractor(restrict_css='ul.pager li.next')
 links = le.extract_links(response)
 if links:
  next_url = links[0].url
  yield scrapy.Request(next_url, callback=self.parse)

# 书籍页面的解析函数
def parse_book(self, response):
 pass

最后完成第5步,实现书籍页面的解析函数(parse_book方法),只需提取书籍信息存入BookItem对象即可。同样,提取书籍信息的细节也在页面分析时讨论过,最终完成代码如下:

import scrapy
from scrapy.linkextractors import LinkExtractor
from ..items import BookItem

class BooksSpider(scrapy.Spider):
 name = "books"
 allowed_domains = ["books.toscrape.com"]
 start_urls = ['http://books.toscrape.com/']

 def parse(self, response):
  le = LinkExtractor(restrict_css='article.product_pod h3')
  for link in le.extract_links(response):
    yield scrapy.Request(link.url, callback=self.parse_book)

  le = LinkExtractor(restrict_css='ul.pager li.next')
  links = le.extract_links(response)
  if links:
    next_url = links[0].url
    yield scrapy.Request(next_url, callback=self.parse)
def parse_book(self, response):
 book = BookItem()
 sel = response.css('div.product_main')
 book['name'] = sel.xpath('./h1/text()').extract_first()
 book['price'] = sel.css('p.price_color::text').extract_first()
 book['review_rating'] = sel.css('p.star-rating::attr(class)')\
          .re_first('star-rating ([A-Za-z]+)')

 sel = response.css('table.table.table-striped')
 book['upc'] = sel.xpath('(.//tr)[1]/td/text()').extract_first()
 book['stock'] = sel.xpath('(.//tr)[last()-1]/td/text()')\
        .re_first('\((\d+) available\)')
 book['review_num'] = sel.xpath('(.//tr)[last()]/td/text()').extract_first()

 yield book

完成代码后,运行爬虫并观察结果:

$ scrapy crawl books -o books.csv --nolog
$ cat -n books.csv
  1  name,stock,price,review_num,review_rating,upc
  2  Scott Pilgrim's Precious Little Life,19,£52.29,0,Five,3b1c02bac2a429e6
  3  It's Only the Himalayas,19,£45.17,0,Two,a22124811bfa8350
  4  Olio,19,£23.88,0,One,feb7cc7701ecf901
  5  Rip it Up and Start Again,19,£35.02,0,Five,a34ba96d4081e6a4

  ... 省略中间输出 ...

  999  Bright Lines,1,£39.07,0,Five,230ac636ea0ea415
 1000  Jurassic Park (Jurassic Park #1),3,£44.97,0,One,a0dd11f6abc421ec
 1001  Into the Wild,3,£56.70,0,Five,a7c3f1010d64799a

从以上结果中看出,我们成功地爬取了网站中1000本书的详细信息,但也有让人不满意的地方,比如csv文件中各列的次序是随机的,看起来比较混乱,可在配置文件settings.py中使用FEED_EXPORT_FIELDS指定各列的次序:

FEED_EXPORT_FIELDS = ['upc', 'name', 'price', 'stock', 'review_rating', 'review_num']

另外,结果中评价等级字段的值是One、Two、Three……这样的单词,而不是阿拉伯数字,阅读起来不是很直观。下面实现一个Item Pipeline,将评价等级字段由单词映射到数字(或许这样简单的需求使用Item Pipeline有点大材小用,主要目的是带领大家复习之前所学的知识)。在pipelines.py中实现BookPipeline,代码如下:

class BookPipeline(object):
 review_rating_map = {
   'One':  1,
   'Two':  2,
   'Three':  3,
   'Four':  4,
   'Five':  5,
 }

 def process_item(self, item, spider):
   rating = item.get('review_rating')
   if rating:
    item['review_rating'] = self.review_rating_map[rating]

   return item

在配置文件settings.py中启用BookPipeline:

ITEM_PIPELINES = {
  'toscrape_book.pipelines.BookPipeline': 300,
}

重新运行爬虫,并观察结果:

$ scrapy crawl books -o books.csv
...
$ cat -n books.csv
  1  upc,name,price,stock,review_rating,review_num
  2  a897fe39b1053632,A Light in the Attic,£51.77,22,3,0
  3  3b1c02bac2a429e6,Scott Pilgrim's Precious Little Life,£52.29,19,5,0
  4  a22124811bfa8350,It's Only the Himalayas,£45.17,19,2,0
  5  feb7cc7701ecf901,Olio,£23.88,19,1,0

  ... 省略中间输出 ...

  999  91eb9605998a7c03,"The Sandman, Vol. 3: Dream Country",£55.55,3,5,0
 1000  f06039c29b5891fa,The Silkworm (Cormoran Strike #2),£23.05,3,5,0
 1001  476c7972e9b41891,The Last Painting of Sara de Vos,£55.55,3,2,0

此时,各字段已按指定次序排列,并且评价等级字段的值是我们所期望的阿拉伯数字。

到此为止,整个项目完成了。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文