前言

上一篇文章中爬取了爬虫练习平台的所有 ssr 网站,都是比较简单的,没有反爬措施,这次来爬一下后面的 spa 系列。

环境准备

这里沿用了上篇文章的环境和设置,就不重新搭建环境了。

开始爬取

spa1

spa1 说明如下:

电影数据网站,无反爬,数据通过 Ajax 加载,页面动态渲染,适合 Ajax 分析和动态页面渲染爬取。

还是无反爬,Ajax 加载数据,那么最简单的方法就是打开 Chrome 控制台, 找 xhr 请求。

 
image

一共有两个请求,第一个请求经过了 301 重定向,所以实际接收到数据的是第二个请求。

 
image

查看数据,基本数据都有了,但是没有作者信息,随便点击一个电影,查看详细信息,查找接口。

 
image
 
image

通过 id 和另一个 API 接口获取详细信息,可以找到作者,OK,开始写代码。

其他代码和之前从 HTML 中解析数据的逻辑一致,只是在解析方法上不一样。

class SPA1Spider(scrapy.Spider):
name = "spa1" def start_requests(self):
urls = [
f'https://spa1.scrape.center/api/movie/?limit=10&offset={a}' for a in range(0, 100, 10)
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse) def parse(self, response, **kwargs):
result = response.json()
for a in result['results']:
item = SPA1Item()
item['title'] = a['name'] + a['alias']
item['fraction'] = a['score']
item['country'] = '、'.join(a['regions'])
item['time'] = a['minute']
item['date'] = a['published_at']
yield Request(url=response.urljoin(f'/api/movie/{a["id"]}/'), callback=self.parse_person,
meta={'item': item}) def parse_person(self, response):
result = response.json()
item = response.meta['item']
item['director'] = result['directors'][0]['name']
yield item

因为是通过 API 接口进行遍历的,所以使用总的页数进行循环就可以得到所有的起始 URL。

然后通过读取 JSON 格式的响应,依次循环获取数据,然后进入到详情页获取作者信息,最后返回数据。

完整代码详见https://github.com/libra146/learnscrapy/tree/spa1

spa2

spa2 说明如下:

电影数据网站,无反爬,数据通过 Ajax 加载,数据接口参数加密且有时间限制,适合动态页面渲染爬取或 JavaScript 逆向分析。

和 spa1 相比多了一个数据接口参数加密,先打开控制台看一下多了什么参数。

 
image

接口没有变,只是多了一个 token 参数,接下来来寻找一下这个 token 是怎么生成的。

既然是网络请求,而且 URL 中包含 token,那么分析 token 来源就要从 URL 断点入手。

 
image

下断点,凡是包含 token 字符串的 URL 全部会断下,刷新页面,断点断下,格式化 js 代码,可以看到断下的位置为 send 函数,用来发送请求。

 
image
 
image

按照右侧调用栈依次往下看,发现在 onFetchData 函数附近发现了 token 参数,取消 URL 断点,在 token 的生成位置下断点。重新刷新页面,断点断下。

 
image

选中断下的这行代码,看下 e 的值正好就是 token,单步进入代码查看具体执行了什么。

 
image

看不到具体的信息,应该是在执行 Object(i["a"]),步出,继续单步跟。

 
image

前边的 Object(i["a"]) 执行后应该是一个函数,传入的参数是当前的 URL,单步进入。

 
image

进入到了新的代码块,这次可以看到 sha1 函数和 base64 函数,应该是加密函数所在位置了,单步走一下看都用到了什么信息。

 
image
 
image

首先获取了当前的时间戳信息,然后从 arguments 中获取传入的 URL 参数。

 
image

循环完之后将当前的 URL 和时间戳都压入 r 这个数组中。

 
image
 
image

将数组使用逗号连接起来,计算 sha1 值。

 
image
 
image

将计算结果和时间戳再次使用逗号连接起来,然后通过 base64 编码得到 token。

别急,还没完,获取首页信息的 token 算法已经有了,但是获取详细信息的页面 URL 是这样的:

 
image

token 应该和上一个请求是一样的,但是红框中这一段信息上个请求没有返回,那么只可能是本地生成的,我解码看了下,结果是:ef34#teuq0btua#(-57w1q5o5--j@98xygimlyfxs*-!i-0-mb1 一堆看起来像乱码的东西,还得分析它是怎么来的,既然都用到了 token,那么它在计算完前边这段信息之后肯定会去计算 token 的,所以继续下 token 断点然后往前找就可以了,话不多说继续下断点。

 
image
 
