抖音短视频爬取实战

需求

爬取用户的抖音号、粉丝数、关注数、点赞数

目的:

某公司做生鲜电商平台,这个公司平台想在流量平台上投放广告,发现抖音这个大平台,

抖音拥有不错的用户流量,通过数据分析预测在抖音投放公司广告,看他是否会对公司利益带来收益,这样就要分析抖音数据,抖音的用户画像,判断用户群体与公司的匹配度。

那么分析昵称,粉丝书,关注书,和公司有什么关系,通过分析可以知道谁的粉丝突然就增多了,谁的点赞数增多,那么这个时候就可以和广告合作,拍广告或者做营销宣传,或者根据点赞趋势,可以知道用户喜欢的视频,,这样就可以想办法把公司产品融入到用户喜欢的视频中,公关公司根据数据,找到网红黑马,进行营销包装。

 

三 粉丝数据接口

使用fiddler抓包

  https://aweme.snssdk.com/aweme/v1/user/follower/list/?user_id=105178085411&max_time=1556944953&count=20&offset=0&source_type=1&address_book_access=1&gps_access=1&retry_type=no_retry&iid=70850033498&device_id=67587886667&ac=wifi&channel=tengxun_new&aid=1128&app_name=aweme&version_code=600&version_name=6.0.0&device_platform=android&ssmix=a&device_type=SM-G955F&device_brand=samsung&language=zh&os_api=22&os_version=5.1.1&uuid=354730011196544&openudid=1c45444f5846a419&manifest_version_code=600&resolution=720*1280&dpi=240&update_version_code=6002&_rticket=1556944951543&mcc_mnc=46007&ts=1556944953&as=a115515c29b3bc480d4611&cp=1c3bc6579cd1ca83e1%5DcKg&mas=01f0b2caf2e5cf4c82c18f011cc8e22af58c8c6c2c260c1c2cc646

 

 

mitmproxy中间人代理

  发现请求头中的加密数据太多,我们换成了mitmproxy中间人代理的方式抓取数据包,并保存我们需要的数据。

 

粉丝请求url

https://aweme.snssdk.com/aweme/v1/user/follower/list/

 

编写mitproxy处理响应体数据,代码如下

  def response(flow):
      if 'aweme/v1/user/follower/list/' in flow.request.url:
          with open('user.txt','w') as f:
              f.write(flow.response.text)

 

启动 mitmdump

 mitmdump -s decode_douyin_fans.py -p 8889

 

手机端或者模拟器端,设置好代理,启动抖音刷粉丝页面,生成txt文件,验证,获取的数据为粉丝列表响应数据。

 

数据清洗过程

 

入库

mitmdump response拦截代码

  import json
  from handle_db import mongo_info
  ​
  ​
  def response(flow):
      if 'aweme/v1/user/follower/list/' in flow.request.url:
          for user in json.loads(flow.response.text)['followers']:
              douyin_info={}
              douyin_info['share_id']=user['uid']
              douyin_info['douyin_id']=user['short_id']
              douyin_info['nickname']=user['nickname']
              mongo_info.save_task(douyin_info)
              # print(douyin_info)

 

handle_db.py数据库操作代码

 import pymongo
  ​
  class Connect_mongo(object):
      def __init__(self):
          #连接数据库,如果没有数据,则创建数据库
          self.client=pymongo.MongoClient(host='106.12.108.236',port=27017)
          self.db=self.client['douyindb']
  ​
  ​
  ​
      def save_task(self,task):
          '保存粉丝信息'
          db_collection=self.db['douyin']
          #找到就更新,没找到就新增
          db_collection.update({'share_id':task['share_id']},task,True)
  ​
      def handle_get_task(self):
          #获取到数据,并删除数据库中的文档
          return self.db['douyin'].find_one_and_delete({})
  ​
  ​
  mongo_info=Connect_mongo()

 

appium实现抖音app自动滑动实现粉丝数据包的处理

1 测试是否连通,代码如下

  from appium import webdriver
  #等待元素加载
  from selenium.webdriver.support.ui import WebDriverWait
  ​
  #定义设备参数
  cap={
      "platformName": "Android",
      "platformVersion": "5.1.1",
      "deviceName": "127.0.0.1:62025",
      "appPackage": "com.ss.android.ugc.aweme",
      "appActivity": "com.ss.android.ugc.aweme.main.MainActivity",
      "noReset": True,
      'unicodekeyboard':True, #使用unicode输入法
      'resetkeyboard':True, #还原输入法
  }
  ​
  driver =webdriver.Remote('http://localhost:4723/wd/hub',cap)

 

2 连通后使用uiautomator viewer,获取空间的id,xpath...表达式

注释事项:抓取截屏数据的时候,必须等待数据加载完毕,或者把视频暂停,否则报错

此过程使用了appium和uiautomator viewer交替定位元素.

 

