次级页面抓取及数据拼接

流程复习: headers中的User-Agent,robot.text以及管道都需要在settings中开启

功能介绍: Schedule功能相当于SPOOLing假脱机技术,用作请求网页的排队
Response负责网页解析
结构化存储需要现在item pipelines中定义结构,然后response将数据交给引擎,由引擎将数据转发给item pipelines
实战
数据传递解析.png
次级页面仍然使用Request请求爬取,需要单独创建解析函数,items仅需要新增一个结构。

db 250在创建时已经生成了一个网页解析函数,如下图
image.png
由下面这部分语句控制数据的爬取,yield语句执行爬取,由回调函数将数据传递给整个项目的parse函数,负责对网页的解析。
image.png

解析函数拿到数据逐步解析,主信息不必关心,主要简介页面数据的解析。这里仍然使用xpath解析子页面的网页URL,在执行爬取语句中,通过回调将特定URL传递给新建的解析函数,这里通过meta函数,将info信息传递给items。
image.png
主解析函数已经对子页面的数据进行网页获取,将子网页响应报文传递给子解析页面,在主解析函数的执行访问语句中,已经绑定了info信息;

子网页响应信息使用meta方法,将执行后的数据赋值给变量info ,将info信息使用字典更新的方式传递到items中存储起来。
image.png

由xpath方法对网页解析,解析后的数据被赋值给变量description,然后将数据传递给items管道
image.png

目标数据分析

子网页数据分析,目前来看,第三项数据最为完整,先从网页简介中剪切关键字,打开网页源代码,通过搜索获取网页片段(Ctrl + F)
https://movie.douban.com/subject/1292052/
image.png

从主页面(豆瓣top250)肖申克主界面源代码中,获取子网页的网址

image.png

选择第二块数据,因为之前已经爬取过,有现成资源可以利用
image.png

爬取流程分析

从主页面douban top250中获得子页面的URL,通过此URL访问电影简介,获取电影简介中的数据

代码调试

首先在终端进入项目外文件夹

  1. cd C:\Users\41999\Documents\PycharmProjects\Python\TZSpyder\第五节Scrapy(一)\db

image.png

在终端使用命令,获取主页面douban top 250的响应报文

  1. scrapy shell https://movie.douban.com/top250

使用XPath解析网页数据,并且以子数据项肖申克为例

  1. node_list = response.xpath('//div[@class="info"]')
  2. node_xsk = node_list[0]
  3. node_xsk.xpath('./div/a/span/text()').extract()[0]

image.png

观察网页路径
image.png

获取子页面网址
解析数据和使用方法获取纯净的URL

  1. node_xsk.xpath('./div/a/@href')
  2. node_xsk.xpath('./div/a/@href').extract_first()
  3. node_xsk.xpath('./div/a/@href').extract()[0] # 两种方法均可

image.png
在遍历中添加对详情页URL的获取

image.png

构建网页请求以及网页解析函数(这里需要重新创建一个网页解析函数,对子网页的网页解析不能再使用原有解析函数)

image.png
创建子页面解析函数,注意缩进
image.png

确定详情页数据解析对象的路径

image.png
link_report有且仅有一个
image.png

退出当前调试命令行,重新测试子页面响应数据

  1. fetch('https://movie.douban.com/subject/1292052/')
  2. exit()
  3. scrapy shell https://movie.douban.com/subject/1292052/

image.png

详情页解析测试,获取电影详情介绍

  1. response.xpath('//div[@id="link-report"]')
  2. response.xpath('//div[@id="link-report"]/span/span[@property="v:summary"]')
  3. response.xpath('//div[@id="link-report"]/span/span[@property="v:summary"]').extract()

image.png

详情页数据解析的排坑

肖申克详情页面的路径有两个span, 但是霸王别姬详情页路径有且仅有一个span;

解决措施: 去掉一个span, 之前的div和span后的指定已经确定了路径下唯一的属性
image.png
image.png
image.png

数据对象的提纯

image.png

自行研究出来的问题

霸王别姬的详情页有换行,如果对extract()指定,则获取的数据一定不完整

补充: strip方法隶属于str数据类型,具体描述如下