image

仍然是断在了 send 函数上,继续寻找熟悉的 onFetchData 函数,熟悉的 token,取消 URL 断点,在 token 生成这一行下断点,刷新页面。

 
image

可以看到在计算 token 时 URL 已经被计算好了,然后网上看,正好 URL 的值来自于 e 这个变量,e 的值来自于 o 这个函数,OK,重新下断点,刷新页面。

 
image
 
image
 
image

断下,但是看样子 o 函数只是格式化了 key 参数,key 是在之前就计算好了,这里并没有计算 key 的流程,看了下其他的调用栈,应该是 vue 被混淆之后的样子,太乱了,此路不通,换条路走。

既然 key 是提前计算好的,那么全局搜索 key,看看是在哪个函数中给 key 赋值的。

 
image

一共 9 个匹配,挨个找也不多,其他的都排除掉了,找到了这个,熟悉的 router-link(这不是 vue 中生成 a 标签方法嘛),熟悉的 detail(URL 中包含这个字符串),熟悉的 key,应该是它没错了,下断点,刷新页面。

 
image

传入了当前电影的 id 值,因为我点击的是第一个电影,所以它的 id 是 1,单步步入。

 
image

加密方式一目了然,这串乱码竟然是硬编码进去的,哈哈,很坑有木有。所以加密方式就是这串硬编码的字符串加上电影的 id 然后使用 base64 编码即可。

好了, 两个加密方法都分析好了,可以写代码了,完整代码如下:

class SPA2Spider(scrapy.Spider):
name = "spa2" @staticmethod
def sign(url):
t = str(int(time.time()) - random.randint(1, 20))
s = ','.join([url, t])
return base64.b64encode(','.join([sha1(s.encode()).hexdigest(), t]).encode()).decode() def start_requests(self):
urls = [
f'http://spa2.scrape.center/api/movie/?limit=10&offset={a}&token={self.sign("/api/movie")}' for a in
range(0, 100, 10)
]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse) def parse(self, response, **kwargs):
result = response.json()
for a in result['results']:
item = SPA2Item()
item['title'] = a['name'] + a['alias']
item['fraction'] = a['score']
item['country'] = '、'.join(a['regions'])
item['time'] = a['minute']
item['date'] = a['published_at']
s = 'ef34#teuq0btua#(-57w1q5o5--j@98xygimlyfxs*-!i-0-mb'
detail = base64.b64encode((s + str(a["id"])).encode()).decode()
yield Request(url=response.urljoin(f'/api/movie/{detail}/?token={self.sign("/api/movie/" + detail)}'),
callback=self.parse_person,
meta={'item': item}) def parse_person(self, response):
result = response.json()
item = response.meta['item']
item['director'] = result['directors'][0]['name']
yield item

为了防止爬取时时间戳都是一样的,加了一点的随机偏移。

完整代码详见https://github.com/libra146/learnscrapy/tree/spa2

spa3

spa3 说明如下:

电影数据网站,无反爬,数据通过 Ajax 加载,无页码翻页,下拉至底部刷新,适合 Ajax 分析和动态页面渲染爬取。

无加密,无反爬,但是也没有页码,也就是说不能直接构造好所有的起始 URL,要在爬取的过程中去计算,代码如下:

class SPA3Spider(scrapy.Spider):
name = "spa3"
offset = 0
url = 'https://spa3.scrape.center/api/movie/?limit=10&offset=%s' def start_requests(self):
urls = [self.url % self.offset]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse) def parse(self, response, **kwargs):
result = response.json()
for a in result['results']:
item = SPA3Item()
item['title'] = a['name'] + a['alias']
item['fraction'] = a['score']
item['country'] = '、'.join(a['regions'])
item['time'] = a['minute']
item['date'] = a['published_at']
yield Request(url=response.urljoin(f'/api/movie/{a["id"]}/'), callback=self.parse_person,
meta={'item': item})
if self.offset < result['count']:
# 因为每次请求限制10条数据,所以每次将偏移量+10
self.offset += 10
yield Request(url=self.url % self.offset, callback=self.parse) def parse_person(self, response):
result = response.json()
item = response.meta['item']
item['director'] = result['directors'][0]['name']
yield item

