import requests
import json
import os
import re
import time

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 QQBrowser/21.1.8717.400',
    'Accept': '*/*',
    'Accept-Language': 'zh-CN,zh;q=0.9',
    'Referer': 'https://haokan.baidu.com/tab/shehui_new',
    'Cookie': 'newlogin=1; BDUSS=EJGZWFBdmdtbEQtRGl6dDg0TzYyR0JybnpxRUdyZHpqeGxxUW91SUZ6RERMMFZxSVFBQUFBJCQAAAAAAAAAAAEAAABr5UEVUVFfMjgyNjE1MTA5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMOiHWrDoh1qc; BDUSS_BFESS=EJGZWFBdmdtbEQtRGl6dDg0TzYyR0JybnpxRUdyZHpqeGxxUW91SUZ6RERMMFZxSVFBQUFBJCQAAAAAAAAAAAEAAABr5UEVUVFfMjgyNjE1MTA5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMOiHWrDoh1qc; BAIDUID=12B658EA09C12D0BD5154700ABB7E735:FG=1; BIDUPSID=12B658EA09C12D0BD5154700ABB7E735; PSTM=1780626218; BDORZ=FFFB88E999055A3F8A630C64834BD6D0; BAIDUID_BFESS=12B658EA09C12D0BD5154700ABB7E735:FG=1; __bid_n=19eb62214d03728183c387; BA_HECTOR=052lakal25a18581a5ag840la58kak1l2v7le28; ZFY=z4dlRWO:Bw01rXjTUYWYrECWhYzyaYFQVwF7:Bq:BLHaw4:C; H_WISE_SIDS=63147_67862_68166_69006_69295_69590_70116_70620_70544_70552_70504_70908_70916_70940_70946_70966_71000_71036_71049_71056_71062_71079_71086_71105; arialoadData=false; H_PS_PSSID=63147_67862_68166_69006_69295_69590_70116_70620_70692_70547_70544_70552_70504_70908_70916_70940_70946_70966_70975_71000_71019_71036_71049_71056_71062_71079_71086_71105; Hm_lvt_4aadd610dfd2f5972f1efee2653a2bc5=1781515173,1781569200,1781584058; HMACCOUNT=F22D314F8316F7B4; Hm_lpvt_4aadd610dfd2f5972f1efee2653a2bc5=1781585057; ab_sr=1.0.1_Y2U1OTE3MDI0YTUwNWY0YjdiNmI0Y2RhYmU4Y2MyOGViNWI4YjljMmYyYjAzZjlkYzc0YjAwNjc1M2Q1NWY2YjgxMGYxYWQ5MGY2NDVlZmZhOWVmZmM3NWI5YThhNDgxYTk0NGJjMjk5MTJjNDc4NmVmYzZhN2FiYjMxM2IwMTQ4NzZkZmUxMTU3NzZiMzQ3Y2Q0Yjc2OTBmZjU1NGQzMg==; reptileData=%7B%22data%22%3A%22149ab658c534ce73cd3179424b269745ac8bf0fad4b939c7f732f0e8fa8d9f071bb53711903bf0c4aa4d177e07366edc2f066cab1154887aae07117d2e9ebbcd0b520a17d508f41a4e12097b61a0f7d198c3991b9565e7a1ce5475f99e6e33f4%22%2C%22key_id%22%3A%2230%22%2C%22sign%22%3A%2227f03425%22%7D; RT="z=1&dm=baidu.com&si=ecd26d73-65c9-4559-96c4-0780df5f2c16&ss=mqg54xs3&sl=2&tt=1400&bcn=https%3A%2F%2Ffclog.baidu.com%2Flog%2Fweirwood%3Ftype%3Dperf&ld=lyzi'  # 保持你的Cookie
}

# 2. 使用Session保持连接池
session = requests.Session()
session.headers.update(headers)


def get_videos(page=1, num_per_page=20):
    """
    获取指定页的视频数据

    Args:
        page: 页码,从1开始
        num_per_page: 每页视频数量

    Returns:
        list: 视频列表,如果失败返回空列表
    """
    # 好看视频的分页参数:pn表示页码,num表示每页数量
    url = f'https://haokan.baidu.com/haokan/ui-web/video/rec?tab=shehui_new&act=pcFeed&pd=pc&num={num_per_page}&pn={page}'

    try:
        resp = session.get(url, timeout=5)
        data = resp.json()

        # 检查返回状态
        if data.get('status') != 0:
            print(f"第{page}页数据获取失败,状态码: {data.get('status')}")
            return []

        videos = data.get('data', {}).get('response', {}).get('videos', [])
        print(f"第{page}页获取到 {len(videos)} 个视频")
        return videos

    except Exception as e:
        print(f"第{page}页请求失败: {e}")
        return []


def download_video(video, index, total):
    """
    下载单个视频

    Args:
        video: 视频数据字典
        index: 当前视频序号
        total: 总视频数
    """
    URL = video.get('play_url', '')
    img = video.get('poster_big', '')  # 预览图地址
    preview_img = re.sub(r'\u0026', '&', img)
    bt = video.get('title', f'未命名视频_{index}')  # 标题

    print(f"\n进度: [{index + 1}/{total}]")
    print(f"视频标题: {bt}")
    print(f"播放链接: {URL}")
    print(f"预览图链接: {preview_img}")

    # 如果没有播放链接,跳过
    if not URL:
        print("❌ 无播放链接,跳过")
        return

    # 保存视频(文件名用标题,去掉非法字符)
    title = re.sub(r'[\\/*?:"<>|]', '', bt)
    if not title:
        title = f"video_{index}"

    # 创建文件夹
    if not os.path.exists('视频'):
        os.makedirs('视频')

    try:
        # 使用stream=True进行流式下载,避免大视频占用过多内存
        video_resp = session.get(URL, stream=True, timeout=30)

        # 检查响应状态码
        if video_resp.status_code == 200:
            file_path = f'视频/{title}.mp4'

            # 检查文件是否已存在
            if os.path.exists(file_path):
                print(f"⚠️  文件已存在: {title}.mp4,跳过下载")
                return

            # 分块写入文件,避免大视频内存溢出
            with open(file_path, mode='wb') as f:
                for chunk in video_resp.iter_content(chunk_size=8192):
                    if chunk:
                        f.write(chunk)

            # 获取文件大小
            file_size = os.path.getsize(file_path)
            print(f"✅ {title}.mp4 下载完成! 文件大小: {file_size / 1024 / 1024:.2f} MB")
        else:
            print(f"❌ 下载失败: {title}, 状态码: {video_resp.status_code}")

    except requests.exceptions.Timeout:
        print(f"❌ 下载超时: {title}")
    except requests.exceptions.RequestException as e:
        print(f"❌ 下载出错: {title}, 错误: {e}")
    except Exception as e:
        print(f"❌ 文件保存失败: {title}, 错误: {e}")


def main():
    """
    主函数:控制爬取和下载流程
    """
    print("=" * 50)
    print("好看视频批量下载工具")
    print("=" * 50)

    # 用户配置
    try:
        total_pages = int(input("请输入要下载的页数(1-10页,建议不超过5页): "))
        total_pages = max(1, min(10, total_pages))  # 限制在1-10页
    except ValueError:
        total_pages = 2
        print(f"输入无效,默认下载 {total_pages} 页")

    num_per_page = 20  # 每页视频数量

    all_videos = []  # 存储所有视频

    # 逐页获取视频列表
    print(f"\n开始获取视频列表,共 {total_pages} 页...")
    for page in range(1, total_pages + 1):
        videos = get_videos(page, num_per_page)

        if not videos:
            print(f"第{page}页没有获取到视频,停止翻页")
            break

        all_videos.extend(videos)

        # 添加延迟,避免请求过快
        if page < total_pages:
            print(f"等待1秒后获取第{page + 1}页...")
            time.sleep(1)

    # 统计结果
    total_videos = len(all_videos)
    print(f"\n总计获取到 {total_videos} 个视频")

    if total_videos == 0:
        print("没有获取到任何视频,程序结束")
        return

    # 询问是否下载
    choice = input(f"\n是否开始下载这 {total_videos} 个视频?(y/n,默认y): ").strip().lower()
    if choice and choice != 'y':
        print("已取消下载")
        return

    # 开始下载所有视频
    print(f"\n开始下载 {total_videos} 个视频...")
    success_count = 0
    fail_count = 0

    for i, video in enumerate(all_videos):
        # 下载每个视频前检查是否存在(在下载函数内部已检查)
        download_video(video, i, total_videos)

        # 每个视频下载后短暂延迟,避免被封IP
        if i < total_videos - 1:
            time.sleep(0.5)

    # 统计下载结果
    print("\n" + "=" * 50)
    print("全部下载完成!")
    print("=" * 50)


if __name__ == "__main__":
    main()

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