爬虫练习(猫眼电影解密)

问题

        随便拿一篇电影做样例。我们发现猫眼的页面数据在预览窗口中全是小方框。在当我们拿到源码以后,数据全是加密后的。所以我们需要想办法破解加密,拿到数据。

破解过程 

        1.源码获取问题与破解

        分析

        在我们刚刚请求url的时候是可以得到数据的,但是过了一段时间后就无法获得数据。虽然状态码为200,但是却没有返回页面源码

一般这种应该是和时间戳有关系,在查看请求负载的时候我们发送,浏览器向这个url不仅发送了时间戳还有一个signKey的密钥。时间戳可以很容易得到,主要问题是如何获得signKey。

        全局搜索signKey,我们发现一段js代码,它的返回值就是我们请求负载的内容。所以需要想办法还原这段js代码。

        分析后发现:

  • d:获取当前时间的函数
  • r:随机数取整
  • c:内容如下method=GET&timeStamp=1725264890773&User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0&index=8&channelId=40011&sVersion=1
    • 可以发现就是多个信息进行拼接(时间戳+User-Agent+index+channelId+sVersion)。
  • f:固定为&key=A013F70DB97834C0A5492378BD76C53A

        分析图片如下:

        同时我们还发现signKey是通过MD5加密(c+f)后得到的。因为1经过MD5加密后得到的内容就是c4ca4238a0b923820dcc509a6f75849b,所以我们可以猜测(0,a[i(_0x140e("0xe4"))])('c+f')就是一个MD5的加密。

         js编写与调用

        有了以上分析后,我们就可以拿页面原始的js代码进行适当的改动。修改后的js代码如下,我们直接返回网页负载需要的params。

        添加首页cookie

        在完成上面步骤后,我们调用js,虽然得到了params,但是还是无法获得到页面的源代码,这可能和cookie有关系,所以我们创建一个session,通过访问首页来保存首页的cookie,然后再来访问这个url看看结果。

        我们发现浏览器请求了两次https://www.maoyan.com/,且第一次存在302跳转,跳转到https://www.maoyan.com/,所以是请求了两次。在python代码中,我们只需要请求有302跳转的链接即可,因为程序会自动进行第二次跳转。

        添加cookie后,使用python程序调用js代码返回params,使用js生成的params去访问url地址运行结果如下:

        2.字体破解

        字体图片下载

        在拿到页面源码以后,我们需要对数字进行获取。直接在返回的源码中搜索,获取.woff文件。得到url://s3plus.meituan.net/v1/mss_73a511b8f91f43d0bdae92584ea6330b/font/e3dfe524.woff,因为每一次请求得到的源码中,woff文件的链接都不同,所以我们需要使用数据提取手段,提取每一次请求得到的woff文件链接并下载保存下来。

        下载并保存woff文件,使用python代码识别woff文件,并保存为图片,识别代码如下,之后会整合到源码中:

from fontTools.ttLib import TTFont
from reportlab.graphics.shapes import Drawing, Path, Group
from reportlab.graphics import renderPM
from reportlab.lib import colors
from reportlab.graphics.shapes import Pathclass ReportLabPen(BasePen):def __init__(self, glyphSet, path=None):BasePen.__init__(self, glyphSet)if path is None:path = Path()self.path = pathdef _moveTo(self, p):(x, y) = pself.path.moveTo(x, y)def _lineTo(self, p):(x, y) = pself.path.lineTo(x, y)def _curveToOne(self, p1, p2, p3):(x1, y1) = p1(x2, y2) = p2(x3, y3) = p3self.path.curveTo(x1, y1, x2, y2, x3, y3)def closePath(self):self.path.closePath()def ttfToImage(fontName, imagePath, fmt="png"):font = TTFont(fontName)  # 打开 WOFF 字体文件gs = font.getGlyphSet()glyphNames = font.getGlyphNames()[1:]  # 排除第一个 .notdef 字形for i in glyphNames:g = gs[i]  # 获取当前字形的 Glyph 对象pen = ReportLabPen(gs, Path(fillcolor=colors.red, strokeWidth=1))  # 创建 ReportLabPen 对象,并设置相关参数g.draw(pen)  # 将当前字形通过 pen 绘制到 path 对象上# 字形的宽度和高度w, h = g.width, g.width + 300  g = Group(pen.path)g.translate(0, 100)  # 将图形向下移动 100 个像素d = Drawing(w, h)  # 创建 Drawing 对象,设置宽度和高度d.add(g)  # 将 Group 对象添加到 Drawing 对象中# 定义输出图片路径和文件名imageFile = f"{imagePath}/{i}.{fmt}"# 将 Drawing 对象渲染成图像文件并保存renderPM.drawToFile(d, imageFile, fmt)# 示例用法:将 `mao.woff` 字体文件的字形保存为图像
ttfToImage(fontName="mao.woff", imagePath='images')

        识别结果如下:

 

         识别图片

        识别代码如下,之后会整合到源码中:

import os
import ddddocr  # 导入 ddddocr 库def orc():# 创建一个 ddddocr 的 OCR 对象ocr = ddddocr.DdddOcr()dicts = {}  # 初始化一个空字典,用于存储识别结果lists = os.listdir('./images')  # 获取 images 目录下的所有文件列表# 遍历每个图片文件for imgs in lists:# 以二进制模式读取图片文件with open('./images/' + imgs, 'rb') as f:img_bytes = f.read()# 使用 OCR 对象的 classification 方法识别图片内容res = ocr.classification(img_bytes)# 输出文件名中提取的 Unicode 代码print(222222222222222222, imgs[3:-4])try:# 将文件名中的 Unicode 代码转换为字符,并将识别结果存入字典dicts[eval('u\'\\u' + imgs[3:-4].lower() + '\'')] = resexcept:# 如果转换或存储过程中出错,则跳过pass# 打印当前的字典内容print(dicts)# 调用 orc 函数
orc()

        字典输出结果如下:

字典替换

        拿到页面加密的源码,然后根据指点的key来替换掉对应的数字

        替换后的数字与原始页面一样

源码

import requests
import execjs
import re
import shutil
import os
import ddddocr
from fontTools.pens.basePen import BasePen
from fontTools.ttLib import TTFont
from reportlab.graphics.shapes import Drawing, Path, Group
from reportlab.graphics import renderPM
from reportlab.lib import colors
from reportlab.graphics.shapes import Pathclass ReportLabPen(BasePen):def __init__(self, glyphSet, path=None):BasePen.__init__(self, glyphSet)if path is None:path = Path()self.path = pathdef _moveTo(self, p):(x, y) = pself.path.moveTo(x, y)def _lineTo(self, p):(x, y) = pself.path.lineTo(x, y)def _curveToOne(self, p1, p2, p3):(x1, y1) = p1(x2, y2) = p2(x3, y3) = p3self.path.curveTo(x1, y1, x2, y2, x3, y3)def closePath(self):self.path.closePath()def ttfToImage(fontName, imagePath, fmt="png"):font = TTFont(fontName)  # 打开 WOFF 字体文件gs = font.getGlyphSet()glyphNames = font.getGlyphNames()[1:]  # 排除第一个 .notdef 字形for i in glyphNames:g = gs[i]  # 获取当前字形的 Glyph 对象pen = ReportLabPen(gs, Path(fillcolor=colors.red, strokeWidth=1))  # 创建 ReportLabPen 对象,并设置相关参数g.draw(pen)  # 将当前字形通过 pen 绘制到 path 对象上# 字形的宽度和高度w, h = g.width, g.width + 300g = Group(pen.path)g.translate(0, 100)  # 将图形向下移动 100 个像素d = Drawing(w, h)  # 创建 Drawing 对象,设置宽度和高度d.add(g)  # 将 Group 对象添加到 Drawing 对象中# 定义输出图片路径和文件名imageFile = f"{imagePath}/{i}.{fmt}"# 将 Drawing 对象渲染成图像文件并保存renderPM.drawToFile(d, imageFile, fmt)def download_woff():with open('猫眼.js','r',encoding='utf-8') as f:ctx = execjs.compile(f.read())headers_home = {"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7","Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6","Cache-Control": "max-age=0","Connection": "keep-alive","Sec-Fetch-Dest": "document","Sec-Fetch-Mode": "navigate","Sec-Fetch-Site": "none","Sec-Fetch-User": "?1","Upgrade-Insecure-Requests": "1","User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0","sec-ch-ua": "\"Chromium\";v=\"128\", \"Not;A=Brand\";v=\"24\", \"Microsoft Edge\";v=\"128\"","sec-ch-ua-mobile": "?0","sec-ch-ua-platform": "\"Windows\""}cookies_home = {"_lxsdk_s": "191b2c23b90-602-526-0ba%7C%7C1"}url = "https://www.maoyan.com/"s = requests.session()# 访问首页,保存cookier = s.get(url, headers=headers_home, cookies=cookies_home)headers = {"Accept": "*/*","Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6","Connection": "keep-alive","Referer": "https://www.maoyan.com/films/1464004","Sec-Fetch-Dest": "empty","Sec-Fetch-Mode": "cors","Sec-Fetch-Site": "same-origin","User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0","X-Requested-With": "XMLHttpRequest","sec-ch-ua": "\"Chromium\";v=\"128\", \"Not;A=Brand\";v=\"24\", \"Microsoft Edge\";v=\"128\"","sec-ch-ua-mobile": "?0","sec-ch-ua-platform": "\"Windows\""}url = "https://www.maoyan.com/ajax/films/1464004"params = ctx.call("get_params")response = s.get(url, headers=headers, params=params).text# 保存woffwoff_url = "https:" + re.findall(r',url.*?woff', response)[0].split('"')[1]woff_res = s.get(woff_url).contentwith open('mao.woff', 'wb') as f:f.write(woff_res)f.close()result = re.findall('<span class="stonefont">(.*?)</span>', response)return resultdef clear_folder(folder_path):# 确保指定路径是一个文件夹if os.path.isdir(folder_path):# 遍历文件夹中的所有文件和子文件夹for filename in os.listdir(folder_path):file_path = os.path.join(folder_path, filename)try:# 如果是文件则删除if os.path.isfile(file_path) or os.path.islink(file_path):os.unlink(file_path)# 如果是文件夹则删除整个文件夹elif os.path.isdir(file_path):shutil.rmtree(file_path)except Exception as e:print(f"删除 {file_path} 时出错: {e}")print("删除完成")def orc():# 创建一个 ddddocr 的 OCR 对象ocr = ddddocr.DdddOcr()dicts = {}  # 初始化一个空字典,用于存储识别结果lists = os.listdir('./images')  # 获取 images 目录下的所有文件列表# 遍历每个图片文件for imgs in lists:# 以二进制模式读取图片文件with open('./images/' + imgs, 'rb') as f:img_bytes = f.read()# 使用 OCR 对象的 classification 方法识别图片内容res = ocr.classification(img_bytes)# 输出文件名中提取的 Unicode 代码print(222222222222222222, imgs[3:-4])try:# 将文件名中的 Unicode 代码转换为字符,并将识别结果存入字典dicts[eval('u\'\\u' + imgs[3:-4].lower() + '\'')] = resexcept:# 如果转换或存储过程中出错,则跳过pass# 返回字典内容return dictsif __name__ == '__main__':data = download_woff()# 指定要清空的文件夹路径folder_path = './images'clear_folder(folder_path)# 转换 TTF 字体并将字形转换为 PNG 图片ttfToImage(fontName="mao.woff", imagePath='images')# 使用ocr识别图片,返回字典res = orc()print(data)print(res)# 遍历字典并将识别结果输出for i in data:# 首先去掉所有的 &#x 和 ;cleaned_str = i.replace('&#x', '').replace(';', '')# 然后进行字符替换for key, value in res.items():cleaned_str = cleaned_str.replace(key.encode('unicode_escape').decode('ascii').replace('\\u', ''), value)print(cleaned_str)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/416572.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Shadow Dom 是什么