由于没有页码,所以起始 URL 的偏移只能从 0 开始,根据 limit 逐渐增大,停止条件就是偏移量大于响应中的 count 字段的值。其他的逻辑和 spa1 是一样的,不多说。

完整代码详见https://github.com/libra146/learnscrapy/tree/spa3

spa4

spa4 说明如下:

新闻网站索引,无反爬,数据通过 Ajax 加载,无页码翻页,适合 Ajax 分析和动态页面渲染抓取以及智能页面提取分析。

spa4 和 spa3 基本上的逻辑是一致的,只是爬取内容不一样。

class SPA4Spider(scrapy.Spider):
name = "spa4"
offset = 0
limit = 100
url = f'https://spa4.scrape.center/api/news/?limit={limit}&offset=%s' def start_requests(self):
urls = [self.url % self.offset]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse) def parse(self, response, **kwargs):
result = response.json()
print(response.url)
for a in result['results']:
item = SPA4Item()
item['code'] = a['code']
item['published_at'] = a['published_at']
item['title'] = a['title']
item['updated_at'] = a['updated_at']
item['url'] = a['url']
item['website'] = a['website']
# 新闻网址都是外部链接,抓取方式都不一样,就不抓取具体信息了
yield item
if int(result['count']) > self.offset:
self.offset += self.limit
yield Request(url=self.url % self.offset, callback=self.parse)

由于 spa4 数据量太大,这里为了学习就不全部爬取了,点到为止。

完整代码详见https://github.com/libra146/learnscrapy/tree/spa4

spa5

spa5 说明如下:

图书网站,无反爬,数据通过 Ajax 加载,有翻页,适合大批量动态页面渲染抓取。

图书网站,需要爬取的内容又不一样了,需要新建一个新的 item,其他的内容和上面的爬取方法是一致的。

class SPA5Spider(scrapy.Spider):
name = "spa5"
offset = 0
limit = 18
url = f'https://spa5.scrape.center/api/book/?limit={limit}&offset=%s' def start_requests(self):
urls = [self.url % self.offset]
for url in urls:
yield scrapy.Request(url=url, callback=self.parse) def parse(self, response, **kwargs):
result = response.json()
print(response.url)
for a in result['results']:
item = SPA5Item()
item['title'] = a['name']
item['author'] = '/'.join(a['authors'] or [])
yield Request(url=f'https://spa5.scrape.center/api/book/{a["id"]}/', callback=self.parse2,
meta={'item': item})
if int(result['count']) > self.offset:
self.offset += self.limit
yield Request(url=self.url % self.offset, callback=self.parse) def parse2(self, response):
item = response.meta['item']
result = response.json()
item['price'] = result['price'] or 0
item['time'] = result['published_at']
item['press'] = result['publisher']
item['page'] = result['page_number']
item['isbm'] = result['isbn']
yield item

先从基本数据中记录下 title 和 author,然后再进入到详细信息页面获取到书本的其他信息,最后存储到数据库。

完整代码详见https://github.com/libra146/learnscrapy/tree/spa5

spa6

spa6 说明如下:

电影数据网站,数据通过 Ajax 加载,数据接口参数加密且有时间限制,源码经过混淆,适合 JavaScript 逆向分析。

经过分析及测试,sp6 和 spa2 貌似是一样的接口,直接使用 spa2 的代码就可以获取到数据,所以就不重复写了。

