抖音爬虫解决方案 获取用户视频/信息 java+appium1.7.2+夜神模拟器+fiddler4

首先本篇讲解的是爬取首页刷到的用户的视频   用户关注用户的信息

所用到的工具有  环境搭建

1.appium1.7.2   : 自动化测试工具 (也可以直接在手机上运行本篇内容 )

2.Appium-desktop   appium桌面可视化工具

3.fiddler4   抓包工具 用于抓取抖音服务器返回的数据

4.jsoup      java的爬虫利器   java-client  和appium交互的jar

<dependency>
			<groupId>io.appium</groupId>
			<artifactId>java-client</artifactId>
			<version>5.0.4</version>
		</dependency>
<dependency>
			<groupId>org.jsoup</groupId>
			<artifactId>jsoup</artifactId>
			<version>1.11.3</version>
		</dependency>

5.安卓sdk   环境需要

6.node6.11.3   appium环境需要

appium-doctor 和python不用安装

(有问题可以留言 写的不详细 , 只写了大致思路 , 代码什么的太多了 自己在项目里看吧 )

先上几张图

这是爬取视频的大体过程(公司电脑太垃圾 模拟器运行)

这是爬取到每个视频的信息(没有下完)  为了显示方便 把他做成TXT文件了 , 在实际使用中最好存到数据库中(有建好的数据库)

基本思路都是一样的 首先抓包获取url分析 下载一个fiddler

链接:本文所需资料 密码:po4y

nox 夜神模拟器 appium 和安卓sdk 还有jdk的安装就不说了 使用的时候记得把数据库改了

fiddler不会使用的参考这篇文章

然后打开抖音 进入用户主页

用户主页

可以看到主页有两个url 第一个是获取用户基本信息的

https://aweme-eagle.snssdk.com/aweme/v1/user/?user_id=105099200058&retry_type=no_retry&iid=49322627881&device_id=58513191314&ac=wifi&channel=aweGW&aid=1128&app_name=aweme&version_code=300&version_name=3.0.0&device_platform=android&ssmix=a&device_type=HUAWEI+MLA-AL10&device_brand=HUAWEI&language=zh&os_api=19&os_version=4.4.2&uuid=863064010401414&openudid=408d5c6d389f2015&manifest_version_code=300&resolution=1280*720&dpi=240&update_version_code=3002&_rticket=1541398890767&ts=1541398889&as=a1d5be8d79d6ab811f4233&cp=e56ab15c9ef7d614e1skao&mas=018568424902e4953147e90285b7cbd6e5cccc4c2c668c8c1c4686

主要参数 : 这两个链接最主要的就是这个user_id 其他的都可以复制粘贴

user_id  : 用户抖音的真实ID 基本所有数据的获取最主要的就是user_id

ts : 当前时间的时间戳 秒为单位

as cp , mas  : 这三个都是加密参数 大佬可以尝试破解一下 如果只针对于爬取某个单独用户的视频的话 这三个参数复制一下就可以用 (目前抖音3.0版本 , 2018.11.5号)

返回的数据 :

只截取一些主要的数据 :

total_favorited : 获赞数
following_count : 关注数
mplatform_followers_count :粉丝数
aweme_count : 	作品数
dongtai_count : 动态数
favoriting_count :喜欢数
avatar_medium  :用户头像
short_id    : 用户抖音号
province : 省份
city : 城市
country : 国家
uid: 用户uid
nickname : 昵称
signature : 用户简介

这样 有了这个url  在你知道用户的user_id 以后 就可以通过该url获取用户的基本数据 (不包括视频信息)

接下来 第二个url 是获取用户的视频信息的