image.png

测试框架的异步性(管道存储和爬取后数据的存放的执行顺序是乱序的)

对管道增加字段名,便于使用管道的方式保存数据(由于爬虫异步,可以将数据和之前爬取的数据放在一起)

image.png

修改db259下前面数据的管道存储

image.png

修改数据解析

对错误的处理
1, 将中文名字写进了dict
2, 没有将response的返回数据传递给解析函数

数据无需,电影信息和详情页不对应

image.png

image.png

绑定数据,便于表示意义

image.png

在简介解析函数中增加数据结构,存储异步数据(简介)

image.png

备注:
代码一遍又一遍检查,至少六遍,先是对着视频检查,然后去群里下载爬虫文件,最后无计可施,居然数据又冒出来了,我不知道是不是因为IP被封锁,拿不到数据(如果封锁IP是不是可以用浏览器试一试),过了五六个小时时间,下午一点四十结束,到下午六点,我才拿到数据。
看到数据的那一刻激动万分,但是找不到出问题的原因特别痛苦。
刚才准备回去保存控制台的数据,但是已经被覆盖了,非常的遗憾,所以待会儿项目学习完成之后,去学习OS模块,试一试把终端的运行数据持久化保存到文档当中,至少还有寻求没有数据的原因的机会,这俩天老师没上班,等上班了还可以问一下,还好今天直播有录屏,到时候回放一下。

项目过程总结:

1 目标数据 电影信息(名字,导演及主演,评分)+ 电影简介, 简介在次级页面的网页源码中

2 请求流程 访问一级页面 提取电影次级页面URL 访问次级页面URL,从次级数据中提取电影简介

3 存储问题,数据获取和保存的次序是混乱的 需要使用meta传参,将同一部电影的数据绑定在一起

meta解析.png

项目完整代码

  1. import json
  2. import scrapy
  3. from ..items import DbItem # 是一个安全的字典
  4. class Db250Spider(scrapy.Spider): # 继承基础类
  5. name = 'db250' # 爬虫文件名字,必须存在且唯一
  6. # allowed_domains = ['https://moive.douban.com'] # 允许的域名 相当于防火墙, 可以不存在,可以访问任何网站
  7. start_urls = ['https://movie.douban.com/top250'] # 初始URL, 必须存在
  8. page_num = 0
  9. def parse(self, response): # 解析函数,处理响应数据
  10. node_list = response.xpath('//div[@class="info"]')
  11. for node in node_list:
  12. # 电影名字
  13. film_name = node.xpath('./div/a/span/text()').extract()[0]
  14. # 导演信息
  15. director_name = node.xpath("./div/p/text()").extract()[0].strip()
  16. # 评分信息
  17. score = node.xpath('./div/div/span[@property="v:average"]/text()').extract()[0]
  18. # 非管道存储
  19. # item = {}
  20. # item['film_name'] = film_name
  21. # item['director_name'] = director_name
  22. # item['score'] = score
  23. # content = json.dumps(item, ensure_ascii=False)
  24. # f.write(content + "\n")
  25. # 使用管道存储
  26. item_pipe = DbItem() # 创建DbItem对象,当作字典来使用
  27. item_pipe['film_name'] = film_name
  28. item_pipe['director_name'] = director_name
  29. item_pipe['score'] = score
  30. # yield item_pipe # 不能使用return , 不影响代码的执行
  31. # print("电影信息", dict(item_pipe))
  32. # 电影简介
  33. detail_url = node.xpath('./div/a/@href').extract()[0]
  34. yield scrapy.Request(detail_url, callback=self.get_detail, meta={"info": item_pipe})
  35. # 发起新一页的请求
  36. # 构造URL
  37. self.page_num += 1
  38. if self.page_num == 3:
  39. return
  40. page_url = "https://movie.douban.com/top250?start={}&filter=".format(self.page_num * 25)
  41. print(page_url)
  42. yield scrapy.Request(page_url, callback=self.parse)
  43. def get_detail(self, response):
  44. item = DbItem()
  45. # 解析详情页的解析函数
  46. # 1 meta会随着response一起返回 2 通过response.meta接收数据 3 通过update添加到新的item中
  47. info = response.meta["info"]
  48. item.update(info)
  49. # 简介
  50. description = response.xpath('//div[@id="link-report"]//span[@property="v:summary"]/text()').extract()[
  51. 0].strip()
  52. # print('description', description)
  53. item["description"] = description
  54. # 通过管道保存
  55. yield item
  56. '''
  57. 目标数据: 从次级页面网页源代码里获取电影简介数据;
  58. 请求流程: 访问一级页面,提取次级页面的url, 从次级页面抓取电影简介
  59. 数据存储的问题: 数据没有次序,需要使用meta传参,保证同一部电影的信息在一起(导演评分加简介)
  60. '''