概念 官方&#xff1a;https://developer.mozilla.org/zh-CN/docs/Web/API/Web_components/Using_shadow_DOM 核心&#xff1a;影子 DOM&#xff08;Shadow DOM&#xff09;允许你将一个 DOM 树附加到一个元素上&#xff0c;并且使该树的内部对于在页面中运行的 JavaScript 和…

Proxyless的多活流量和微服务治理

1. 引言 1.1 项目的背景及意义 在当今的微服务架构中&#xff0c;应用程序通常被拆分成多个独立的服务&#xff0c;这些服务通过网络进行通信。这种架构的优势在于可以提高系统的可扩展性和灵活性&#xff0c;但也带来了新的挑战&#xff0c;比如&#xff1a; 服务间通信的复…

合碳智能 × Milvus:探索化学合成新境界——逆合成路线设计

合碳智能&#xff08;C12.ai&#xff09;成立于2022年&#xff0c;致力于运用AI和具身智能技术&#xff0c;为药物研发实验室提供新一代智能化解决方案&#xff0c;推动实验室从自动化迈向智能化&#xff0c;突破传统实验模式与人员的依赖&#xff0c;解决效率和成本的瓶颈&…

电脑错误mfc140.dll丢失怎么办?mfc140.dll丢失如何修复?

