(笔记)数据采集基础08

20240421

1.网易云爬取

import zlib
# pip install PyExecJS
import execjs
import requests
import time
import hashlib
import base64
import os
import json
import re
headers = {
    "authority": "music.163.com",
    "accept": "*/*",
    "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
    "cache-control": "no-cache",
    "content-type": "application/x-www-form-urlencoded",
    "origin": "https://music.163.com",
    "pragma": "no-cache",
    "referer": "https://music.163.com/",
    "sec-ch-ua": "^\\^Not_A",
    "sec-ch-ua-mobile": "?0",
    "sec-ch-ua-platform": "^\\^Windows^^",
    "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/120.0.0.0 Safari/537.36 Edg/120.0.0.0"
}

def get_js():
    f = open("tests.js", 'r',encoding='utf8')
    line = f.readline()
    htmlstr = ''
    while line:
        htmlstr = htmlstr+line
        line = f.readline()
    return htmlstr

def get_des_psswd(e):
    js_str = get_js()
    ctx = execjs.compile(js_str)
    #这里hello为js文件里的函数,e为向hello这个函数里传递的参数
    #这里我们的e这个形参主要用来传递歌曲的id,这样我们只需要向该函数传递不同的歌曲Id,即可返回不同的下载链接
    return (ctx.call('hello',e))

def get_song_url(song_id):
    token = get_des_psswd(song_id)
    url = "https://music.163.com/weapi/song/enhance/player/url/v1"
    params = {
        "csrf_token": ""
    }
    data = {
        "params": token[0],
        "encSecKey": token[1]
    }
    print(data)
    response = requests.post(url, headers=headers,params=params, data=data).json()
    song_url = response['data'][0]['url']
    return song_url

def save_song(song_url,song_path):
    song_response = requests.get(song_url).content
    op = open('{}.mp3'.format(song_path),'wb')
    op.write(song_response)
    op.close()

def get_song_id(album_id):
    album_url = 'https://music.163.com/playlist?id={}'.format(album_id)
    album_source = requests.get(album_url,headers=headers).text
    result = re.findall('<a href="/song\?id=(\d+)">(.*?)</a>',album_source)
    return result

def main():
    song_id_and_song_name = get_song_id(158010361)
    if not os.path.isdir('158010361'):
        os.mkdir('158010361')
    for song_id,song_name in song_id_and_song_name:
        song_url = get_song_url(song_id)
        if song_url:
            song_path = "./158010361/{}".format(song_name)
            save_song(song_url, song_path)

main()

2.js知识

(1)js声明,var  let  const 区别

var 声明的变量属于函数作用域,而 let const 声明的变量属于块级作用域;( js 作用域在上篇文章)
var 声明的变量存在变量提升,而 let const 没有
var 声明的变量可以重复声明,而在同一块级作用域, let 变量不能重新声明, const 常量不能修改(对象的属性和方法,数组的内容可以修改)
var 声明的作用域
使用 var 声明的变量,这个变量属于当前的函数作用域,如果变量的声明在任何函数外,那么这个变量就属于全局作用域
var a = 1 ; // 此处声明的变量 a 为全局变量
function foo (){
var a = 2 ; // 此处声明的变量 a 为函数 foo 的局部变量
console . log ( a ); //2
}
foo ();
console . log ( a ); //1
如果在声明变量时,省略 var 的话,该变量就会变成全局变量,如全局作用域中存在该变量,就会更新其值
var a = 1 ; // 此处声明的变量 a 为全局变量
function foo (){
a = 2 ; // 此处的变量 a 也是全局变量
console . log ( a ); //2
}
foo ();
console . log ( a ); //2
var 声明的变量提升
var 的声明会在 js 预解析时把 var 的声明提升到当前作用域的最前面,意思是是指无论 var 出现在一个作用域的哪个位置,这个声明都属于当前的整个作用域,在其中到处都可以访问到。只有变量声明才会提升,对变量赋值并不会提升
console . log ( a ); //undefined
var a = 1 ;
相当于执行以下代码
var a ;
console . log ( a ); //undefined
a = 1 ;
let 声明
let 声明的变量具有块作用域的特征。
在同一个块级作用域,不能重复声明变量
function foo (){
let a = 1 ;
let a = 2 ; //Uncaught SyntaxError: Identifier 'a' has already been declared
}
let 声明的变量不存在变量提升
let a = 1 ;
console . log ( a ); //1
console . log ( b ); //Uncaught ReferenceError: b is not defined
let b = 2 ;
var let( 作用域 )
var 声明
for (var i = 0; i < 10; i++) {
setTimeout(function(){
console.log(i);
},100)
};
1. 此时的 var 声明的变量 i 属于函数作用域,声明又不在函数里,所以 i 属于全局变量 2.此时的定时器函数属于异步函数,隔 100 毫秒才会执行,而这 100 毫秒的时间内, for 循环已经循环结束,全局变量i 已经为 10 ,最后代码的执行后,会在控制台打印出10 10 ) 主要的原因是 var 声明的变量的没有块级作用域
let 声明
for ( let i = 0 ; i < 10 ; i ++ ) {
// 每一轮都会形成一个私有的块级作用域,并且有一个私有的变量 i ,分别存储每一轮循环的索引
setTimeout ( function (){
console . log ( i );
}, 100 )
};
const 声明
const 声明方式,除了具有 let 的上述特点外,其还具备一个特点,即 const 定义的变量,一旦定义后,
就不能修改,即 const 声明的为常量。
但是,并不是说 const 声明的变量其内部内容不可变,如:
const a = 1 ;
console . log ( a ); //1
a = 2 ;
console . log ( a ); //Uncaught TypeError: Assignment to constant variable.
const obj = { a : 1 , b : 2 };
console . log ( obj . a ); //1
obj . a = 3 ;
console . log ( obj . a ); //3
所以准确的说,是 const 声明创建一个值的只读引用。但这并不意味着它所持有的值是不可变的,只是变量标识符不能重新分配。
(2) js原型对象
构造函数:用来在创建对象时初始化对象。特点:构造函数名一般为大写字母开头;与 new 运算符 一起 使用来实例化对象。
function Person (){} //Person 构造函数
var p = new Person (); //Person 构造函数创建对象 , 也可叫做实例化
这里注意一点,所有的函数实际上都可以作为构造函数,因为 js 里面没有类的概念,你可以认为构造函数就是类,而使用构造函数创建对象这个过程等同于类的实例化过程。
(3)js基本逻辑语法
条件语句与数据类型;
(4)js定义函数
方法一:
function print(s) {
  console.log(s);
}
方法二:
var print = function(s) {
  console.log(s);
};
方法三:

var add = new Function(
  'x',
  'y',
  'return x + y'
);

// 等同于
function add(x, y) {
  return x + y;
}

(5)立刻调用表达式数组

函数作用域:

作用域(scope)指的是变量存在的范围。在 ES5 的规范中,JavaScript 只有两种作用域:一种是全局作用域,变量在整个程序中一直存在,所有地方都可以读取;另一种是函数作用域,变量只在函数内部存在。

函数内部定义的变量,会在该作用域内覆盖同名全局变量。

.......

3.eval加密网站解决

基本用法

eval命令接受一个字符串作为参数,并将这个字符串当作语句执行。

eval('var a = 1;');
a // 1

4.强制转换

强制转换主要指使用Number()String()Boolean()三个函数,手动将各种类型的值,分别转换成数字、字符串或者布尔值。

Number()

// 数值:转换后还是原来的值
Number(324) // 324

// 字符串:如果可以被解析为数值,则转换为相应的数值
Number('324') // 324

// 字符串:如果不可以被解析为数值,返回 NaN
Number('324abc') // NaN

// 空字符串转为0
Number('') // 0

// 布尔值:true 转成 1,false 转成 0
Number(true) // 1
Number(false) // 0

// undefined:转成 NaN
Number(undefined) // NaN

// null:转成0
Number(null) // 0

类似函数parseInt函数的使用

parseInt('42 cats') // 42
Number('42 cats') // NaN

String()

String(123) // "123"
String('abc') // "abc"
String(true) // "true"
String(undefined) // "undefined"
String(null) // "null"
String({a: 1}) // "[object Object]"
String([1, 2, 3]) // "1,2,3"

Boolean()

Boolean()函数可以将任意类型的值转为布尔值。

它的转换规则相对简单:除了以下五个值的转换结果为false,其他的值全部为true

  • undefined

  • null

  • 0(包含-0+0

  • NaN

  • ''(空字符串)

Boolean(undefined) // false
Boolean(null) // false
Boolean(0) // false
Boolean(NaN) // false
Boolean('') // false
Boolean(true) // true
Boolean(false) // false
Boolean({}) // true
Boolean([]) // true
Boolean(new Boolean(false)) // true

自动转换

遇到以下三种情况时,JavaScript 会自动转换数据类型,即转换是自动完成的,用户不可见。

第一种情况,不同类型的数据互相运算。

123 + 'abc' // "123abc"

第二种情况,对非布尔值类型的数据求布尔值。

if ('abc') {
  console.log('hello')
}  // "hello"

第三种情况,对非数值类型的值使用一元运算符(即+-)。

+ {foo: 'bar'} // NaN
- [1, 2, 3] // NaN

自动转换的规则是这样的:预期什么类型的值,就调用该类型的转换函数。比如,某个位置预期为字符串,就调用String()函数进行转换。如果该位置既可以是字符串,也可能是数值,那么默认转为数值。

由于自动转换具有不确定性,而且不易除错,建议在预期为布尔值、数值、字符串的地方,全部使用Boolean()Number()String()函数进行显式转换。

5.Object对象函数

Object.setPrototypeOf
在 JavaScript 中用于设置一个对象的原型(即内部的 [[Prototype]] 属性)。这个方法通常用于改变对象的继承链,或者将一个对象的原型设置为 null 来防止进一步的原型继承。

下面是一个使用 Object.setPrototypeOf 方法的例子:

// 构造函数
function Animal(name) {
  this.name = name;
}

// 添加一个方法到 Animal 原型上
Animal.prototype.speak = function() {
  console.log(this.name + ' makes a sound.');
};

// 使用构造函数创建一个实例
const animal = new Animal('Generic animal');

// 创建另一个构造函数
function Vehicle(make, model) {
  this.make = make;
  this.model = model;
}

// 添加一个方法到 Vehicle 原型上
Vehicle.prototype.describe = function() {
  console.log('This is a ' + this.make + ' ' + this.model + ' vehicle.');
};

// 使用构造函数创建一个 Vehicle 实例
const car = new Vehicle('Toyota', 'Corolla');

// 现在,我们将 car 实例的原型设置为 animal 实例
// 这意味着 car 将继承 animal 实例的所有属性和方法
Object.setPrototypeOf(car, animal);

// 验证原型链是否已改变
console.log(Object.getPrototypeOf(car) === animal); // 输出: true

// 由于 car 的原型现在是 animal,它继承了 speak 方法
car.speak(); // 输出: Generic animal makes a sound.

// 为了演示,我们也可以将 car 的原型设置为 null,这样它就不再有原型
Object.setPrototypeOf(car, null);

// 再次验证原型链
console.log(Object.getPrototypeOf(car) === null); // 输出: true

// 现在 car 没有原型,因此不能访问 speak 方法
car.speak(); // 输出: TypeError: car.speak is not a function
在这个例子中,我们首先定义了两个构造函数 Animal 和 Vehicle,并分别为它们的原型添加了方法 speak 和 describe。然后,我们分别使用这两个构造函数创建了 animal 和 car 实例。

接下来,我们使用 Object.setPrototypeOf 方法将 car 实例的原型设置为 animal 实例。这意味着 car 现在继承了 animal 实例的所有属性和方法。我们通过调用 car.speak() 来验证这一点,它输出了 animal 实例的名字和声音。

最后,我们再次使用 Object.setPrototypeOf 方法将 car 的原型设置为 null,这意味着 car 将不再继承任何属性和方法。当我们尝试调用 car.speak() 时,由于 car 没有 speak 方法,因此抛出了一个类型错误。

这个例子展示了 Object.setPrototypeOf 方法如何用于修改对象的原型链,以及如何通过改变原型来控制对象继承的属性和方法

Object.getOwnPropertyDescriptor
`Object.getOwnPropertyDescriptor` 方法在 JavaScript 中用于获取指定对象上给定属性的描述信息。描述信息是一个包含属性的各种元数据的对象,例如属性是否可枚举、是否可写、默认值以及 getter 和 setter 函数等。

这个方法对于深入了解对象属性的具体行为和特征非常有用,尤其是在调试或者需要精确控制对象属性时。

下面是一个使用 `Object.getOwnPropertyDescriptor` 方法的例子:

```javascript
// 创建一个对象,并定义一个属性
const myObject = {
  myProperty: 'Hello, World!'
};
 
// 定义一个 getter 和一个 setter 函数
const getter = function () {
  return this.myProperty + ' (accessed via getter)';
};
 
const setter = function (newValue) {
  this.myProperty = newValue + ' (updated via setter)';
};
 
// 使用 Object.defineProperty 为对象添加一个带有 getter 和 setter 的属性
Object.defineProperty(myObject, 'accessorProperty', {
  get: getter,
  set: setter,
  enumerable: true,
  configurable: true
});
 
// 使用 Object.getOwnPropertyDescriptor 获取属性描述信息
const descriptor = Object.getOwnPropertyDescriptor(myObject, 'accessorProperty');
 
// 打印属性描述信息
console.log(descriptor);
// 输出:
// {
//   value: 'Hello, World! (accessed via getter)',
//   writable: false,
//   enumerable: true,
//   configurable: true,
//   get: [Function: getter],
//   set: [Function: setter]
// }
 
// 直接访问属性值
console.log(myObject.accessorProperty); // 输出: 'Hello, World! (accessed via getter)'
 
// 修改属性值
myObject.accessorProperty = 'Goodbye, World!';
console.log(myObject.accessorProperty); // 输出: 'Goodbye, World! (updated via setter)'

在这个例子中,我们首先创建了一个对象 `myObject` 并给它定义了一个普通的属性 `myProperty`。接着,我们定义了一个 getter 函数和一个 setter 函数,并将它们与 `Object.defineProperty` 方法一起使用,为 `myObject` 添加了一个带有访问器(accessor)的属性 `accessorProperty`。

使用 `Object.getOwnPropertyDescriptor` 方法,我们获取了 `accessorProperty` 的描述信息,并打印出来。描述信息对象包含了属性的值、是否可写、是否可枚举、是否可配置以及 getter 和 setter 函数的引用。

然后,我们通过直接访问 `accessorProperty` 来触发 getter 函数,并打印出它的返回值。之后,我们尝试修改 `accessorProperty` 的值,这将触发 setter 函数,并将修改后的值打印出来。

通过这个例子,我们可以看到 `Object.getOwnPropertyDescriptor` 方法如何帮助我们获取和理解对象属性的详细信息。

  • 36
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值