Scrapy shell

全部shell命令

  1. [s] Available Scrapy objects:
  2. [s] scrapy scrapy module (contains scrapy.Request, scrapy.Selector, etc) # 模块
  3. [s] crawler <scrapy.crawler.Crawler object at 0x000001BAE3CA41F0> # 爬虫对象
  4. [s] item {} # item对象
  5. [s] request <GET https://www.baidu.com/> # 请求对象
  6. [s] response <200 https://www.baidu.com/> # 响应对象
  7. [s] settings <scrapy.settings.Settings object at 0x000001BAE3CA4310> # 配置文件
  8. [s] spider <DefaultSpider 'default' at 0x1bae41480d0> # Spider文件
  9. [s] Useful shortcuts:
  10. [s] fetch(url[, redirect=True]) Fetch URL and update local objects (by default, redirects are followed) # 通过URL获取response数据
  11. [s] fetch(req) Fetch a scrapy.Request and update local objects # 通过请求对象 获取request
  12. [s] shelp() Shell help (print this help) # 列出命令
  13. [s] view(response) View response in a browser # response 页面 执行快速页面跳转,由本地浏览器查看
  14. 2021-02-14 19:35:51 [asyncio] DEBUG: Using proactor: IocpProactor

image.png

settings数据的读取
先使用如何命令对网页进行解析,然后开始shell命令测试

  1. scrapy shell https://www.baidu.com/

命令如下
image.png

快速切换爬取对象

image.png

查看命令

image.png

快速查看response响应

  1. view(response)

Scrapy 选择器

第一步构建文件

  1. # -*- codeing = utf-8 -*-
  2. # @Time : 2/14/2021 8:04 PM
  3. # @Autor : Caesar
  4. # @File : scrapy_demo.py
  5. # @ Software : PyCharm
  6. from scrapy.selector import Selector
  7. html_str = '''
  8. <div class="info">
  9. <div class="hd">
  10. <a href="https://movie.douban.com/subject/1292052/" class="">
  11. <span class="title">肖申克的救赎</span>
  12. <span class="title">&nbsp;/&nbsp;The Shawshank Redemption</span>
  13. <span class="other">&nbsp;/&nbsp;月黑高飞(港) / 刺激1995(台)</span>
  14. </a>
  15. <span class="playable">[可播放]</span>
  16. </div>
  17. <div class="bd">
  18. <p class="">
  19. 导演: 弗兰克·德拉邦特 Frank Darabont&nbsp;&nbsp;&nbsp;主演: 蒂姆·罗宾斯 Tim Robbins /...<br>
  20. 1994&nbsp;/&nbsp;美国&nbsp;/&nbsp;犯罪 剧情
  21. </p>
  22. <div class="star">
  23. <span class="rating5-t"></span>
  24. <span class="rating_num" property="v:average">9.7</span>
  25. <span property="v:best" content="10.0"></span>
  26. <span>2273737人评价</span>
  27. </div>
  28. <p class="quote">
  29. <span class="inq">希望让人自由。</span>
  30. </p>
  31. '''
  32. # 通过text参数构造对象
  33. selc_text = Selector(text=html_str)
  34. print(selc_text.extract())
  35. print("ok")

1 选择器会主动添加网页结构
image.png

2 用xpath解析数据

image.png

scrapy.Spider

这里的信息整理在思维导图中,由于会在后面用到,所以只是做一个概览

Scrapy(二).xmind

Scrapy(二).png