https://aweme.snssdk.com/aweme/v1/aweme/post/?max_cursor=0&user_id=105099200058&count=20&retry_type=no_retry&iid=49322627881&device_id=58513191314&ac=wifi&channel=aweGW&aid=1128&app_name=aweme&version_code=300&version_name=3.0.0&device_platform=android&ssmix=a&device_type=HUAWEI+MLA-AL10&device_brand=HUAWEI&language=zh&os_api=19&os_version=4.4.2&uuid=863064010401414&openudid=408d5c6d389f2015&manifest_version_code=300&resolution=1280*720&dpi=240&update_version_code=3002&_rticket=1541398890999&ts=1541398890&as=a1f53edd9a269b61bf4233&cp=e066b95fa6f8de11e1skao&mas=01412d4994e2e4bb1ed91dace119a90400cccc4c2c66468c6c469c
 

基本参数可以参照第一个链接, 有一点需要注意的是 max_cursor 和 count 这两个参数

max_cursor  :  根据这个参数来返回用户的视频列表 , 第一次访问时 , 本参数为0 , 会返回最新的20个视频 (count为20时)

                       这个url返回的数据中有一个参数同样为 max_cursor 的 , 这个数据就作为获取第21-40 个视频的max_cursor

                       然后以此类推 ,

count     : 返回视频的数量 用默认20就好 , 如果你修改的话 也会根据你的count返回对应的数量

通过解析第二个url返回的数据 得到

aweme_list : 视频列表
has_more   : 是否还有视频 , 1有 , 0没有
list的每个子项 :
    aweme_id      : 视频id
    desc          : 视频标题
    share_count   : 分享次数
    digg_count    : 点赞次数
    comment_count : 评论数
    forward_count : 转发数
    video         : 视频的其他信息
    video的每个子项:
        radio             : 清晰度
        dynamic_cover     : 封面gif(数组 每一项都是)
        origin_cover      : 封面图片(数组 每一项都是)
        download_addr     : 视频下载地址(数组 每一项都是)
        play_addr         : 也可以用来下载

这样 这两个url就解析完成 , 有了这两个url , 再知道用户的user_id , 就可以把一个用户的视频全部下载下来

需要注意的一点是 , 需要模拟手机请求 , 设置下user-Agent 这个请求头 从浏览器调试的时候自己找就好

Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Mobile Safari/537.36

接下来除了这两个接口 其余接口都是有时效的 , 所有不能只在java程序中执行

本篇的解决方案 : 使用appium自动化测试工具 模拟用户行为 操控夜神模拟器 ,  再用fiddler保存请求数据 ,用java解析得到的数据

搭建完环境后 , 配置夜神模拟器 和appium

长按 修改网络 根据自己ip 和 fiddler 配置

fiddler 自动保存数据配置

这是本文需要的配置 :

static function OnBeforeResponse(oSession: Session) {
        if (m_Hide304s && oSession.responseCode == 304) {
            oSession["ui-hide"] = "true";
        }
		//aweme 用户主页视频详情
		if ((oSession.HostnameIs("api.amemv.com")|| oSession.HostnameIs("aweme.snssdk.com")) && oSession.uriContains("/aweme/v1/aweme/post")) {
			oSession.utilDecodeResponse();
			//消除保存的请求可能存在乱码的情况
			
			var url = oSession.url.ToString();
			var demo1 = getAllUrlParams(url);
			
			var fso;
			var file;
			fso = new ActiveXObject("Scripting.FileSystemObject");
			//文件保存路径,可自定义
			file = fso.OpenTextFile("D:/douyin/request/" + demo1.user_id +"_aweme.txt",8 ,true,true);
			
			file.writeLine( oSession.GetResponseBodyAsString());
			
			file.close();

		}
		//用户主页信息详情
		if ((oSession.HostnameIs("api.amemv.com")|| oSession.HostnameIs("aweme.snssdk.com")) && oSession.uriContains("/aweme/v1/user")) {
			oSession.utilDecodeResponse();
			//消除保存的请求可能存在乱码的情况
			
			var url = oSession.url.ToString();
			var demo1 = getAllUrlParams(url);
			
			var fso;
			var file;
			fso = new ActiveXObject("Scripting.FileSystemObject");
			//文件保存路径,可自定义
			file = fso.OpenTextFile("D:/douyin/request/" + demo1.user_id +"_user.txt",8 ,true , true);
		
			file.writeLine( oSession.GetResponseBodyAsString());
			
			file.close();

		}
		
		//用户关注人详情
		if ((oSession.HostnameIs("api.amemv.com")|| oSession.HostnameIs("aweme.snssdk.com"))&& oSession.uriContains("/aweme/v1/user/following/list")) {
			oSession.utilDecodeResponse();
			//消除保存的请求可能存在乱码的情况
			
			var url = oSession.url.ToString();
			var demo1 = getAllUrlParams(url);
			
			var fso;
			var file;
			fso = new ActiveXObject("Scripting.FileSystemObject");
			//文件保存路径,可自定义
			file = fso.OpenTextFile("D:/douyin/request/" + demo1.user_id +"_following.txt",8 ,true , true);
			
			file.writeLine( oSession.GetResponseBodyAsString());
			
			file.close();

		}
		
    }