最终appium代码,如下

  import time
  ​
  from appium import webdriver
  #等待元素加载
  from selenium.webdriver.support.ui import WebDriverWait
  ​
  #定义设备参数
  cap={
      "platformName": "Android",
      "platformVersion": "6.0.1",
      "deviceName": "758ca8ba",
      "appPackage": "com.ss.android.ugc.aweme",
      "appActivity": "com.ss.android.ugc.aweme.main.MainActivity",
      "noReset": True,
      'unicodekeyboard':True, #使用unicode输入法
      'resetkeyboard':True, #还原输入法
  }
  ​
  driver =webdriver.Remote('http://localhost:4723/wd/hub',cap)
  ​
  def get_size():
      #获取设备屏幕大小
      x=driver.get_window_size()['width']
      y=driver.get_window_size()['height']
      return x,y
  ​
  #点击搜索框
  try:
      if WebDriverWait(driver, 10).until(lambda x: x.find_element_by_id("com.ss.android.ugc.aweme:id/aqz")):
          driver.find_element_by_id("com.ss.android.ugc.aweme:id/aqz").click()
  except:
      pass#定位搜索框
  try:
      if WebDriverWait(driver, 10).until(lambda x: x.find_element_by_xpath(
              "//android.widget.EditText[@resource-id='com.ss.android.ugc.aweme:id/agq']")):
          driver.find_element_by_xpath(
              "//android.widget.EditText[@resource-id='com.ss.android.ugc.aweme:id/agq']").send_keys('191433445')
  except:
      pass#点击搜索
  if WebDriverWait(driver,10).until(lambda x:x.find_element_by_xpath("//android.widget.TextView[@resource-id='com.ss.android.ugc.aweme:id/agt']")):
      driver.find_element_by_xpath("//android.widget.TextView[@resource-id='com.ss.android.ugc.aweme:id/agt']").click()
  ​
  #点击用户,进入个人主页
  if WebDriverWait(driver,10).until(lambda x:x.find_element_by_xpath("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout[2]/android.widget.RelativeLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.support.v4.view.ViewPager/android.widget.LinearLayout/android.widget.FrameLayout/android.view.ViewGroup/android.support.v7.widget.RecyclerView/android.widget.LinearLayout[1]/android.widget.LinearLayout[1]/android.support.v7.widget.RecyclerView/android.widget.RelativeLayout/android.widget.FrameLayout/android.widget.ImageView[2]")):
      el4 = driver.find_element_by_xpath("/hierarchy/android.widget.FrameLayout/android.widget.LinearLayout/android.widget.FrameLayout/android.widget.FrameLayout/android.widget.FrameLayout[2]/android.widget.RelativeLayout/android.widget.FrameLayout/android.widget.RelativeLayout/android.support.v4.view.ViewPager/android.widget.LinearLayout/android.widget.FrameLayout/android.view.ViewGroup/android.support.v7.widget.RecyclerView/android.widget.LinearLayout[1]/android.widget.LinearLayout[1]/android.support.v7.widget.RecyclerView/android.widget.RelativeLayout/android.widget.FrameLayout/android.widget.ImageView[2]")
      el4.click()
  ​
  # 点击粉丝
  if WebDriverWait(driver,10).until(lambda x:x.find_element_by_xpath("//android.widget.TextView[@text='粉丝']")):
      driver.find_element_by_xpath("//android.widget.TextView[@text='粉丝']").click()
  ​
  # 等待页面刷新
  time.sleep(3)
  ​
  #开始滑动
  l =get_size()
  x1=int(l[0]*0.5)
  y1=int(l[1]*0.9)
  y2=int(l[1]*0.25)
  ​
  while True:
      if '没有更多了' in driver.page_source:
          break
      else:
          #初始位置x1,y1   结束位置为:x1,y2
          driver.swipe(x1,y1,x1,y2)
          time.sleep(0.2)
  ​

 

电脑开wifi给手机用,使用mitmdump开代理,有点坑,ip找这里,我用的是网线!!!

 

代码执行顺序

1 启动弄mitmproxy

  mitmdump -p 8889 -s decode_douyin_fans.py

 

2 启动appnium程序,把粉丝信息入库

  python handle_appium_douyin.py

 

3 执行采集分享id对应用户的详细信息,并入库

  python handle_share_web.py

 

多任务端

 

注意事项

查找模拟器对应端口,找到对应的设备.

没有粉丝处理

返回,清空,重新输入

 

 

封装兼容不同设备函数

 

启动多进程

 

 

伪装爬虫

 

抖音视频下载

网页源码中找到加密js

 

请求参数分析 max_cursor

网页中加的dytk

解密signature找寻流程

网页张找到js加密文件,全局搜索(点击右侧三个点,或者ctrl +f)

 

继续找

 

一层一层的找,找到所有的流程

找完之后,开始构建html文件和js文件

html 头部文件

 

 

html尾部文件(复制调用的js函数到脚部文件)

使用文件合并的方法,生成html文件

 

每次打开生成的html 自动生成signature 秘钥

 

js 破解思路:

  使用开发者工具,全局搜索加密字段所在的文件,理清楚加密过程,即执行流程,自己构建html文件,模拟加密执行函数,无需自己写,只需要拷贝js代码.

 

项目总结

个人数据 --TTF 字体混淆

 

fontedit解析字体,找到映射关系表

 

映射表,正则替换

 

粉丝数据抓取

appium模拟滑动+mitmdump解析数据

 

使用了uiautomator viewer和appium结合进行元素定位,进行元素的操作,使用mitmdump中间人攻击技术,在请求分数数据的时候,把响应数据进行解析,入库.

 

视频抓取阶段

编写破解文件

 

注意:

1 ttf字体数据对应关系,如果关系更改了,爬虫就需要做响应的更改.

2 一般一个名人最多获取5000条粉丝数据

3 移动设备设置代理抓包后,如果无法联网或无法解析https数据时候,需要安装xpose框架+justtrustme组件进行屏蔽证书强校验.

4 在设置多设备、多进程数据抓取时候,需要设置appium服务器端的bootstrap端口,以及客户端的udid字段.

5 视频数据抓取,需要破解signature字段,并使用html文件,解析js,

拼接html文件,逆退出加密过程.

6 数据抓取需要加上代理,伪装爬虫.

7 最好使用真实的移动设备.

 

 

原文地址:https://www.cnblogs.com/angle6-liu/p/10813484.html