python - 爬取西瓜视频 一年一影帝 百年周星驰

612 阅读2分钟

最近在学习python的爬虫,正好有一个需求,要下载一部电影,所以就打算用python爬虫来实现。

本来想起来挺简单的,但是做起来真的挺难的。主要是在西瓜视频代码里找到视频的链接,大概花了半天的时间才慢慢找到方法。

西瓜视频的网页是动态加载的,我们直接使用request请求是没有返回任何有用的信息,所以我们需要使用其他方法。

这里我使用的是pyppeteer这个库,可以模仿浏览器的访问,等待指定资源标签加载完成后,就可以看到我们想要的内容了。

这里我们在西瓜视频随便找一个视频,然后打开Chrome的开发者模式,寻找视频链接。 在这里插入图片描述 红框部分就包含我们需要的视频链接,我们把这段代码复制到文本编辑器,查看可以看到有一个main_url标签,后边是一连串的字符串,是base64加密的,这个就是我们需要获取的,然后在解码就可以看到视频链接了,有了链接就可以下载了。

完整代码如下:

import asyncio
import logging
import sys

import requests
from base64 import b64decode
from pyppeteer import launch
from pyquery import PyQuery as pq

url_list = []
logging.basicConfig(level=logging.INFO)


async def main():
    # 启动浏览器
    browser = await  launch()
    # 开启一个新的标签页
    page = await  browser.newPage()
    # 访问url地址
    await page.goto("https://www.ixigua.com/7194376404279624203?logTag=894da6bbbe2fd383bdeb")
    # 等待指定节点加载完成
    await page.waitForSelector('#SSR_HYDRATED_DATA')
    # 初始化pyquery
    doc = pq(await page.content())
    scripts_data = doc('[id*=SSR_HYDRATED_DATA]').text()
    # 获取视频title,作为保存文件名
    title = doc('title').text()

    # 格式化字符串
    str = scripts_data.split("=", 1)[1]
    str = str.replace("null", "\"null\"").replace("false", "\'false\'"). \
        replace("true", "\'true\'").replace("undefined", "\'undefined\'")

    # 字符串转换为字典
    b = eval(str)

    # 迭代获取字典的key,返回一个value的列表
    main_urls = get_item(b, "main_url")

    # 初始化列表,存储解码后的main_url数据
    video_url = ""
    for main_url in main_urls:
        url = base64_code(main_url)
        # 判断url可用性,如果可用则加入列表
        if check_url(url):
            video_url = url


    filename = title + ".mp4"
    save_data(filename, video_url)

    await browser.close()

# 获取字典内指定key的值
def get_item(it, k):
    # 判断对象是否为字典
    if isinstance(it, dict):
        for d in it:
            if isinstance(it[d], (dict, list)):
                get_item(it[d], k)
            else:
                if d == k:
                    # print(it[d])
                    url_list.append(it[d])
    # 判断对象如果是list
    elif isinstance(it, list):
        for l in it:
            if isinstance(l, (dict, list)):
                get_item(l, k)
    # 列表去重并返回
    urls = list(set(url_list))
    return urls


# 保存视频
def save_data(filename, url):
    logging.info("正在保存数据...")
    data = requests.get(url).content
    with open(filename, "wb") as f:
        f.write(data)
    logging.info("保存完成")


# bs64编码
def base64_code(str):
    logging.info("正在进行解码。。。")
    str = b64decode(str).decode("utf8")
    return str

# 检查url可用性
def check_url(url):
    logging.info("正在检查%s的可用性" % url)
    response = requests.get(url, timeout=20)
    if response.status_code == 200:
        return True
    else:
        return False


asyncio.get_event_loop().run_until_complete(main())