static function getAllUrlParams(url) {
		var queryString = url ? url.split('?')[1] : "";
		var obj = {};
		if (queryString) {
			queryString = queryString.split('#')[0];
			var arr = queryString.split('&');

			for (var i = 0; i < arr.length; i++) {
				var a = arr[i].split('=');
				var paramNum = undefined;
				var paramName = a[0].replace(/\[\d*\]/, function (v) {
						paramNum = v.slice(1, -1);
						return '';
					});

 
				var paramValue = typeof(a[1]) === 'undefined' ? true : a[1];
               
				if (obj[paramName]) {
					if (typeof obj[paramName] === 'string') {
						obj[paramName] = [obj[paramName]];
					}
					if (typeof paramNum === 'undefined') {
						obj[paramName].push(paramValue);
					}
					else {
						obj[paramName][paramNum] = paramValue;
					}
				}
				else {
					obj[paramName] = paramValue;
				}
			}
		}
		return obj;
	}

在D盘新建一个douyin文件夹

接下来使用java写appium启动抖音脚本

AndroidDriver driver;
        DesiredCapabilities cap = new DesiredCapabilities();
        cap.setCapability("automationName", "Appium");//appium做自动化
//    cap.setCapability("app", "C:\\software\\jrtt.apk");//安装apk
//    cap.setCapability("browserName", "chrome");//设置HTML5的自动化,打开谷歌浏览器
        cap.setCapability("deviceName", "127.0.0.1:62001");//设备名称
        cap.setCapability("platformName", "Android"); //安卓自动化还是IOS自动化
        cap.setCapability("platformVersion", "4.4.2"); //安卓操作系统版本
        //cap.setCapability("automationName", "UIAutomator2"); //支持新版安卓在手机上运行记//得打开
        //cap.setCapability("udid", "127.0.0.1:62001"); //设备的udid (adb devices 查看到的)
        cap.setCapability("appPackage", "com.ss.android.ugc.aweme");//被测app的包名
        cap.setCapability("appActivity", "com.ss.android.ugc.aweme.splash.SplashActivity");//被测app的入口Activity名称
        //cap.setCapability("unicodeKeyboard", "True"); //支持中文输入
        // cap.setCapability("resetKeyboard", "True"); //支持中文输入,必须两条都配置
        cap.setCapability("noSign", "True"); //不重新签名apk
        cap.setCapability("noReset", "True"); //不重新签名apk
        //cap.setCapability("newCommandTimeout", "30"); //没有新命令,appium30秒退出
        URL url = new URL("http://127.0.0.1:4723/wd/hub");
        driver = new AndroidDriver(new URL("http://127.0.0.1:4723/wd/hub"), cap);//把以上配置传到appium服务端并连接手机
        driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);//隐式等待

        Thread.sleep(10000);//休息10s避免广告

deviceName 通过 adb devices 命令查找

如果遇到以下问题 :

1 . adb 不存在  : 在sdk/platform-tools  添加到path环境变量中

把夜神模拟器的bin 添加到path环境变量中

2 . adb server version (31) doesn't match this client (36)

夜神的adb和sdk的不一致 把sdk中的 复制到夜神的安装目录中