scrapy学习之爬虫练习平台22的更多相关文章

  1. scrapy学习之爬虫练习平台35

    前言 上一篇文章中爬取了爬虫练习平台的所有 ssr 网站,都是比较简单的,没有反爬措施,这次来爬一下后面的 spa 系列. 环境准备 这里沿用了上篇文章的环境和设置,就不重新搭建环境了. 开始爬取 s ...

  2. python爬虫之Scrapy学习

    在爬虫的路上,学习scrapy是一个必不可少的环节.也许有好多朋友此时此刻也正在接触并学习scrapy,那么很好,我们一起学习.开始接触scrapy的朋友可能会有些疑惑,毕竟是一个框架,上来不知从何学 ...

  3. scrapy学习(完全版)

    scrapy1.6中文文档 scrapy1.6中文文档 scrapy中文文档 Scrapy框架 下载页面 解析页面 并发 深度 安装 scrapy学习教程 如果安装了anconda,可以在anacon ...

  4. 爬虫管理平台以及wordpress本地搭建

    爬虫管理平台以及wordpress本地搭建 学习目标: 各爬虫管理平台了解 scrapydweb gerapy crawlab 各爬虫管理平台的本地搭建 Windows下的wordpress搭建 爬虫 ...

  5. 转载一个不错的Scrapy学习博客笔记

    背景: 最近在学习网络爬虫Scrapy,官网是 http://scrapy.org 官方描述:Scrapy is a fast high-level screen scraping and web c ...

  6. 【Python】【爬虫】如何学习Python爬虫?

    如何学习Python爬虫[入门篇]? 路人甲 1 年前 想写这么一篇文章,但是知乎社区爬虫大神很多,光是整理他们的答案就够我这篇文章的内容了.对于我个人来说我更喜欢那种非常实用的教程,这种教程对于想直 ...

  7. Scrapy:学习笔记(2)——Scrapy项目

    Scrapy:学习笔记(2)——Scrapy项目 1.创建项目 创建一个Scrapy项目,并将其命名为“demo” scrapy startproject demo cd demo 稍等片刻后,Scr ...

  8. scrapy 学习笔记1

    最近一段时间开始研究爬虫,后续陆续更新学习笔记 爬虫,说白了就是获取一个网页的html页面,然后从里面获取你想要的东西,复杂一点的还有: 反爬技术(人家网页不让你爬,爬虫对服务器负载很大) 爬虫框架( ...

  9. Python学习网络爬虫--转

    原文地址:https://github.com/lining0806/PythonSpiderNotes Python学习网络爬虫主要分3个大的版块:抓取,分析,存储 另外,比较常用的爬虫框架Scra ...

随机推荐

  1. kubernetes:用kubeadm管理token(kubernetes 1.18.3)

    一,token的用途: 1,token是node节点用来连接master节点的令牌字串, 它和ca证书的hash值是把一台node节点加入到kubernetes集群时要使用的凭证 2, 通过kubea ...

  2. centos8平台使用iotop监控磁盘io

    一,iotop的作用: iotop是监视磁盘I/O使用状况的top类工具, 可以针对进程和线程统计io的使用情况 说明:刘宏缔的架构森林是一个专注架构的博客,地址:https://www.cnblog ...

  3. Centos6.X 手动升级gcc

    操作环境 CentOS6.5 64bit,gcc原版本为4.4.7,不能支持C++11的特性,所以需要升级 [root@zengxj ~]# wget http://ftp.gnu.org/gnu/g ...

  4. sql server 2008 r2 直接下载地址,可用迅雷下载

    sqlserver 2008 r2 直接下载地址,可用迅雷下载 下载sqlserver 2008 r2 ,微软用了一个下载器,经过从下载器上,将他的地址全部用键盘敲了下来.最终的简体中文版地址如下: ...

  5. 学python,大概要多久?

    都让开!本人文科生,自学Python 2年半,作为一个曾经完全0基础,啥都不懂纯靠自学学会python的文科生,有一些不成熟的小建议可以分享一下. 首先不要觉着编程难,只要你认识26个英文字母,有一点 ...

  6. rpc服务在游戏中的简单运用

    我们最开始做的游戏框架,多数都是client->server->db的模式,但是随着玩家数量的增加,一个server进程就会扛不住,需要多个进程服务于多个玩家.但是给定了不同进程的玩家,有 ...

  7. c++ stl库中的set

    简单说来 set(集合)里的元素 不会有相同元素(也就是说 相同的值不存 )并且 存进去会自动排序  类比sort默认排序从小到大 set排序也是 set/multiset会根据待定的排序准则,自动将 ...

  8. Redis学习笔记(七)——数据结构之有序集合(sorted set)

    一.介绍 Redis有序集合和集合一样都是string类型元素的机会,且不允许重复的成员. 不同的是每个元素都会关联一个double类型的分数.Redis正是通过分数来为集合中的成员进行从小到放大的排 ...

  9. npm的使用说明

    博主是刚开始写项目的前端小白菜,边学边整理,以供后面的小猿参考,共同进步. 首先: npm的官网地址:https://www.npmjs.com Windows 安装包(.msi) 32 位安装包下载 ...

  10. 动态规划之KMP字符匹配算法

    KMP 算法(Knuth-Morris-Pratt 算法)是一个著名的字符串匹配算法,效率很高,但是确实有点复杂. 很多读者抱怨 KMP 算法无法理解,这很正常,想到大学教材上关于 KMP 算法的讲解 ...