抖音爬虫解决方案 获取用户视频/信息 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的安装就不说了 使用的时候记得把数据库改了
然后打开抖音 进入用户主页
可以看到主页有两个url 第一个是获取用户基本信息的
主要参数 : 这两个链接最主要的就是这个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 是获取用户的视频信息的
基本参数可以参照第一个链接, 有一点需要注意的是 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 配置
这是本文需要的配置 :
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 命令运行