oGsLegolas 发表于 2020-3-6 01:02

【原创开源】快手爬虫,根据id批量爬取用户的所有图集和视频

本帖最后由 oGsLegolas 于 2020-3-23 16:32 编辑


# 更新日志

> 所有版本更新日志会记录在这里

### v0.4.0(2020-03-23)

* 修复id转eid的一些bug
* 从该版本开始,爬取视频均为无水印 **感谢@(https://github.com/tjftjftjf)**

### v0.3.0(2020-03-10)

* 修复一些因为用户昵称中存在windows下文件(夹)名非法字符导致os无法写入读取的bug
* 简单看了一点python面向对象,将核心功能提取为crawler类,降低耦合
* 基于crawler类,分出两个文件,一个用于直接在python环境下跑代码,另一个则用于打包好exe一键运行
* 提供exe版本

### v0.2.0(2020-02-26)

* 增加对数字id自动查询转换为eid的支持

### v0.1.1 (2020-02-25)

* 修复`caption`项文字保存为文件名的各种问题
* 增加对`workType` 'ksong'、'single'的支持
* 增加对于爬取过程中请求失败的解决方法
* 增加CHANGELOG.md

# 写在前面

* **项目源码地址 (https://github.com/oGsLP/kuaishou-crawler)**
* 代码功能如题,根据快手用户的id来爬取用户所有公开作品,包括图集和视频。
* 原理:其实就是利用基于chromium内核的浏览器自带的devtools对所有请求进行排查找出包含作品链接的请求,然后用代码模拟请求去获得数据,再根据url下载作品保存就行了,包括一些网站的自动注册登录、操作都可以模拟。这个其实应该算是写过爬虫的同学们都知道,我自己其实不怎么用过python,也没写过什么复杂的python项目,说的不对的还请多多包涵。如果有同学还是想让我讲一下怎么爬的,我考虑再做一期详细的,其实代码应该还是可以看得懂的2333

# 核心代码

* 废话不多说,上核心代码
```python
def __crawl_user(self, uid):
    if uid.isdigit():
      uid = self.__switch_id(uid)

    payload = {"operationName": "privateFeedsQuery",
               "variables": {"principalId": uid, "pcursor": "", "count": 999},
               "query": "query privateFeedsQuery($principalId: String, $pcursor: String, $count: Int) {\nprivateFeeds(principalId: $principalId, pcursor: $pcursor, count: $count) {\n    pcursor\n    list {\n      id\n      thumbnailUrl\n      poster\n      workType\n      type\n      useVideoPlayer\n      imgUrls\n      imgSizes\n      magicFace\n      musicName\n      caption\n      location\n      liked\n      onlyFollowerCanComment\n      relativeHeight\n      timestamp\n      width\n      height\n      counts {\n      displayView\n      displayLike\n      displayComment\n      __typename\n      }\n      user {\n      id\n      eid\n      name\n      avatar\n      __typename\n      }\n      expTag\n      __typename\n    }\n    __typename\n}\n}\n"}
    res = requests.post(self.__data_url, headers=self.__headers, json=payload)

    works = json.loads(res.content.decode(encoding='utf-8', errors='strict'))['data']['privateFeeds']['list']

    if not os.path.exists("../data"):
      os.makedirs("../data")

    # 这两行代码将response写入json供分析
    # with open("data/" + uid + ".json", "w") as fp:
    #   fp.write(json.dumps(works, indent=2))

    # 防止该用户在直播,第一个作品默认为直播,导致获取信息为NoneType
    if works['id'] is None:
      works.pop(0)
    name = re.sub(r'[\\/:*?"<>|\r\n]+', "", works['user']['name'])

    dir = "data/" + name + "(" + uid + ")/"
    # print(len(works))
    if not os.path.exists(dir):
      os.makedirs(dir)

    # if not os.path.exists(dir + ".list"):
    #   print("")

    print("开始爬取用户 " + name + ",保存在目录 " + dir)
    print(" 共有" + str(len(works)) + "个作品")

    for j in range(len(works)):
      self.__crawl_work(uid, dir, works, j + 1)
      time.sleep(1)

    print("用户 " + name + "爬取完成!")
    print()
    time.sleep(1)


'''
快手分为五种类型的作品,在作品里面表现为workType属性
* 其中两种图集: `vertical`和`multiple`,意味着拼接长图和多图,所有图片的链接在imgUrls里
* 一种单张图片: `single` 图片链接也在imgUrls里
* K歌: `ksong` 图片链接一样,不考虑爬取音频...
* 视频: `video` 需要解析html获得视频链接
'''

    def __crawl_work(self, uid, dir, work, wdx):
      w_type = work['workType']
      w_caption = re.sub(r"\s+", " ", work['caption'])
      w_name = re.sub(r'[\\/:*?"<>|\r\n]+', "", w_caption)
      w_time = time.strftime('%Y-%m-%d', time.localtime(work['timestamp'] / 1000))

      if w_type == 'vertical' or w_type == 'multiple' or w_type == "single" or w_type == 'ksong':
            w_urls = work['imgUrls']
            l = len(w_urls)
            print("" + str(wdx) + ")图集作品:" + w_caption + "," + "共有" + str(l) + "张图片")
            for i in range(l):
                p_name = w_time + "_" + w_name + "_" + str(i + 1) + ".jpg"
                pic = dir + p_name
                if not os.path.exists(pic):
                  r = requests.get(w_urls)
                  r.raise_for_status()
                  with open(pic, "wb") as f:
                        f.write(r.content)
                  print("    " + str(i + 1) + "/" + str(l) + " 图片 " + p_name + " 下载成功 √")
                else:
                  print("    " + str(i + 1) + "/" + str(l) + " 图片 " + p_name + " 已存在 √")
      elif w_type == 'video':
            w_url = self.__work_url + work['id']
            res = requests.get(w_url, headers=self.__headers_mobile,
                               params={"fid": 1841409882, "cc": "share_copylink", "shareId": "143108986354"})
            html = res.text
            waitreplace = work['id'] + '&#34.*?&#34;srcNoMark&#34;:&#34;(.*?)&#34;'

            v_url = re.findall(waitreplace, html)
            # pattern = re.compile(r"playUrl", re.MULTILINE | re.DOTALL)
            # script = soup.find("script", text=pattern)
            # s = pattern.search(script.text).string
            # v_url = s.split('playUrl":"').split('.mp4').encode('utf-8').decode('unicode-escape') + '.mp4'
            try:
                print("" + str(wdx) + ")视频作品:" + w_caption)
            except:
                print("这里似乎有点小错误,已跳过")
            v_name = w_time + "_" + w_name + ".mp4"
            video = dir + v_name

            if v_url:
                if not os.path.exists(video):
                  r = requests.get(v_url)
                  r.raise_for_status()

                  with open(video, "wb") as f:
                        f.write(r.content)
                  print("    视频 " + v_name + " 下载成功 √")
                else:
                  print("    视频 " + v_name + " 已存在 √")
            else:
                print("未找到视频")
      else:
            print("错误的类型")
```

* payload就是post参数,这个是在devtools的request请求底下可以找到的
* 其实就是解析json,然后里面有图片的url和视频的id,我注释掉的两行代码可以保存完整的json的,你可以去掉注释然后看分析保存的json
* 剩下的看源码吧,不难理解的

---

#### 既然是编程语言区嘛,主要还是代码,(https://github.com/oGsLP/kuaishou-crawler) ,链接查看源码,这是编程语言区,不是工具软件区,我只是提供代码供大家学习,不是只让你用这个爬视频图片的。既然在编程语言区,相信大家多多少少还是会一点编程的,或者说这样的代码学习成本不是很大,不希望大家是伸手党,自己折腾出来的才是经验。

(ps: 从v0.3版本开始有打包好的exe提供了 >_>)

#### 至于什么评分热心值和毕看着给吧23333,第一次发帖希望能帮到大家就好,如果代码真的帮到你了,或者用起来很舒服,可以github上给一波star什么的 [*link*](https://github.com/oGsLP/kuaishou-crawler)各位看心情看心情

#### 具体的使用说明,直接抄我github上的readme的原文了。。

---

# kuaishou-crawler

> As you can see, a crawler for kuaishou pictures and videos

## Latest

### Version 0.4.0 (2020-03-23)

* 现在已经提供exe版本一键执行 [查看](#release) | 或者查看如何运行代码 [查看](#run)
* Python 3.7.3
    * requests
    * json
    * os
    * ~~BeautifulSoup~~
    * re
* 自v0.3.0版本开始,已用面向对象重构,核心代码在`lib/crawler.py`中,启动文件为`crawl.py` / `ks.py`
* 功能:根据用户ID来爬取快手用户的作品,包括视频和图片
    1. 在preset文件(**使用exe版本忽略此文件**)中一行行填写用户id,若缺少文件会自动创建(目前版本已提供自动根据数字id获取真实eid)
      * 分享链接用浏览器打开,根据网址 https://live.kuaishou.com/profile/xxxx 获取id: xxxx~~
      * 或者手机里点开快手用户的头像,底下会告诉你快手号的
    2. 使用时请自己用账号登录快手网站,并使用自己的`cookie['headers']`和`didweb`替换,不保证源代码中对应值可用
      * 关于cookie的did值,你在电脑浏览器中打开快手网站登录后随便打开一个用户的视频作品,然后再地址栏中找到这一项,咱们以giao哥的第一个视频为例
            - https://live.kuaishou.com/u/3x4tn6nm8gnh9xk/3xtd5zf5qbduphc?did=web_12345shiwoluandade
            - 你要保存下来的就是did后面的 `web_12345shiwoluandade`
    3. 因为快手官网会根据cookie,识别你是否在线,爬取的时候要将网页登录并挂着
      * 实测快手网站的用户验证存在30-60分钟左右的有效时长,出现`list index out of range`时极可能是有效期已过,登录网站验证即可
      * 暂且不知道快手官方对过多请求的处理,目前碰到的有上述验证失效,也许也会有请求达到数量会中断请求,此时注释preset中已爬取的用户id,重新开始运行脚本即可
    4. 爬取的视频~~暂时是带水印的(以后考虑获取无水印视频)~~ 是无水印的 **感谢@(https://github.com/tjftjftjf)提供手机抓包链接和方法**
* 注意事项:
    * 不考虑提供列表可选的批量下载功能
    * 有需要的合理功能可以issue反馈,看到后会考虑是否修改
    * 如果需要自定义自己的需求,可以拿走代码自行修改,喜欢的话给个star给个follow
    * **本代码仅供学习使用,不可违反法律爬取视频,以及私自盗用搬运视频,后果自负**
    * **本代码仅供学习使用,不可违反法律爬取视频,以及私自盗用搬运视频,后果自负**
    * **本代码仅供学习使用,不可违反法律爬取视频,以及私自盗用搬运视频,后果自负**
    * 重要的说三遍

## Run

0. python3环境与命令行工具
1. 进入项目目录 `cd kuaishou-crawler`
2. 安装依赖 `pip install -r requirements.txt`
3. 运行,有两个版本,`crawl.py`为运行版本,`ks.py`是用于构建exe的版本,当然也可以运行
    * `python crawl.py` / `python ks.py`
   
## Release

https://github.com/oGsLP/kuaishou-crawler/releases

* 下载打包好的exe一键运行(点击download下载即可)
    * ks.exe
    * ks.7z


## Future

* ~~自动根据id获取eid~~ √
* 获取无水印视频 √
* 进一步丰富preset预设文件的可配置选项
* ~~优化代码和log~~ √
* 提供便捷的打包exe √

## Again

**本代码仅供学习使用,不可违反法律爬取视频,以及私自盗用搬运视频,后果自负**

oGsLegolas 发表于 2020-3-9 20:05

本帖最后由 oGsLegolas 于 2020-3-12 14:21 编辑

大街上要饭的 发表于 2020-3-9 16:46
感谢楼主您能理解,这个要求是有点无理,十分感激您能回我这个小白的回复
https://github.com/oGsLP/kuaishou-crawler/raw/master/dist/ks.exe

~~暂时先写了一个简单易用的,以后有时间会完善的,帖子持续会更新的~~
**已经提供exe版本,见帖子中文档详情**

## 有三个要点
1. 关于cookie的did值,你在电脑浏览器中打开快手网站登录后随便打开一个用户的视频作品,然后再地址栏中找到这一项,咱们以giao哥的第一个视频为例
      - https://live.kuaishou.com/u/3x4tn6nm8gnh9xk/3xtd5zf5qbduphc?did=web_12345shiwoluandade
      - 你要保存下来的就是did后面的 `web_12345shiwoluandade`
2. 关于用户的id,手机里点开快手用户的头像,底下会告诉你快手号的
3. 因为快手官网会根据cookie,识别你是否在线,爬取的时候要将网页登录并挂着

oGsLegolas 发表于 2020-4-18 22:49

这段时间忙毕设,有问题反馈的我都会记录下来,有时间再更新

oGsLegolas 发表于 2020-8-27 23:21

Excel_w 发表于 2020-8-27 22:54
好吧我刚重新下载了一下源码,重新执行了一下exe文件,遇到视频倒是没有闪退,但是全都显示未找到视频了,源 ...

* 替换crawl.py中的did_web
* `python crawl.py`

oGsLegolas 发表于 2020-8-12 21:55

可以用了,前两天改了一下

chen180 发表于 2020-9-6 00:33

好像出bug 了?
Traceback (most recent call last):
File "D:/code/github/kuaishou-crawler/crawl.py", line 23, in <module>
    crawl()
File "D:/code/github/kuaishou-crawler/crawl.py", line 19, in crawl
    crawler.crawl()
File "D:\code\github\kuaishou-crawler\lib\crawler.py", line 73, in crawl
    self.__crawl_user(uid)
File "D:\code\github\kuaishou-crawler\lib\crawler.py", line 113, in __crawl_user
    self.__crawl_work(dir, works, j + 1)
File "D:\code\github\kuaishou-crawler\lib\crawler.py", line 167, in __crawl_work
    v_url = re.search(pattern, html).group(1)+".mp4"
AttributeError: 'NoneType' object has no attribute 'group'

TechPeng 发表于 2020-3-6 10:06

谢谢,分享

bazett 发表于 2020-3-6 10:35

这个Payload是怎么得到的

oGsLegolas 发表于 2020-3-6 12:26

bazett 发表于 2020-3-6 10:35
这个Payload是怎么得到的

打开网站,打开devtools的network,寻找里面的请求数据的request请求,

花形透 发表于 2020-3-9 02:32

占楼~~~~~

cndaofeng 发表于 2020-3-9 03:28

大街上要饭的 发表于 2020-3-9 10:17

您对编程的维护,小的十分赞成感激,表示理解您对编程过程的热爱与维护
我在此表达敬意的同时也希望您能理解我们小白的感受,的确基本的编程技术还是应该有的,但我也希望您都能理解,所以。。。。emmm感谢您的exe

oGsLegolas 发表于 2020-3-9 14:29

大街上要饭的 发表于 2020-3-9 10:17
您对编程的维护,小的十分赞成感激,表示理解您对编程过程的热爱与维护
我在此表达敬意的同时也希望您能理 ...

2333好吧,这两天有时间弄一个出来:lol

大街上要饭的 发表于 2020-3-9 16:46

oGsLegolas 发表于 2020-3-9 14:29
2333好吧,这两天有时间弄一个出来

感谢楼主您能理解,这个要求是有点无理,十分感激您能回我这个小白的回复
页: [1] 2 3 4 5 6 7 8 9 10
查看完整版本: 【原创开源】快手爬虫,根据id批量爬取用户的所有图集和视频