在使用基于Microsoft Visual Studio 2015开发的应用程序时&#xff0c;可能会遇到个别组件影响整体功能的情况&#xff0c;其中“mfc140.dll丢失”错误就是常见的一个技术障碍。这个DLL文件属于Microsoft Foundation Class (MFC) Library&#xff0c;它对Windows应用程序的运行…

MySQL record

更改密码&#xff1a; alter user rootlocalhost identified with mysql_native_password by ‘123456’; 注意&#xff1a; 在命令行方式下&#xff0c;每条MySQL的命令都是以分号结尾的&#xff0c;如果不加分号&#xff0c;MySQL会继续等待用户输入命令&#xff0c;直到MyS…

【C++】STL—vector的使用

目录 前言vector的常见构造迭代器的使用vector空间增长问题vector的增删查改尾插和尾删findinsert和erase 前言 vector是表示可变大小数组的序列容器。它就像数组一样&#xff0c;采用连续的存储空间来存储元素&#xff0c;且它的大小可以动态改变。并且vector在访问元素时也非…

AI语音机器人:通过 Azure Speech 实现类人类的交互

语音对话的重要性 在竞争日益激烈的客户互动领域&#xff0c;人工智能语音对话正成为重中之重。随着数字参与者的崛起&#xff0c;组织认识到语音机器人的强大力量&#xff0c;它是一种自然而直观的沟通方式&#xff0c;可以提供类似人类的体验&#xff0c;深度吸引用户&#…

jmeter中上传文件接口,当文件名为中文时出现乱码

1、在jmeter5.1.1中配置上传文件接口&#xff0c;当上传的文件为中文名称时&#xff0c;文件名称会显示乱码如图 2、解决方案是需要更换jmeter/lib/ext文件中的ApacheJMeter_http.jar 包 3、更换jar包经过多次实验&#xff0c;发现有的上传文件接口中文名正常&#xff0c;但是有…

负载均衡--资源申请说明(三)

1.负载方式&#xff1a;分为四层负载和七层负载 2.负载协议&#xff1a;四层负载为TCP和UDP&#xff0c;七层负载为HTTP和HTTPS 4.负载端口&#xff1a;填写虚地址的端口&#xff08;一般与后端服务端口保持一致&#xff09; 5.真实服务IP&#xff1a;指被负载的后台真实服务…

从零开始:理解并实践Prompt Flow

在人工智能领域中&#xff0c;代码先行与模型紧密结合的Prompt Flow逐渐成为开发者关注的新焦点。今天&#xff0c;我们将共同探索如何快速启动Prompt Flow项目&#xff0c;深入了解流&#xff08;flow&#xff09;的概念&#xff0c;并学习如何使用VS Code Extension或YAML来编…