找到安装的路径:\Nox\bin,里面有个nox_adb.exe,其实就是adb.exe,为了避免冲突在nox里面换了个名称。

接下来把android-sdk里面的adb.exe版本复制出来,然后改个名称叫nox_adb.exe,替换nox安装的路径:\Nox\bin下的nox_adb.exe文件就行了

3 . adb server version (31) doesn't match this client (40); killing...
could not read ok from ADB Server
* failed to start daemon
error: cannot connect to daemon 

证明端口被占用  大部分是360安全卫士 也可以 修改端口

这都是我遇到的坑

app包名和入口 通过 aapt dump badging douyin3.0.apk

包名:

入口:

具体逻辑 : 在主页上滑 然后左滑进入用户主页 这时候fiddler会自动获取到 这两个文件

然后点击关注view

view获取通过appium-desktop 查看

打开appium 直接点击start 然后点击图下按钮

配置如下

{
  "platformName": "Android",
  "deviceName": "127.0.0.1:62001",
  "appPackage": "com.ss.android.ugc.aweme",
  "appActivity": "com.ss.android.ugc.aweme.splash.SplashActivity",
  "platformVersion": "4.4.2"
}

然后点击右下方start session 启动 启动完成界面如下

点击上方

找到关注view

右边会显示

这个a7t就是这个view的ID 也可以通过下面的xpath路径获取(不推荐 不好用 经常找不到)

然后点击

可以看到

java脚本 的获取方式

点击

可以获取到xy坐标

接下来就可以编写脚本了

    //获取用户的关注信息
    public static void getConcernUser(AndroidDriver driver) throws InterruptedException {

        //获取关注的view
        WebElement webElement = getElementById("a7t", driver);

        if (webElement != null ){
            //获取关注数量view
            WebElement nums = getElementById("a7u", driver);

            String text = nums.getAttribute("text");
            if (StringUtils.isNotBlank( text )) {
                int count = (Integer.parseInt(text)- 10 ) / 5;
                //点击关注页
                webElement.click();
                for (int i = 0 ; i <= count ; i ++){
                    //屏幕上滑
                    Swipe.swipeUp(driver);
                }
                //加载完所有关注用户信息 返回用户视频首页
                Swipe.back(driver);//点击返回
            }
        }
        //右滑 进入视频首页
        Swipe.back(driver);//点击返回
    }
 public static WebElement getElementById(String id , AndroidDriver driver){
        List<WebElement> list = driver.findElements(By.id(id));
        if (list != null && !list.isEmpty()){
           return list.get(0);
        }
        return null;
    }
    //下滑进入用户主页
    public static void upSelectUser(AndroidDriver driver) throws InterruptedException {
        //下滑两次
        Swipe.swipeUp(driver);

        /*WebElement arw = getElementById("arw", driver);
        if (arw != null )
        arw.click(); //进入用户主页*/

        Swipe.swipeLeft(driver);


    }
    public static void selectUser(String shortId , AndroidDriver driver){

        //右滑搜索
        Swipe.swipeRight(driver);
        WebElement a65 = driver.findElementById("a65");
        a65.click();
        //driver.pressKeyCode(8);
        a65.sendKeys(  shortId);


        //搜索按钮点击
        driver.findElementById("a67").click();

        //用户头像 点击
        List<WebElement> atk = driver.findElements(By.id("atk"));
        if (atk != null && !atk.isEmpty()){
            atk.get(0).click();
        }else{
            //头像点击
            atk = driver.findElements(By.id("arw"));
            if ( atk != null && ! atk.isEmpty())
            atk.get(0).click();
        }
    }

这些是封装好部分的脚本  接下来可以直接运行

解析fiddler获取到的文件 在项目中的DouYinVideoUtl 类中

项目文件是douyin.zip

数据库文件 : douyin.sql

还可以实现自动搜索啊 各种功能  大家可以自己研究 在idea中运行 直接运行主main 启动springboot

打包成jar 通过java -jar  命令运行

发布了15 篇原创文章 · 获赞 21 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/q690080900/article/details/83748488
今日推荐