爬虫学习-Scrape Center闯关(spa系列1-3)

2023-10-11 08:40

本文主要是介绍爬虫学习-Scrape Center闯关(spa系列1-3),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

场景

上次写了ssr系列,它主要依靠服务器渲染代码,并且没有什么困难的地方,主要学习的是request请求中的技巧和html页面元素的定位抓取等爬虫基础技巧。这次记录的是spa系列的1-3个,因为这三个是一个电影网站其他只是细节不同,记录起来比较容易点,这个系列的代码数据都通过Ajax加载,页面动态渲染,主要学习的是通过接口获取json数据并处理以及js的逆向分析,难度提升了一大截,这个系列我的目标是爬取首页每个电影的标题,主题,评分以及电影详情页里面的电影剧情

技术

json数据的处理,js逆向分析

关卡

spa1

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

这关主要学习的是通过接口抓取指定的json数据并处理

import json
import urllib3
import requests
import pandas as pdurllib3.disable_warnings()  #去除因为网页没有ssl证书出现的警告
url,title,theme,score,content = [],[],[],[],[]
headers =  {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ''Chrome/87.0.4280.141 Safari/537.36'}
global url_list,title_list,theme_list,score_list,content_list
for i in range(0,10):the_url = "https://spa1.scrape.center/api/movie/?limit=10&offset="+str(i*10)index=requests.get(the_url,headers=headers,verify=False)dict_data = json.loads(index.text)    #将响应的内容转化为jsonfor x in range(0,10):title.append(dict_data['results'][x]['name']+dict_data['results'][x]['alias'])theme.append(str(dict_data['results'][x]['categories']).replace('[','').replace(']','').replace("'",''))score.append(dict_data['results'][x]['score'])
for y in range(1,101):the_url="https://spa1.scrape.center/api/movie/"+str(y)+"/"response=requests.get(the_url,headers=headers,verify=False)dict_data = json.loads(response.text)content.append(dict_data['drama'])bt = {'标题':title,'主题':theme,'评分':score,'剧情介绍':content
}
work = pd.DataFrame(bt)
work.to_csv('work.csv')

spa2

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

分析网站主页token产生的js

通过打开网站主页面,发现接口多了一个token的值
在这里插入图片描述
而且点开电影详情页面,接口不仅多了一个token值,而且在movie/后面多了一串字符串
在这里插入图片描述
先对网站主页的token进行分析,在浏览器调试工具中调试相关js代码,分析token的产生加密方法
因为是网络请求,并且url中包括token,所以通过url断点入手
下断点,刷新请求
在这里插入图片描述
在send函数处断下
在这里插入图片描述
在右侧的栈依次往下看,在onFetchData函数中发现了token的值,取消url断点,在token处设下断点,刷新请求
在这里插入图片描述
object()向后面传入了当前的url,单步进入后面的代码,进入新的代码块,这次可以看到 sha1 函数和 base64 函数,应该是加密函数所在位置了,进行单步调试
在这里插入图片描述

首先可以看到时间戳,然后从arguments中获取了当前的url
在这里插入图片描述

然后进行循环,循环之后将当前url和时间戳放进了r中,
在这里插入图片描述
第10页的网站页面是这样的
在这里插入图片描述
我们可以发现除了时间戳因为时间变化之外,中间的数值也改变了,我们可以看出来中间的数值的规律是:
假设当前页面为n,数值的值为a
a=(n-1)*10

r的值放到SHA1加密函数中进行加密,出来的值为o
在这里插入图片描述

然后将o的值和时间戳t放进base64进行加密,加密后的值为c

然后返回
在这里插入图片描述

可以看到最后token的值就是我们刚才的c值
这样,token的加密方式就已经解出来了,步骤为:

o=sha1(url,(页面数-1)*10,时间戳)=>c=Base64(o,时间戳)=> token=c

转换为代码为:

SHA1加密

    t = int(time.time())     #获取10位整数的时间戳s1 = f'/api/movie,{i*10},{t}'o = hashlib.sha1(s1.encode('utf-8')).hexdigest()     #进行SHA1加密

Base64加密

str_str = str(f"{o},{t}")bytesStr = str_str.encode(encoding='utf-8')b64str = base64.b64encode(bytesStr)   #最后的base64加密b64str = b64str.decode('utf-8')   # 将字节转换为str

b64str就是最后的token的值了,在这个过程中需要注意的是两次加密,共用的是同一个时间戳,并不是一次加密获取一个

分析网站电影详情页的url中的加密

我们刚开始看到在电影详情页面中除了token之外还有一串加密的字符
在这里插入图片描述
我们接下来的目标就是分析token和这串字符的产生方式

url中字符串的加密分析

这串字符是固定的,尝试直接url断点,然后堆栈往下读,到了熟悉的onFetchData函数
在这里插入图片描述
设置断点,清除掉url断点,继续刷新请求单步调试
在这里插入图片描述

可以看到在上面的e变量已经url计算好了,e变量来源于o变量,调整断点,进行断点调试,但是在调试的过程中发现值来源于key参数,但是在调试中并没有找到key的计算来源,只能全局搜索key,看看key是怎么来的
一共9个匹配项,一点点调试可疑的,最后找到

在这里插入图片描述
设置断点,刷新,单步进入

在这里插入图片描述
继续调试
在这里插入图片描述
最后可以看到那串加密过的字符串,加密的方式是通过n这个固定的字符串变量+t,而t则表示的是第几个电影,然后经过base64加密
所以加密的方式为:
Base64(ef34#teuq0btua#(-57w1q5o5–j@98xygimlyfxs*-!i-0-mb+t)

url中token分析

而token和上面的基本一致,但是中间的一些细节可能不一样,在分析过程中,加密方式的大致流程也是先SHA1加密,然后再Base64加密,但是中间的参数却是不一样了
大致流程为:
o=sha1(url,0,时间戳)=>c=Base64(o,时间戳)=> token=c
但是url中在后面加入了刚才的字符串,中间的本来是页数,现在固定为0
在这里插入图片描述

在这里插入图片描述

这部分代码的加密和上面的类似,就不再单独写

最后,所有的加密方式以及对应的代码都已经分析好了

单线程

import base64
import json
import time
import urllib3
import requests
import pandas as pd
import hashliburllib3.disable_warnings()  #去除因为网页没有ssl证书出现的警告
url,title,theme,score,content = [],[],[],[],[]
headers =  {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ''Chrome/87.0.4280.141 Safari/537.36'}
global url_list,title_list,theme_list,score_list,content_list,tdef token1():t = int(time.time())  # 获取10位整数的时间戳s1 = f'/api/movie,{i*10},{t}'o = hashlib.sha1(s1.encode('utf-8')).hexdigest()     #进行SHA1加密str_str = str(f"{o},{t}")bytesStr = str_str.encode(encoding='utf-8')b64str = base64.b64encode(bytesStr)   #最后的base64加密b64str_all = b64str.decode('utf-8')   # 将字节转换为strreturn b64str_alldef token2():a = 'ef34#teuq0btua#(-57w1q5o5--j@98xygimlyfxs*-!i-0-mb' + str(y)bytesStr = a.encode(encoding='utf-8')b64str = base64.b64encode(bytesStr).decode('utf-8')return b64str
def decode2(b64str):t = int(time.time())  # 获取10位整数的时间戳s1 = f'/api/movie/{b64str},0,{t}'o = hashlib.sha1(s1.encode('utf-8')).hexdigest()  # 进行SHA1加密str_str =(f"{o},{t}")bytesStr = str_str.encode(encoding='utf-8')b64str = base64.b64encode(bytesStr)  # 最后的base64加密b64str_all = b64str.decode('utf-8')  # 将字节转换为strreturn b64str_allfor i in range(0,10):the_url = "https://spa2.scrape.center/api/movie/?limit=10&offset="+str(i*10)+"&token="+token1()index=requests.get(the_url,headers=headers,verify=False)dict_data = json.loads(index.text)    #将响应的内容转化为jsonfor x in range(0,10):title.append(dict_data['results'][x]['name']+dict_data['results'][x]['alias'])theme.append(str(dict_data['results'][x]['categories']).replace('[','').replace(']','').replace("'",''))score.append(dict_data['results'][x]['score'])for y in range(i*10+1,i*10+11):the_url = "https://spa2.scrape.center/api/movie/" + token2() + '/?token=' + decode2(token2())response = requests.get(the_url, headers=headers, verify=False)dict_data_1 = json.loads(response.text)content.append(dict_data_1['drama'])bt = {'标题':title,'主题':theme,'评分':score,'剧情介绍':content
}
work = pd.DataFrame(bt)
work.to_csv('work.csv')

多线程

import base64
import datetime
import hashlib
import threading
import time
import pandas as pd
import json
import requests
import urllib3urllib3.disable_warnings()  #去除因为网页没有ssl证书出现的警告
title,theme,score,content = [],[],[],[]
headers =  {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ''Chrome/87.0.4280.141 Safari/537.36'}def token1(i):t = int(time.time())  # 获取10位整数的时间戳s1 = f'/api/movie,{i*10},{t}'o = hashlib.sha1(s1.encode('utf-8')).hexdigest()     #进行SHA1加密str_str = str(f"{o},{t}")bytesStr = str_str.encode(encoding='utf-8')b64str = base64.b64encode(bytesStr)   #最后的base64加密b64str_all = b64str.decode('utf-8')   # 将字节转换为strreturn b64str_alldef token2(y):a = 'ef34#teuq0btua#(-57w1q5o5--j@98xygimlyfxs*-!i-0-mb' + str(y)bytesStr = a.encode(encoding='utf-8')b64str = base64.b64encode(bytesStr).decode('utf-8')return b64str
def decode2(b64str):t = int(time.time())  # 获取10位整数的时间戳s1 = f'/api/movie/{b64str},0,{t}'o = hashlib.sha1(s1.encode('utf-8')).hexdigest()  # 进行SHA1加密str_str =(f"{o},{t}")bytesStr = str_str.encode(encoding='utf-8')b64str = base64.b64encode(bytesStr)  # 最后的base64加密b64str_all = b64str.decode('utf-8')  # 将字节转换为strreturn b64str_alldef index():for i in range(0, 10):the_url = "https://spa2.scrape.center/api/movie/?limit=10&offset=" + str(i * 10) + "&token=" + token1(i)index = requests.get(the_url, headers=headers, verify=False)dict_data = json.loads(index.text)  # 将响应的内容转化为jsonfor x in range(0, 10):title.append(dict_data['results'][x]['name'] + dict_data['results'][x]['alias'])theme.append(str(dict_data['results'][x]['categories']).replace('[', '').replace(']', '').replace("'", ''))score.append(dict_data['results'][x]['score'])return title,theme,scoredef details():for y in range(1,101):the_url = "https://spa2.scrape.center/api/movie/" + token2(y) + '/?token=' + decode2(token2(y))response = requests.get(the_url, headers=headers, verify=False)dict_data_1 = json.loads(response.text)content.append(dict_data_1['drama'])return contentif __name__=='__main__':begin = datetime.datetime.now()t1 = threading.Thread(target=index)t2 = threading.Thread(target=details)t1.start()t2.start()t1.join()t2.join()bt = {'标题': title,'主题': theme,'评分': score,'剧情介绍': content}work = pd.DataFrame(bt)work.to_csv('work.csv')stop = datetime.datetime.now()print(stop-begin)  # 40s

因为想尝试一下单线程与多线程的区别,所以两种都写了一种,但是最后结果有点让人脑袋疼,多线程最后运行的时间和单线程的差不多,而且多次测试中,有时候还会比单线程更慢

spa3

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

这关是没有页码,下拉直接刷新,通过分析我们可以看出来,这个是通过构造api连接,limit控制个数,offset控制从第几个开始,api直接提供了所有的东西,所以直接把第一关的拿过来稍微改改就可以使用
在这里插入图片描述
在这里插入图片描述

总代码

import json
import urllib3
import requests
import pandas as pdurllib3.disable_warnings()  #去除因为网页没有ssl证书出现的警告
title,theme,score,content = [],[],[],[]
headers =  {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) ''Chrome/87.0.4280.141 Safari/537.36'}
global url_list,title_list,theme_list,score_list,content_list
for i in range(0,10):the_url = "https://spa3.scrape.center/api/movie/?limit=10&offset="+str(i*10)index=requests.get(the_url,headers=headers,verify=False)dict_data = json.loads(index.text)    #将响应的内容转化为jsonfor x in range(0,10):title.append(dict_data['results'][x]['name']+dict_data['results'][x]['alias'])theme.append(str(dict_data['results'][x]['categories']).replace('[','').replace(']','').replace("'",''))score.append(dict_data['results'][x]['score'])content.append(dict_data['results'][x]['drama'])
bt = {'标题':title,'主题':theme,'评分':score,'剧情介绍':content
}
work = pd.DataFrame(bt)
work.to_csv('work.csv')

这篇关于爬虫学习-Scrape Center闯关(spa系列1-3)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/186882

相关文章

Java进阶学习之如何开启远程调式

《Java进阶学习之如何开启远程调式》Java开发中的远程调试是一项至关重要的技能,特别是在处理生产环境的问题或者协作开发时,:本文主要介绍Java进阶学习之如何开启远程调式的相关资料,需要的朋友... 目录概述Java远程调试的开启与底层原理开启Java远程调试底层原理JVM参数总结&nbsMbKKXJx

Python爬虫selenium验证之中文识别点选+图片验证码案例(最新推荐)

《Python爬虫selenium验证之中文识别点选+图片验证码案例(最新推荐)》本文介绍了如何使用Python和Selenium结合ddddocr库实现图片验证码的识别和点击功能,感兴趣的朋友一起看... 目录1.获取图片2.目标识别3.背景坐标识别3.1 ddddocr3.2 打码平台4.坐标点击5.图

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl