Введение
Если вы видите видео блогера Douyin, которое вам особенно интересно, и хотите сбросить их все, что вам делать? Ниже показано, как использовать python для экспорта всей видеоинформации определенного пользователя.
Анализ захвата пакетов
- Инструменты разработчика Chrome Инструменты разработчика браузера Chrome
В приложении Douyin скопируйте адрес домашней страницы видеоблогера, например:http://v.douyin.com/kGcU4y/
, Используйте браузер Chrome, чтобы зарегистрироваться на стороне ПК, и имитируйте мобильный телефон, выберите здесь iPhone, а затем поместите скопированный адрес домашней страницы в браузер для доступа, и страница перейдет кhttps://www.iesdouyin.com/share/user/110677980134
- Потяните вниз домашнюю страницу, выберите вкладку Network => XHR и увидите аналогичный запрос.
:authority: www.iesdouyin.com
:method: GET
:path: /web/api/v2/aweme/post/?user_id=110677980134&sec_uid=&count=21&max_cursor=1561112910000&aid=1128&_signature=3Xf-nxAQgGfUO4SKisB.Ld13.o&dytk=061ae6e81229e178146aa674327eba89
:scheme: https
accept: application/json
accept-encoding: gzip, deflate, br
accept-language: zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7,zh-TW;q=0.6,da;q=0.5
cookie: tt_webid=6690145457198417412; _ga=GA1.2.605400954.1557670882; _ba=BA0.2-20181226-5199e-GIJXgXk9ajNkyFhmv7Wy; _gid=GA1.2.1914501522.1562857517
referer: https://www.iesdouyin.com/share/user/110677980134
user-agent: Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1
x-requested-with: XMLHttpRequest
Скриншот возврата данных
Анализируя URL-адрес запроса ajaxhttps://www.iesdouyin.com/web/api/v2/aweme/post/?user_id=110677980134&sec_uid=&count=21&max_cursor=1559299764000&aid=1128&_signature=3Xf-nxAQgGfUO4SKisB.Ld13.o&dytk=061ae6e81229e178146aa674327eba89
Полученные параметры запроса в основном включают в себя:
поле | тип | инструкция |
---|---|---|
user_id | int | Идентификатор учетной записи Douyin |
count | int | Количество возвращаемых данных, используйте значение по умолчанию 21. |
max_cursor | int | Запрошенный курсор, каждый запрос принимает значение max_cursor, возвращенное предыдущим запросом. |
aid | int | Используйте значение по умолчанию 11128 |
_signature | string | Подпись параметра при каждом запросе |
dytk | string | Один параметр на запрос |
Как получить параметры:
- user_id получает доступ к адресу домашней страницы и перенаправляет URL-адрес (
https://www.iesdouyin.com/share/user/110677980134
) может получить user_id - dytk включает этот параметр в исходный код веб-страницы главной страницы, фрагмент исходного кода веб-страницы
(function() {
$(function(){
__M.require('douyin_falcon:page/reflow_user/index').init({
uid: "110677980134",
dytk: '061ae6e81229e178146aa674327eba89'
});
});
})();
Этот параметр приобретается положительным
- _signature получить сложнее, Douyin запутывает и сжимает внешний js-код, поэтому напрямую анализировать процесс алгоритма непросто, но он может выполнить код алгоритма подписи и вернуть соответствующий результат подписи.
- Вы можете использовать nodejs или selenium webdriver для выполнения кода js. Здесь рекомендуется использовать selenium webdriver. Среда выполнения js для nodejs отличается от среды браузеров. Вычисленный результат подписи не может пройти проверку. Selenium webdriver может вызывать локальный браузер для расчета Подпись может быть согласована с подписью, рассчитанной путем прямого доступа браузера.
- js код после форматирования,Нажмите, чтобы просмотреть, выполнить метод js
_bytedAcrawler.sign("110677980134")
Подпишите параметр
Реализация кода для экспорта списка видео на главной странице
def get_user_video_list_by_uid(user_id, cursor=0):
url = 'https://www.iesdouyin.com/web/api/v2/aweme/post/?'
sign, dytk = signature(user_id)
tk_logger.info("sign:%s,dytk:%s" % (sign, dytk))
if sign is None or dytk is None:
tk_logger.log("sign [%s] or dytk [%s] is none" % (sign, dytk))
return None
headers = dict_merge(CHROME_HEADER, {
"Accept": "application/json",
"X-Requested-With": "XMLHttpRequest",
})
params = {
"user_id": user_id,
"count": "21",
"max_cursor": cursor,
"aid": "1128",
"_signature": sign,
"dytk": dytk
}
res = requests.get(url, headers=headers, params=params)
tk_logger.info("request url: %s" % res.url)
content = res.content.decode("utf8")
jsn = json.loads(content)
return jsn
Получить информацию о списке видео
Получить фрагмент информационного кода видео
def get_video_detail_by_id(video_id):
url = "https://aweme-hl.snssdk.com/aweme/v1/aweme/detail/?version_code=6.5.0&pass-region=1&pass-route=1&js_sdk_version=1.16.2.7&app_name=aweme&vid=9D5F078E-A1A9-4F64-81C7-F89CA6A3B1DC&app_version=6.5.0&device_id=34712926793&channel=App%20Store&mcc_mnc=46011&aid=1128&screen_width=750&openudid=263bd93f02801d126ca004edccbff8f6e1b19f51&os_api=18&ac=WIFI&os_version=12.3.1&device_platform=iphone&build_number=65014&device_type=iPhone9,1&iid=74239983401&idfa=F39B285A-4B4F-4874-9D7E-C728A892BF6D"
data = {"aweme_id": video_id}
headers = {
"sdk-version": "1",
"x-Tt-Token": "00fc1e7950db67b5f43a312e9265cdfee513ea70c36d918c871f3bb553347f3db50ffca143b8722327b345816a75efca071d",
"User-Agent": "Aweme 6.5.0 rv:65014 (iPhone; iOS 12.3.1; en_CN) Cronet",
"Content-Type": "application/x-www-form-urlencoded",
"Cookie": "tt_webid=6636348554880222728; __tea_sdk__user_unique_id=6636348554880222728; odin_tt=76d9b82d6e6f2ddfc99719a5b5d44a7d703cf977f0f7bddf8537f93920d57cb9ec33162ee47868b760f6b09e69209bb2f90bad220b75678af850a0dfa9f056e2; install_id=74239983401; ttreq=1$dab0516952a4157c0c11d4993533c09d6e45fc94; sid_guard=fc1e7950db67b5f43a312e9265cdfee5%7C1559955316%7C5184000%7CWed%2C+07-Aug-2019+00%3A55%3A16+GMT; uid_tt=0afcb06309f632d872799ec0ac3b2c80; sid_tt=fc1e7950db67b5f43a312e9265cdfee5; sessionid=fc1e7950db67b5f43a312e9265cdfee5",
"X-Khronos": "1559956401",
"X-Gorgon": "8300000000002e40eee38cad71d14037bd1385d18bc973f094f5",
}
ret = {}
res = requests.post(url, data=data, headers=headers)
if res.status_code == 200:
# tk_logger.info("video detail raw:%s" % res.content.decode("utf8"))
jsn = json.loads(res.content)
detail = jsn.get("aweme_detail", {})
video_info = get_video_info(detail)
user_info = get_user_info(detail)
play_addr = get_play_address(detail)
video_cover = get_video_cover(detail)
ret["video_info"] = video_info
ret["user_info"] = user_info
ret["play_addr"] = play_addr
ret["video_cover"] = video_cover
else:
raise TKException("get video detail failed [%s][%d]" % (url, res.status_code))
return ret
Скачать фрагменты кода видео
detail = get_video_detail_by_id(video_id)
def download_video(detail):
url = detail.get("play_addr", {}).get("url_list", [])
if len(url) == 0:
raise TKException("cannot get video url list [%s]" % detail)
url = url[0]
folder = DOWNLOAD_DIR + '/' + detail.get('user_info', {}).get("uid", "unknown")
if not os.path.exists(folder):
os.mkdir(folder)
video_id = detail.get('video_info', {}).get('statistics', {}).get('aweme_id')
# filename = "%s/%s" % (folder, detail.get("video_info", {}).get("desc", video_id) + ".mp4")
filename = "%s/%s" % (folder, video_id + ".mp4")
tk_logger.info("download video %s" % url)
if os.path.isfile(filename):
file_size = get_remote_file_size(url)
if file_size == os.path.getsize(filename):
tk_logger.info("file already downloaded, skip ...")
return
else:
tk_logger.info("download file , file size:%d" % file_size)
res = requests.get(url, headers=IOS_HEADER)
if res.status_code == 200:
with open(filename, "wb") as fp:
for chunk in res.iter_content(chunk_size=1024):
fp.write(chunk)
else:
raise TKException("download video [%s] failed [%d]" % (url, res.status_code))
скачать видео
утверждение
Это руководство предназначено только для общения и обучения, а не для коммерческих целей.Посмотреть исходный текст