栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

python爬虫初步学习

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

python爬虫初步学习

文章目录
  • 一、爬虫是什么?
  • 二、使用步骤
    • 1.引入库
    • 2.分析网站
    • 3.扣js代码
    • 3.访问链接,提取数据,下载文件
  • 总结


一、爬虫是什么?

网络爬虫(又称为网页蜘蛛,网络机器人,在FOAF社区中间,更经常的称为网页追逐者),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁、自动索引、模拟程序或者蠕虫。


二、使用步骤 1.引入库
import execjs
import os
import requests
import json
from prettytable import PrettyTable

execjs是python的一个可以执行js代码的类库
os库用来实现音乐文件的下载
requests和json库用来获得并解析请求的数据
prettytable 用来生成美观的表格


2.分析网站

打开开发者工具随便点开一首音乐播放


可以正常播放,接下来就是分析并构造这个得到的链接

https://xxxxxx.xxxxxx.com/71_53_T10038957307_128_4_1_0_sdk-cpm/cn/0311/M00/09/2A/ChAKDF1svm6AHYSzAD6g01xj8aU721.mp3?xcode=eee00a33db9116c68c5c93c960b8f1d427bd421

首先,这个链接特别复杂,还有路径也是,唯一一个突破口就是后面的参数xcode,所以考虑在开发者工具里搜索xcode字段

如图,得到六个结果,但是都不是我们要的xcode,有wxcodeVisible,wxcode等类型的含xcode字符串的变量,在前端文件里没有搜索到,我们可以考虑这个链接是否会包含在响应体里,然后我就在network里搜索xcode

果然被我找到了,在这个响应体里包含了一首歌全部的信息,并且这个音乐文件的链接也在其中
然后就该想想怎么构造这个链接了

https://XXXXX.XXXXX.com/v1/song/tracklink?sign=86c3590d5397c7fd770ef967fa2b4bc9&appid=16073360&TSID=T10038957304×tamp=1630377809

这就是刚刚那个得到xcode信息的请求头,我们可以看到这一共四个参数,sign,appid,TSID,timestamp,timestamp是时间戳,TSID是每首歌曲的ID,appid是不变的,应该是这网站的ID,只有sign最可疑,很明显,86c3590d5397c7fd770ef967fa2b4bc9这串32位的16进制数是通过md5加密方式得到的,所以重复刚刚的步骤,在前端文件里搜索sign。

但是仔细看,在六个匹配结果里面有2个sign: md5(r += secret),分别在37787行和40395行,经过我打断点调试发现,这两处加密的sign分别对应获取音乐文件详情(里面包含音乐文件链接)时的sign和用一个关键字搜索来得到后端返回的音乐列表的请求的参数sign,两个md5加密对应两个不同的sign创建方式
同学们可能看蒙了,现在的情况是这样的:有两个sign的加密方式
这是有xcode的链接

https://XXXXX.XXXXX.com/v1/song/tracklink?sign=86c3590d5397c7fd770ef967fa2b4bc9&appid=16073360&TSID=T10038957304×tamp=1630377809

这是通过搜索关键字得到音乐列表的一个链接,appid有了,时间戳在代码里创建,如果有TSID就能构建这样的链接

https://XXXXX.XXXXX.com/v1/search?sign=ebb28a01cc860659c1efb5da8332ba12&word=%E8%AE%B8%E5%B5%A9&type=1&pageSize=20&appid=16073360×tamp=1630380738

word就是要传的关键字,type等于1是固定的(意思是要后端给我有关的音乐),估计是一个请求类型吧,pageSize是我们想要的音乐数量,appid是固定的,时间戳也是代码里创建,所以我们先构建这个链接得到TSID,再用TSID就能构建第一个链接。

很明显,后端就是根据其他参数来验证sign,符合前后端统一的检验规则就能给你返回正确数据,否则就返回错误信息。


我现在想要这样一个过程,即先在我的python程序里传入一个关键字,和搜索范围(多少首音乐)先得到音乐列表,再,选择序号,下载对应的音乐,两者联系起来的关键就是TSID,而在第二个链接得到的json数据里,是有TSID的,如下图。

一个TSID对应一首歌,得到TSID就可以用它来构造第一个请求得到包含Xcode链接的url,也就是音乐文件,这是经过许多次开发者工具里调试js代码得到的结论。

那就来构建第二个链接吧,sign怎么得到呢,回到刚刚的问题,我已经得到md5的js代码了,前后端校验sign的规则是一样的,那我只有传参给js,得到sign的返回值就可以了,可是我太会前端js呀,而且我也根本不懂md5加密方式怎么办?没事,扣js代码

可以看到时间戳的创建方式:var t = Math.floor(Date.now() / 1e3);
传入怎样的参数:{TSID: ‘T10044518961’, appid: 16073360}
得到怎样的结果:return { sign: md5(r += secret), timestamp: t,md5: md5},这里有我们需要的时间戳和sign,取出来构造链接就可以了。


那我在python程序里怎么用js代码呀? 这时execjs库就起到作用了,众所周知,js能在node环境里运行,所以我们就可以用python创建一个node环境,加载这个js文件就可以了,python代码如下所示:
def getsonglist(name,number):
    # url = "https://xxxxx.xxxxx.com/v1/search?sign={}&word={}&type=1&pageSize=20&appid=16073360×tamp={}".format()
    ctx = execjs.compile(open('songlist.js', encoding='utf-8').read())
    funcName1 = "createSign({word: " + name + ", type: 1, pageSize: "+number+", appid: 16073360}).sign"
    funcName2 = "createSign({word: " + name + ", type: 1, pageSize: "+number+", appid: 16073360}).timestamp"
    sign = ctx.eval(funcName1)
    timestamp = ctx.eval(funcName2)
    # print(sign)
    # print(timestamp)
    name = eval(name)
    url = "https://xxxxx.xxxxx.com/v1/search?sign=" + sign + "&word=" + name + "&type=1&pageSize="+number+"&appid=16073360×tamp=" + str(
        timestamp)
    return url

另一个sign的md5加密也在这展示一下:

传入参数{word: ‘张雷’, type: 1, pageSize: 20, appid: 16073360}也能得到sign和时间戳。这样就能构建第二种链接了。其中pageSize是默认的20,我们可以自己定义。

3.扣js代码

还有,扣js代码要考虑到函数之间的依赖关系,下面就讲讲我是怎样扣js代码的:
其实我是不太懂js的,要我写,我肯定写不来这上千行js代码,折磨的我肯定不会全要,我只要能实现sign,和时间戳创建的代码就可以了,并且能返回到我的python程序中,整个过程,我不需要知道它是怎样实现的,就相当于,有一个函数,给他什么样的参数,它给我什么值,就是这样。

为此,我先准备一个参数:{word: ‘张雷’, type: 1, pageSize: 20, appid: 16073360}就用这个,

调用creatSign函数就可以了,以上是开发者工具控制台键入,我们先准备一个js代码调试工具,我用的是鬼鬼js调试工具,我先把creatSign函数放进去

如图,仅仅有creatSign函数是不够的,现在是我们的新环境里,不是在开发者工具里,所以还需要扣md5加密函数,md5加密函数又依赖createCommonjsModule函数,继续扣

secret没有定义,这边建议把全部js代码拷贝到自己的编辑器里,方便查找和查看,可以找到藏在js代码里的secret是写死的

secret = "0b50b02fd0d73a9c4c8c3a781c30845f"


成功得到sign,这边我把返回参数改了一下,只返回sign,返回对象的话,我这lj调试工具报错了,我也不知道怎么回事,但是放我pycharm里可以返回对象(包含sign和时间戳,另一个md5没有用),我这把js文件命名为songlist.js,扣完扣了200行,如下,使用方法,

def getsonglist(name,number):
    # url = "https://xxxxxc.xxxxx.com/v1/search?sign={}&word={}&type=1&pageSize=20&appid=16073360×tamp={}".format()
    ctx = execjs.compile(open('songlist.js', encoding='utf-8').read())
    funcName1 = "createSign({word: " + name + ", type: 1, pageSize: "+number+", appid: 16073360}).sign"
    funcName2 = "createSign({word: " + name + ", type: 1, pageSize: "+number+", appid: 16073360}).timestamp"
    sign = ctx.eval(funcName1)
    timestamp = ctx.eval(funcName2)
    # print(sign)
    # print(timestamp)
    name = eval(name)
    url = "https://xxxxx.xxxxx.com/v1/search?sign=" + sign + "&word=" + name + "&type=1&pageSize="+number+"&appid=16073360×tamp=" + str(
        timestamp)
    return url

sign = ctx.eval(funcName1)
timestamp = ctx.eval(funcName2)
字符串funcName1和funcName2就相当于在控制台输入的方法,sign和timestamp用来接收返回值

得到参数然后就能构造链接发送请求。
这只是第二个链接的构造,第一个链接的构造也是如此,我就不再啰嗦了。


3.访问链接,提取数据,下载文件

main函数

def main():
    name = input("请输入歌手或歌名:")
    name = "'" + name + "'"
    # print(name)
    number = input("请输入搜索范围:")
    SongListJson = json.loads(getText(getsonglist(name,number)))
    songs = handlejson(SongListJson)
    print(songs)
    number = input("*********************************选择你要听的歌的序号:*********************************n")
    print("")
    Id = SongListJson['data']['typeTrack'][int(number) - 1]["id"]
    songName=SongListJson['data']['typeTrack'][int(number) - 1]["title"]
    Id = "'" + Id + "'"
    download(Id,songName)

得到链接响应内容:

def getText(url):
    try:
        header = {'User-Agent': 'Mozilla/5.0'}
        r = requests.get(url, timeout=20, headers=header, stream=True)
        r.raise_for_status()
        return r.content
    except:
        print("获取数据失败")

处理音乐列表数据:

def handlejson(SongListJson):
    table = PrettyTable(["序号", "歌曲", "歌手", "专辑", "要求会员?", "id"])
    for i in range(SongListJson['data']['typeTrack'].__len__()):
        table.add_row([str(i + 1), SongListJson['data']['typeTrack'][i]["title"],
                       SongListJson['data']['typeTrack'][i]["artist"][0]["name"],
                       SongListJson['data']['typeTrack'][i]['albumTitle'],
                       SongListJson['data']['typeTrack'][i]["isVip"], SongListJson['data']['typeTrack'][i]["id"]])
    return table

构建得到音乐列表的链接:

def getsonglist(name,number):
    # url = "https://XXXXX.XXXXX.com/v1/search?sign={}&word={}&type=1&pageSize=20&appid=16073360×tamp={}".format()
    ctx = execjs.compile(open('songlist.js', encoding='utf-8').read())
    funcName1 = "createSign({word: " + name + ", type: 1, pageSize: "+number+", appid: 16073360}).sign"
    funcName2 = "createSign({word: " + name + ", type: 1, pageSize: "+number+", appid: 16073360}).timestamp"
    sign = ctx.eval(funcName1)
    timestamp = ctx.eval(funcName2)
    # print(sign)
    # print(timestamp)
    name = eval(name)
    url = "https://XXXXX.XXXXX.com/v1/search?sign=" + sign + "&word=" + name + "&type=1&pageSize="+number+"&appid=16073360×tamp=" + str(
        timestamp)
    return url

构建每个音乐文件详细信息并下载:

def download(Id,songName):
    ctx = execjs.compile(open('abc.js', encoding='utf-8').read())
    funcName1 = "createSign({TSID: " + Id + ", appid: 16073360}).sign"
    funcName2 = "createSign({TSID: " + Id + ", appid: 16073360}).timestamp"
    sign = ctx.eval(funcName1)
    timestamp = ctx.eval(funcName2)
    Id = eval(Id)
    url = "https://XXXXX.XXXXX.com/v1/song/tracklink?sign={}&appid=16073360&TSID={}×tamp={}".format(sign, Id, timestamp)
    # print(url)
    ResponseJson=json.loads(getText(url))
    musicUrl=ResponseJson["data"]["path"]
    writeFile("下载音乐/"+songName+".mp3",getText(musicUrl))
总结 通过这个demo学到很多方面知识,尤其是我对浏览器的开发者工具的调试功能精进不少,也提高了我阅读分析Javascript代码的能力。
还有,了解了一些网站对数据的保护措施以及反爬的手段。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/460870.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号