抖音分享页面(2020版本) 数据抓取 Golang版

 

本文以分享技术为主,禁止将本文内容使用任何形式的商业形式或违法活动,后果自负。

---

# 原理介绍

通过代码模拟浏览器打开抖音的分享页面,从而获取到用户的信息,而不需要理解各种抖音请求的算法,最低门槛开始数据获取之路。

 

简单看一下下面的图,显示出来的信息都可以获取到,比如关注数,粉丝数,点赞数,作品数,喜欢数。

 

# 分享从哪里来?

 

在右下角菜单"我"-->右上角菜单"三条杠"-->我的二维码, 如果是其他用户也可以在用户信息界面的右上角菜单找到.

 

 

 

到目前为止就得到了一个链接,我们将要开始模拟浏览器打开请求获取到这个链接的数据。  

 

使用到的技术点:

编程语言:Golang(需要go mod)

模拟浏览器:chromedp

便捷部署:Docker

 

获取到这个数据是这样一个过程:

1. 打开分享页面的链接,会发生302跳转,从location的头部信息拿到跳转后的URL

2. 通过跳转后的URL中拿到sec_uid参数

3. 使用sec_uid参数拼接数据接口,请求数据  

 

就是这样三步就可以拿到用户的基本信息了。

 

项目准备工作,以linux命令展示:

创建项目目录(douyin): mkdir douyin

初始化go mod信息: go mod init

创建main.go文件 : touch main.go,将完整代码写入到main.go

 

以下是完整的代码:

package main

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"log"
	"net/http"
	"strings"
	"time"

	"github.com/chromedp/cdproto/cdp"
	"github.com/chromedp/cdproto/fetch"
	"github.com/chromedp/cdproto/network"
	"github.com/chromedp/chromedp"
)

func main() {
	// 设置路由,如果访问/,则调用index方法
	http.HandleFunc("/index", index)
	// 启动web服务,监听9090端口
	err := http.ListenAndServe(":8000", nil)
	if err != nil {
		panic(err)
	}
	log.Println("server.started")
}

func index(w http.ResponseWriter, r *http.Request) {
	url := r.URL.Query().Get("url")
	log.Println("get.url:", url)
	result := loadDouYin(url)
	fmt.Fprintf(w, result)
}

func loadDouYin(firstUrl string) string {
	opts := append(chromedp.DefaultExecAllocatorOptions[:],
		chromedp.DisableGPU,
		chromedp.NoDefaultBrowserCheck,
		chromedp.NoSandbox,
		chromedp.Flag("headless", true),
		chromedp.Flag("ignore-certificate-errors", true),
	)

	//创建chrome
	allocCtx, _ := chromedp.NewExecAllocator(context.Background(), opts...)

	br, brcancel := chromedp.NewContext(allocCtx)
	//打开一个空白页面测试一下是否打开
	err := chromedp.Run(br, chromedp.Navigate("about:blank"))
	if err != nil {
		panic("chrome open blank failed, " + err.Error())
	}
	defer brcancel()
	//新建一个tab
	tab, tabcancel := chromedp.NewContext(br)
	defer tabcancel()

	secondurl := ""
	//监听请求的数据
	chromedp.ListenTarget(tab, func(ev interface{}) {
		switch evnt := ev.(type) {
		case *fetch.EventRequestPaused:
			go func(evt *fetch.EventRequestPaused) {
				var requestid = evt.RequestID
				nctx := chromedp.FromContext(tab)
				lctx := cdp.WithExecutor(tab, nctx.Target)
				defer func() {
					//如果不执行这一步 请求在发起一个请求后停住  比如打开一个页面有三个请求,如果不执行这里 则会卡在第一个请求后
					err = fetch.ContinueRequest(requestid).Do(lctx)
					if err != nil {
						log.Println(" continuerequest failed," + err.Error())
					}
				}()
				log.Println("evt.ResponseStatusCode:", evt.Request.URL, evt.ResponseStatusCode)
				if evt.ResponseStatusCode == 302 {
					for _, v := range evt.ResponseHeaders {
						//拿到302请求中的location头部信息, 需要跳转的URL
						if v.Name == "location" {
							secondurl = v.Value
							break
						}
					}
					return
				} else if evt.ResponseStatusCode == 304 {
					return
				}
				if secondurl != "" {
					//拿到了302的链接
					log.Println("get.302.url.success:", secondurl)
				} else {
					log.Println("get.302.url.failed")
				}
			}(evnt)
		}
	})

	err = chromedp.Run(tab, network.Enable(), fetch.Enable().WithPatterns([]*fetch.RequestPattern{
		{
			//监听请求的response类型
			ResourceType: network.ResourceTypeDocument,
			RequestStage: "Response",
		},
	}), chromedp.Navigate(firstUrl))
	// time.Sleep(3 * time.Second)
	if secondurl != "" {
		//https://www.iesdouyin.com/share/user/96875399323?u_code=15a973a6e&sec_uid=MS4wLjABAAAADufhDZPJqGpNo-4ay7ZQtVsZbvCxVPEesV4PUAE2wyc&timestamp=1602391166&utm_source=copy&utm_campaign=client_share&utm_medium=android&share_app_name=douyin
		strs1 := strings.Split(secondurl, "&")
		for i := 0; i < len(strs1); i++ {
			if strings.Contains(strs1[i], "sec_uid") {
				strs := strings.Split(strs1[i], "=")
				if len(strs) > 0 {
					secUid := strs[1]
					log.Println("get secUid :", secUid)
					url := "https://www.iesdouyin.com/web/api/v2/user/info/?sec_uid=" + secUid
					return doDYinfo(url)
				}
			}

		}
	}
	return "nothing"
}

