三滑稽甲苯 发表于 2022-2-17 15:35

[Scrape Center - spa2]含加密参数的动态网页爬取

# 网址

主站:https://scrape.center/
题目:https://spa2.scrape.center/
# 前言
这个题目和 (https://www.52pojie.cn/thread-1589077-1-1.html) 很像,但是难度大了很多,因为它有**加密参数**。但是俗话说得好,功夫不负有心人,只要坚持一定能够拿下它。
# 分析
找到 api 接口,发现相较于 spa1 多了一个 token 参数,那么我们只要找到生成 token 的方法即可。

查看它的请求调用堆栈,第一个直接点进去,然后格式化


下了断点后发现 `u` 是 `null` , `d` 就是一个普通的 `XMLHttpRequest` ,并且这个 js 里区分大小写搜索 `token` 后没有出现类似于 `limit: xxx, offset: xxx, token: xxx` 的结构,那大概率是找错地方了

带有 `:formatted:` 肯定是查看过的 js ,那么接下来看看没有 `:formatted:` 的 js 。

跟进去,是不是看到了很熟悉的结构?

```
params: {
        limit: this.limit,
        offset: a,
        token: e
}
```
接下来只需要下个断点,看看 `e` 是怎么出来的就好了

点击第 2 页,看看断到了什么
`Object(i["a"])` 是一个函数

同时可以猜测 `this.$store.state.url.index` 是一个定值

`a` 就是简简单单的一个 offset 。
跟进 `Object(i["a"])` ,格式化,计算 `token` 的函数终于映入眼帘

猜测 `n` 指代的就是 cryptojs ,这个很容易验证,接下来只要把 js 转为 Python 就好了。
把代码复制下来,进行“翻译”
```javascript
function i() { // arg: '/api/movie', offset
    for (var t = Math.round((new Date).getTime() / 1e3).toString(), e = arguments.length, r = new Array(e), i = 0; i < e; i++)
      //   t = str(int(time()))                                 e = len(args) # 2   r = [?, ?]      for i in range(e)
      r = arguments; // r = args
    r.push(t); // r.append(t)
    var o = n.SHA1(r.join(",")).toString(n.enc.Hex)
    // o = sha1(','.join(str(item) for item in r).encode()).hexdigest()
    //                  ↑ 这里不套一层 str 会导致 Python 报错
      , c = n.enc.Base64.stringify(n.enc.Utf8.parse(.join(",")));
      //c =            b64encode(f'{o},{t}'.encode('utf-8')      ).decode()
      //               stringify 应该包括了 b64encode 和 decode 两个操作
    return c
}
// token = i("/api/movie", offset)
```
完整的生成 token 代码
```python
def generate_token(*args) -> str:
    t = str(int(time()))
    e = len(args)
    r =
    for i in range(e):
      r = args
    r.append(t)
    o = sha1(",".join(str(item) for item in r).encode()).hexdigest()
    c = b64encode(f"{o},{t}".encode("utf-8")).decode()
    return c
```
最后在原来代码的基础上添加参数 `token=generate_token("/api/movie", offset)` 即可使用。
# 完整代码
```python
from requests import Session
from time import time
from hashlib import sha1
from base64 import b64encode


def show(movies: list):
    for movie in movies:
      print(f"{movie['name']} - {movie['alias']}")
      print("Score:", movie["score"])
      print("Tags:", *movie["categories"])
      print("Regions:", *movie["regions"])
      print(f'Duration: {movie["minute"]} min')
      print("Release date:", movie["published_at"])
      print("Link:", f"https://spa2.scrape.center/detail/{movie['id']}")
      print()


def generate_token(*args) -> str:
    t = str(int(time()))
    e = len(args)
    r =
    for i in range(e):
      r = args
    r.append(t)
    o = sha1(",".join(str(item) for item in r).encode()).hexdigest()
    c = b64encode(f"{o},{t}".encode("utf-8")).decode()
    return c


if __name__ == "__main__":
    start = time()
    x = Session()
    LIMIT = 10# 每次最多获取10条数据
    URL = "https://spa2.scrape.center/api/movie/"
    r = x.get(
      URL, params={"limit": 1, "offset": 0, "token": generate_token("/api/movie", 0)}
    )
    TOTAL = r.json()["count"]
    offset = 0
    while offset < TOTAL:
      r = x.get(
            URL,
            params={
                "limit": LIMIT,
                "offset": offset,
                "token": generate_token("/api/movie", offset),
            },
      )
      show(r.json()["results"])
      offset += LIMIT
    end = time()
    input(f"Time used: {end - start} s.")
```
# 效果图

Xw丶小威 发表于 2022-2-17 16:55

学到了。真好。

君月栩 发表于 2022-2-17 17:39

感谢楼主分享

tt5200 发表于 2022-2-17 17:45


感谢楼主分享

998858 发表于 2022-2-17 18:19

收藏起来先,以后用到{:1_918:}

cflying 发表于 2022-2-17 21:24

看看,学学
页: [1]
查看完整版本: [Scrape Center - spa2]含加密参数的动态网页爬取