基于Python的机器学习系列(26):PyTorch中的梯度计算

在本篇中&#xff0c;我们将探讨PyTorch的autograd功能&#xff0c;它为张量操作提供自动微分。我们将学习如何使用torch.autograd工具计算梯度并进行反向传播。 自动微分&#xff08;Autograd&#xff09; PyTorch的autograd包自动计算张量的梯度。当一个张量的.requires_gra…

C++和Python混合编程——C++调用Python入门

大纲 代码结构初始化 Python 解释器获取 GIL为什么需要 GIL&#xff1f;GIL 的影响 导入 Python 模块并执行代码释放 GIL终止 Python 解释器 完整代码编译执行结果项目地址 在《C和Python混合编程——Python调用C入门》一文中&#xff0c;我们熟悉了Python调用C编译的动态库的方…

集成电路学习:什么是PWM脉冲宽度调制

一、PWM&#xff1a;脉冲宽度调制 PWM&#xff0c;全称为脉冲宽度调制&#xff08;Pulse Width Modulation&#xff09;&#xff0c;是一种通过调整脉冲信号的宽度来控制电路输出的技术。在PWM中&#xff0c;信号的输出由一系列等幅值的脉冲组成&#xff0c;这些脉冲的宽度根据…

上海亚商投顾:深成指、创业板指均涨超1%,华为产业链反复活跃

上海亚商投顾前言&#xff1a;无惧大盘涨跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 一.市场情绪 大小指数昨日走势分化&#xff0c;沪指全天震荡调整&#xff0c;2800点失而复得&#xff0c;深成指、创业板指…

golang

一.go执行流程 4&#xff09;go run运行时间明显要比第一种时间长一点 【3】编译后的文件可以替换名称 二.语法注意 1.源文件以“go”为扩展名 2.程序的执行入口是main&#xff08;&#xff09;函数。 3.严格区分大小写 4.方法由一条条语句构成&#xff0c;每个语句后不需…

Docker安全

Docker安全 和本地共享资源&#xff0c;会有安全性问题 1. Cgroups 对资源的上限进行控制 /sys/devices/system1.1 cpu资源限制 # 进行cpu控制&#xff0c;只使用20%的资源 docker run -it --rm --cpu-period 100000 --cpu-quota 20000 ubuntu# 测试cpu使用 dd if/dev/zer…

【题目全解】蓝桥杯24省赛C++中高级组题干题解

本帖只提供六道编程题的解题思路&#xff0c;部分题目并不提供实际的代码&#xff08;因为我赛时忘记把代码截图下来了&#xff09;。 T1 - 看书 题干描述&#xff1a; 一本书共 n n n 页&#xff0c;小明计划第一天看 x x x 页&#xff0c;此后每一天都要比前一天多看 y y…

【面试八股总结】MySQL 锁:全局锁、表级锁、行级锁

1. 全局锁 顾名思义&#xff0c;全局锁就是对整个数据库实例加锁。 MySQL 提供了⼀个加全局读锁的方法&#xff1a; flush tables with read lock 释放全局锁&#xff0c;执行命令&#xff1a; unlock tables 需要让整个库处于只读状态的时候&#xff0c;可以使用全局锁命…

鸿蒙(API 12 Beta6版)图形【NativeImage开发指导 (C/C++)】方舟2D图形服务

场景介绍 NativeImage是提供Surface关联OpenGL外部纹理的模块&#xff0c;表示图形队列的消费者端。开发者可以通过NativeImage接口接收和使用Buffer&#xff0c;并将Buffer关联输出到OpenGL外部纹理。 针对NativeImage&#xff0c;常见的开发场景如下&#xff1a; 通过Nati…

linux系统中USB模块鼠标驱动实现

各位开发者大家好,今天主要给大家分享一下,Linux系统中使用libusb的方法以及鼠标驱动实现。 第一:libusb概述 参考网址:* libusb GIT仓库:https://github.com/libusb/libusb.git * libusb 官网:https://libusb.info/ * libusb API接口:https://libusb.sourceforge.io/…