func doDYinfo(url string) string {

	request, err := http.NewRequest("GET", url, nil)
	if err != nil {
		panic(err)
	}

	request.Header.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8")
	request.Header.Add("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3")
	request.Header.Add("Connection", "keep-alive")
	//写入浏览器的user agent
	request.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36")

	// 超时时间:5秒
	client := &http.Client{Timeout: 5 * time.Second}
	resp, err := client.Do(request)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	var buffer [512]byte
	result := bytes.NewBuffer(nil)
	for {
		n, err := resp.Body.Read(buffer[0:])
		result.Write(buffer[0:n])
		if err != nil && err == io.EOF {
			break
		} else if err != nil {
			panic(err)
		}
	}
	log.Println("req.result:", result.String())
	return result.String()
}

 

这段代码的最终结果是用Golang自带的http server启动了一个http服务,端口号是8000,开放了一个/index路径的API,API只有一个参数,也就是分享页面的链接.返回的结果就是抖音接口的数据.  

例如:  

1. 启动程序.

[root@normal11 douyin]# go run main.go 

2. 请求一个分享页面的用户基本数据

[root@normal11 faither]# curl localhost:8000/index?url=https://v.douyin.com/JP1BNGa/
{"status_code":0,"user_info":{"uid":"59424687354","aweme_count":10,"following_count":42,"verification_type":1,"original_musician":{"music_count":0,"music_used_count":0},"followers_detail":null,"platform_sync_info":null,"secret":0,"type_label":null,"nickname":"PVP","avatar_thumb":{"uri":"tos-cn-avt-0015/70e93c2224fc560d7b72d634c7a30600","url_list":["https://p3-dy-ipv6.byteimg.com/img/tos-cn-avt-0015/70e93c2224fc560d7b72d634c7a30600~c5_100x100.jpeg?from=4010531038","https://p29-dy.byteimg.com/img/tos-cn-avt-0015/70e93c2224fc560d7b72d634c7a30600~c5_100x100.jpeg?from=4010531038","https://p6-dy-ipv6.byteimg.com/img/tos-cn-avt-0015/70e93c2224fc560d7b72d634c7a30600~c5_100x100.jpeg?from=4010531038"]},"avatar_medium":{"uri":"tos-cn-avt-0015/70e93c2224fc560d7b72d634c7a30600","url_list":["https://p9-dy.byteimg.com/img/tos-cn-avt-0015/70e93c2224fc560d7b72d634c7a30600~c5_720x720.jpeg?from=4010531038","https://p29-dy.byteimg.com/img/tos-cn-avt-0015/70e93c2224fc560d7b72d634c7a30600~c5_720x720.jpeg?from=4010531038","https://p3-dy-ipv6.byteimg.com/img/tos-cn-avt-0015/70e93c2224fc560d7b72d634c7a30600~c5_720x720.jpeg?from=4010531038"]},"follower_count":9315,"custom_verify":"","region":"CN","is_gov_media_vip":false,"short_id":"10566482","signature":"","avatar_larger":{"uri":"tos-cn-avt-0015/70e93c2224fc560d7b72d634c7a30600","url_list":["https://p26-dy.byteimg.com/img/tos-cn-avt-0015/70e93c2224fc560d7b72d634c7a30600~c5_1080x1080.jpeg?from=4010531038","https://p29-dy.byteimg.com/img/tos-cn-avt-0015/70e93c2224fc560d7b72d634c7a30600~c5_1080x1080.jpeg?from=4010531038","https://p3-dy-ipv6.byteimg.com/img/tos-cn-avt-0015/70e93c2224fc560d7b72d634c7a30600~c5_1080x1080.jpeg?from=4010531038"]},"favoriting_count":1459,"total_favorited":"462000","unique_id":"","geofencing":null,"policy_version":null},"extra":{"now":1602950144000,"logid":"202010172355440101980230914C625738"}}

注意:程序需要chromedp,这个是开源的模拟chrome的工具.需要自行安装,不希望安装这个工具可以接着往下看:使用Docker部署

 

# Docker便捷部署

编写了一个Dockerfile,用于docker部署,这样本机就可以不用折腾chromedp相关的环境问题了。  

Dockerfile的内容如下:

FROM centos:7.6.1810

# 安装中文字体和chromedp
RUN yum install -y wget && \
    yum install -y wqy-microhei-fonts wqy-zenhei-fonts && \
    wget https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm && \
    yum install -y ./google-chrome-stable_current_*.rpm && \
    google-chrome --version && \
    rm -rf *.rpm

# 设置go mod proxy 国内代理和golang path
ENV GOPROXY=https://goproxy.io GOPATH=/gopath PATH="${PATH}:/usr/local/go/bin"
# 定义使用的Golang 版本
ARG GO_VERSION=1.13.6

# 安装 golang 1.13.6
RUN wget "https://dl.google.com/go/go$GO_VERSION.linux-amd64.tar.gz" && \
    rm -rf /usr/local/go && \
    tar -C /usr/local -xzf "go$GO_VERSION.linux-amd64.tar.gz" && \
    rm -rf *.tar.gz && \
    go version && go env;


WORKDIR $GOPATH
COPY . douyin

RUN cd douyin && go build -o app;

EXPOSE 8000

CMD ["douyin/app"]

 

 自行打包成docker镜像后就可以使用了,命令如下:

1. cd到项目目录下,目录应该有四个文件

[root@normal11 douyin]# ls
Dockerfile  go.mod  go.sum  main.go

2. 打包docker镜像

 docker build -t douyin .

3. 启动docker容器

docker run --rm -p 8000:8000 douyin

4. 访问接口获取数据

linux: 

curl localhost:8000/index?url=你要获取数据的分享页面链接

windows:

在浏览器打开localhost:8000/index?url=你要获取数据的分享页面链接

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值