feat: 添加DMHeaderManager来管理Dailymotion API请求中的标头和代理设置
This commit is contained in:
parent
3bfb71d64a
commit
f178f68e65
512
oneget.py
Normal file
512
oneget.py
Normal file
@ -0,0 +1,512 @@
|
||||
import requests
|
||||
import uuid
|
||||
import random
|
||||
import time
|
||||
import copy
|
||||
from threading import Lock
|
||||
import logging
|
||||
from DB import DBVidcon
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
db = DBVidcon()
|
||||
proxiesdict = db.get_proxy_agent_dict()
|
||||
|
||||
|
||||
class DMHeaderManager:
|
||||
_headers_template = {
|
||||
'Accept': '*/*, */*',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive',
|
||||
'Content-Type': 'application/json, application/json',
|
||||
'Host': 'graphql.api.dailymotion.com',
|
||||
'Origin': 'https://www.dailymotion.com',
|
||||
'Referer': 'https://www.dailymotion.com/',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'same-site',
|
||||
'User-Agent': 'Mozilla/5.0',
|
||||
'X-DM-AppInfo-Id': 'com.dailymotion.neon',
|
||||
'X-DM-AppInfo-Type': 'website',
|
||||
'X-DM-AppInfo-Version': 'v2025-05-26T13:45:05.666Z',
|
||||
'X-DM-Neon-SSR': '0',
|
||||
'X-DM-Preferred-Country': 'tw',
|
||||
'accept-language': 'zh-CN',
|
||||
'authorization': '',
|
||||
'sec-ch-ua': '"Chromium";v="128", "Not;A=Brand";v="24", "Google Chrome";v="128"',
|
||||
'sec-ch-ua-mobile': '?0',
|
||||
'sec-ch-ua-platform': '"Windows"',
|
||||
'x-dm-visit-id': '',
|
||||
'x-dm-visitor-id': '',
|
||||
}
|
||||
|
||||
_proxies = {
|
||||
"http": "http://127.0.0.1:10808",
|
||||
"https": "http://127.0.0.1:10808"
|
||||
}
|
||||
|
||||
_user_agents = [
|
||||
'Mozilla/5.0 (Windows; U; Windows NT 6.1; en-us) AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50',
|
||||
'Mozilla/5.0 (Windows NT 6.1; rv:2.0.1) Gecko/20100101 Firefox/4.0.1',
|
||||
'Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11',
|
||||
'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; 360SE)',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/136.0.0.0 Safari/537.36',
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36',
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
self._headers_cache = None
|
||||
self._cache_lock = Lock()
|
||||
|
||||
def get_headers(self, retry: int = 2) -> dict:
|
||||
for attempt in range(retry + 1):
|
||||
try:
|
||||
return self._generate_headers()
|
||||
except Exception as e:
|
||||
logger.warning(f"[get_headers] 第 {attempt + 1} 次尝试失败: {e}")
|
||||
time.sleep(2)
|
||||
|
||||
with self._cache_lock:
|
||||
if self._headers_cache:
|
||||
logger.info("[get_headers]")
|
||||
return copy.deepcopy(self._headers_cache)
|
||||
|
||||
logger.warning("[get_headers] 基础 headers")
|
||||
return copy.deepcopy(self._headers_template)
|
||||
|
||||
def _generate_headers(self) -> dict:
|
||||
visitor_id = str(uuid.uuid4())
|
||||
visit_id = str(int(time.time() * 1000))
|
||||
traffic_segment = str(random.randint(100_000, 999_999))
|
||||
ua = random.choice(self._user_agents)
|
||||
|
||||
token_headers = {
|
||||
'Accept': '*/*',
|
||||
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Connection': 'keep-alive',
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Origin': 'https://www.dailymotion.com',
|
||||
'Pragma': 'no-cache',
|
||||
'Referer': 'https://www.dailymotion.com/',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'same-site',
|
||||
'User-Agent': ua,
|
||||
'sec-ch-ua': '"Chromium";v="136", "Google Chrome";v="136", "Not.A/Brand";v="99"',
|
||||
'sec-ch-ua-mobile': '?0',
|
||||
'sec-ch-ua-platform': '"Windows"',
|
||||
}
|
||||
|
||||
data = {
|
||||
'client_id': 'f1a362d288c1b98099c7',
|
||||
'client_secret': 'eea605b96e01c796ff369935357eca920c5da4c5',
|
||||
'grant_type': 'client_credentials',
|
||||
'traffic_segment': traffic_segment,
|
||||
'visitor_id': visitor_id,
|
||||
}
|
||||
|
||||
response = requests.post(
|
||||
'https://graphql.api.dailymotion.com/oauth/token',
|
||||
headers=token_headers,
|
||||
data=data,
|
||||
proxies=self._proxies,
|
||||
timeout=10
|
||||
)
|
||||
response.raise_for_status()
|
||||
token = response.json()['access_token']
|
||||
|
||||
new_headers = copy.deepcopy(self._headers_template)
|
||||
new_headers['authorization'] = f'Bearer {token}'
|
||||
new_headers['x-dm-visit-id'] = visit_id
|
||||
new_headers['x-dm-visitor-id'] = visitor_id
|
||||
new_headers['User-Agent'] = ua
|
||||
|
||||
with self._cache_lock:
|
||||
self._headers_cache = copy.deepcopy(new_headers)
|
||||
|
||||
return new_headers
|
||||
|
||||
|
||||
dmheader_manager = DMHeaderManager()
|
||||
|
||||
headers = dmheader_manager.get_headers()
|
||||
|
||||
import json
|
||||
|
||||
data = {
|
||||
"operationName": "SEARCH_QUERY",
|
||||
"variables": {
|
||||
"query": "朝雪录 ep",
|
||||
"shouldIncludeTopResults": True, # 是否包含热门结果
|
||||
"shouldIncludeChannels": False, # 是否包含频道
|
||||
"shouldIncludePlaylists": False, # 是否包含播放列表
|
||||
"shouldIncludeHashtags": False, # 是否包含标签
|
||||
"shouldIncludeVideos": False, # 是否包含视频
|
||||
"shouldIncludeLives": False, # 是否包含直播
|
||||
"page": 1,
|
||||
"limit": 100,
|
||||
"recaptchaToken": None
|
||||
},
|
||||
"query": """
|
||||
fragment VIDEO_BASE_FRAGMENT on Video {
|
||||
id
|
||||
xid
|
||||
title
|
||||
createdAt
|
||||
duration
|
||||
aspectRatio
|
||||
thumbnail(height: PORTRAIT_240) {
|
||||
id
|
||||
url
|
||||
__typename
|
||||
}
|
||||
creator {
|
||||
id
|
||||
xid
|
||||
name
|
||||
displayName
|
||||
accountType
|
||||
avatar(height: SQUARE_60) {
|
||||
id
|
||||
url
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
|
||||
fragment CHANNEL_BASE_FRAG on Channel {
|
||||
id
|
||||
xid
|
||||
name
|
||||
displayName
|
||||
accountType
|
||||
isFollowed
|
||||
avatar(height: SQUARE_120) {
|
||||
id
|
||||
url
|
||||
__typename
|
||||
}
|
||||
followerEngagement {
|
||||
id
|
||||
followDate
|
||||
__typename
|
||||
}
|
||||
metrics {
|
||||
id
|
||||
engagement {
|
||||
id
|
||||
followers {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
total
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
|
||||
fragment PLAYLIST_BASE_FRAG on Collection {
|
||||
id
|
||||
xid
|
||||
name
|
||||
description
|
||||
thumbnail(height: PORTRAIT_240) {
|
||||
id
|
||||
url
|
||||
__typename
|
||||
}
|
||||
creator {
|
||||
id
|
||||
xid
|
||||
name
|
||||
displayName
|
||||
accountType
|
||||
avatar(height: SQUARE_60) {
|
||||
id
|
||||
url
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
metrics {
|
||||
id
|
||||
engagement {
|
||||
id
|
||||
videos(filter: {visibility: {eq: PUBLIC}}) {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
total
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
|
||||
fragment HASHTAG_BASE_FRAG on Hashtag {
|
||||
id
|
||||
xid
|
||||
name
|
||||
metrics {
|
||||
id
|
||||
engagement {
|
||||
id
|
||||
videos {
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
total
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
|
||||
fragment LIVE_BASE_FRAGMENT on Live {
|
||||
id
|
||||
xid
|
||||
title
|
||||
audienceCount
|
||||
aspectRatio
|
||||
isOnAir
|
||||
thumbnail(height: PORTRAIT_240) {
|
||||
id
|
||||
url
|
||||
__typename
|
||||
}
|
||||
creator {
|
||||
id
|
||||
xid
|
||||
name
|
||||
displayName
|
||||
accountType
|
||||
avatar(height: SQUARE_60) {
|
||||
id
|
||||
url
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
|
||||
query SEARCH_QUERY(
|
||||
$query: String!,
|
||||
$shouldIncludeTopResults: Boolean!,
|
||||
$shouldIncludeVideos: Boolean!,
|
||||
$shouldIncludeChannels: Boolean!,
|
||||
$shouldIncludePlaylists: Boolean!,
|
||||
$shouldIncludeHashtags: Boolean!,
|
||||
$shouldIncludeLives: Boolean!,
|
||||
$page: Int,
|
||||
$limit: Int,
|
||||
$sortByVideos: SearchVideoSort,
|
||||
$durationMinVideos: Int,
|
||||
$durationMaxVideos: Int,
|
||||
$createdAfterVideos: DateTime,
|
||||
$recaptchaToken: String
|
||||
) {
|
||||
search(token: $recaptchaToken) {
|
||||
id
|
||||
|
||||
stories(query: $query, first: $limit, page: $page) @include(if: $shouldIncludeTopResults) {
|
||||
metadata {
|
||||
id
|
||||
algorithm {
|
||||
uuid
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
nextPage
|
||||
__typename
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
...VIDEO_BASE_FRAGMENT
|
||||
...CHANNEL_BASE_FRAG
|
||||
...PLAYLIST_BASE_FRAG
|
||||
...HASHTAG_BASE_FRAG
|
||||
...LIVE_BASE_FRAGMENT
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
|
||||
videos(
|
||||
query: $query,
|
||||
first: $limit,
|
||||
page: $page,
|
||||
sort: $sortByVideos,
|
||||
durationMin: $durationMinVideos,
|
||||
durationMax: $durationMaxVideos,
|
||||
createdAfter: $createdAfterVideos
|
||||
) @include(if: $shouldIncludeVideos) {
|
||||
metadata {
|
||||
id
|
||||
algorithm {
|
||||
uuid
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
nextPage
|
||||
__typename
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
...VIDEO_BASE_FRAGMENT
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
|
||||
lives(query: $query, first: $limit, page: $page) @include(if: $shouldIncludeLives) {
|
||||
metadata {
|
||||
id
|
||||
algorithm {
|
||||
uuid
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
nextPage
|
||||
__typename
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
...LIVE_BASE_FRAGMENT
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
|
||||
channels(query: $query, first: $limit, page: $page) @include(if: $shouldIncludeChannels) {
|
||||
metadata {
|
||||
id
|
||||
algorithm {
|
||||
uuid
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
nextPage
|
||||
__typename
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
...CHANNEL_BASE_FRAG
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
|
||||
playlists: collections(query: $query, first: $limit, page: $page) @include(if: $shouldIncludePlaylists) {
|
||||
metadata {
|
||||
id
|
||||
algorithm {
|
||||
uuid
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
nextPage
|
||||
__typename
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
...PLAYLIST_BASE_FRAG
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
|
||||
hashtags(query: $query, first: $limit, page: $page) @include(if: $shouldIncludeHashtags) {
|
||||
metadata {
|
||||
id
|
||||
algorithm {
|
||||
uuid
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
pageInfo {
|
||||
hasNextPage
|
||||
nextPage
|
||||
__typename
|
||||
}
|
||||
edges {
|
||||
node {
|
||||
id
|
||||
...HASHTAG_BASE_FRAG
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
|
||||
__typename
|
||||
}
|
||||
}
|
||||
"""
|
||||
}
|
||||
|
||||
payload = json.dumps(data).encode()
|
||||
|
||||
response = requests.post('https://graphql.api.dailymotion.com/', headers=headers, data=payload,
|
||||
proxies=dmheader_manager._proxies)
|
||||
|
||||
data = response.json()
|
||||
edges = data['data']['search']['stories']['edges']
|
||||
|
||||
for i, edge in enumerate(edges):
|
||||
print(i, edge['node']['xid'])
|
Loading…
x
Reference in New Issue
Block a user