bilix icon indicating copy to clipboard operation
bilix copied to clipboard

突发2024-11-28无法下载,报错 bilix.exception.APIParseError: APIParseError Caused by AttributeError in <bilix.sites.bilibili.api:get_video_info>

Open 9ihbd2DZSMjtsf7vecXjz opened this issue 1 year ago • 4 comments

使用命令 bilix v https://www.bilibili.com/video/BV1R6BRYmE99 --image -q 360p -fb firefox -d C:\Users\admin\Desktop\project\1

版本 Name: bilix Version: 0.18.9

报错内容如下, 求修复方法,感谢

Traceback (most recent call last): File "C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\site-packages\bilix\download\utils.py", line 90, in wrapped return await func(client, args, **kwargs) File "C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\site-packages\bilix\sites\bilibili\api.py", line 431, in get_video_info return await _get_video_info_from_html(client, url) File "C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\site-packages\bilix\sites\bilibili\api.py", line 445, in _get_video_info_from_html return _parse_bv_html(url, html) File "C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\site-packages\bilix\sites\bilibili\api.py", line 372, in _parse_bv_html play_info = re.search('?})

The above exception was the direct cause of the following exception:

Traceback (most recent call last): File "C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\runpy.py", line 196, in _run_module_as_main return _run_code(code, main_globals, None, File "C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\runpy.py", line 86, in run_code exec(code, run_globals) File "C:\Users\admin\AppData\Local\Programs\Python\Python310\Scripts\bilix.exe_main.py", line 7, in sys.exit(main()) File "C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\site-packages\click\core.py", line 1157, in call return self.main(*args, **kwargs) File "C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\site-packages\click\core.py", line 1078, in main rv = self.invoke(ctx) File "C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\site-packages\click\core.py", line 1434, in invoke return ctx.invoke(self.callback, **ctx.params) File "C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\site-packages\click\core.py", line 783, in invoke return __callback(*args, **kwargs) File "C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\site-packages\bilix\cli\main.py", line 374, in main loop.run_until_complete(cor) File "C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\asyncio\base_events.py", line 649, in run_until_complete return future.result() File "C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\site-packages\bilix\sites\bilibili\downloader.py", line 335, in get_video video_info = await api.get_video_info(self.client, url) File "C:\Users\admin\AppData\Local\Programs\Python\Python310\lib\site-packages\bilix\download\utils.py", line 94, in wrapped raise APIParseError(e, func) from e bilix.exception.APIParseError: APIParseError Caused by AttributeError in bilix.sites.bilibili.api:get_video_info

9ihbd2DZSMjtsf7vecXjz avatar Nov 28 '24 13:11 9ihbd2DZSMjtsf7vecXjz

因为我着急用,所以写了一个临时修复的方法,修改bilix/sites/bilibili/api.py中的两处代码 1. _parse_bv_html函数

async def _parse_bv_html(client,url, html: str) -> VideoInfo:
    init_info = re.search(r'<script>window.__INITIAL_STATE__=({.*?});\(', html).groups()[0]  # this line may raise
    init_info = json.loads(init_info)
    if len(init_info.get('error', {})) > 0:
        raise APIResourceError("视频已失效", url)  # 啊叻?视频不见了?在分区下载的时候可能产生
    # extract meta
    pages = []
    h1_title = legal_title(re.search('<h1[^>]*title="([^"]*)"', html).groups()[0])
    status = Status(**init_info['videoData']['stat'])
    bvid = init_info['bvid']
    desc = init_info['videoData'].get('desc', '')
    tags = [i['tag_name'] for i in init_info['tags']]
    aid = init_info['aid']
    (p, cid), = init_info['cidMap'][bvid]['cids'].items()
    p = int(p) - 1
    title = legal_title(init_info['videoData']['title'])
    base_url = url.split('?')[0]
    for idx, i in enumerate(init_info['videoData']['pages']):
        p_url = f"{base_url}?p={idx + 1}"
        p_name = f"P{idx + 1}-{i['part']}" if len(init_info['videoData']['pages']) > 1 else ''
        pages.append(Page(p_name=p_name, p_url=p_url))
    # extract dash and flv_url
    
    play_info_response = await req_retry(client, f'https://api.bilibili.com/x/player/wbi/playurl?bvid={bvid}&cid={cid}&fnval=4048')
    play_info = json.loads(play_info_response.text)['data']
    dash, other = None, []
    try:
        dash = Dash.from_dict(play_info)
    except KeyError:
        pass
    try:
        for i in play_info['durl']:
            suffix = re.search(r'\.([a-zA-Z0-9]+)\?', i['url']).group(1)
            other.append(Media(base_url=i['url'], backup_url=i['backup_url'], suffix=suffix))
    except KeyError:
        pass
    # extract img url
    img_url = re.search('property="og:image" content="([^"]*)"', html).groups()[0]
    if not img_url.startswith('http'):  # https://github.com/HFrost0/bilix/issues/52 just for some video
        img_url = 'http:' + img_url.split('@')[0]
    # construct data
    video_info = VideoInfo(title=title, aid=aid, cid=cid, status=status,
                           p=p, pages=pages, img_url=img_url, bvid=bvid, dash=dash, other=other,
                           desc=desc, tags=tags)
    return video_info
  1. _get_video_info_from_html函数
async def _get_video_info_from_html(client: httpx.AsyncClient, url: str) -> VideoInfo:
    res = await req_retry(client, url, follow_redirects=True)
    if str(res.url).startswith("https://www.bilibili.com/festival"):
        raise APIInvalidError("特殊节日页面", url)
    html = res.text
    if "window._riskdata_" in html:
        raise APIInvalidError("web 前端访问被风控", url)
    if "window.__INITIAL_STATE__" in html:
        return await _parse_bv_html(client,url, html)
    elif "__NEXT_DATA__" in html:
        video_info = _parse_ep_html(url, html)
        await _attach_ep_dash(client, video_info)
        return video_info
    else:
        raise APIUnsupportedError("未知页面类型", url)

lvdongyi avatar Nov 28 '24 15:11 lvdongyi

因为我着急用,所以写了一个临时修复的方法,修改bilix/sites/bilibili/api.py中的两处代码 1. _parse_bv_html函数

async def _parse_bv_html(client,url, html: str) -> VideoInfo:
    init_info = re.search(r'<script>window.__INITIAL_STATE__=({.*?});\(', html).groups()[0]  # this line may raise
    init_info = json.loads(init_info)
    if len(init_info.get('error', {})) > 0:
        raise APIResourceError("视频已失效", url)  # 啊叻?视频不见了?在分区下载的时候可能产生
    # extract meta
    pages = []
    h1_title = legal_title(re.search('<h1[^>]*title="([^"]*)"', html).groups()[0])
    status = Status(**init_info['videoData']['stat'])
    bvid = init_info['bvid']
    desc = init_info['videoData'].get('desc', '')
    tags = [i['tag_name'] for i in init_info['tags']]
    aid = init_info['aid']
    (p, cid), = init_info['cidMap'][bvid]['cids'].items()
    p = int(p) - 1
    title = legal_title(init_info['videoData']['title'])
    base_url = url.split('?')[0]
    for idx, i in enumerate(init_info['videoData']['pages']):
        p_url = f"{base_url}?p={idx + 1}"
        p_name = f"P{idx + 1}-{i['part']}" if len(init_info['videoData']['pages']) > 1 else ''
        pages.append(Page(p_name=p_name, p_url=p_url))
    # extract dash and flv_url
    
    play_info_response = await req_retry(client, f'https://api.bilibili.com/x/player/wbi/playurl?bvid={bvid}&cid={cid}')
    play_info = json.loads(play_info_response.text)['data']
    dash, other = None, []
    try:
        dash = Dash.from_dict(play_info)
    except KeyError:
        pass
    try:
        for i in play_info['durl']:
            suffix = re.search(r'\.([a-zA-Z0-9]+)\?', i['url']).group(1)
            other.append(Media(base_url=i['url'], backup_url=i['backup_url'], suffix=suffix))
    except KeyError:
        pass
    # extract img url
    img_url = re.search('property="og:image" content="([^"]*)"', html).groups()[0]
    if not img_url.startswith('http'):  # https://github.com/HFrost0/bilix/issues/52 just for some video
        img_url = 'http:' + img_url.split('@')[0]
    # construct data
    video_info = VideoInfo(title=title, aid=aid, cid=cid, status=status,
                           p=p, pages=pages, img_url=img_url, bvid=bvid, dash=dash, other=other,
                           desc=desc, tags=tags)
    return video_info
  1. _get_video_info_from_html函数
async def _get_video_info_from_html(client: httpx.AsyncClient, url: str) -> VideoInfo:
    res = await req_retry(client, url, follow_redirects=True)
    if str(res.url).startswith("https://www.bilibili.com/festival"):
        raise APIInvalidError("特殊节日页面", url)
    html = res.text
    if "window._riskdata_" in html:
        raise APIInvalidError("web 前端访问被风控", url)
    if "window.__INITIAL_STATE__" in html:
        return await _parse_bv_html(client,url, html)
    elif "__NEXT_DATA__" in html:
        video_info = _parse_ep_html(url, html)
        await _attach_ep_dash(client, video_info)
        return video_info
    else:
        raise APIUnsupportedError("未知页面类型", url)

太牛了,感谢, 救世主一样的瞬间出现.

9ihbd2DZSMjtsf7vecXjz avatar Nov 28 '24 15:11 9ihbd2DZSMjtsf7vecXjz

因为我着急用,所以写了一个临时修复的方法,修改bilix/sites/bilibili/api.py中的两处代码 1. _parse_bv_html函数

async def _parse_bv_html(client,url, html: str) -> VideoInfo:
    init_info = re.search(r'<script>window.__INITIAL_STATE__=({.*?});\(', html).groups()[0]  # this line may raise
    init_info = json.loads(init_info)
    if len(init_info.get('error', {})) > 0:
        raise APIResourceError("视频已失效", url)  # 啊叻?视频不见了?在分区下载的时候可能产生
    # extract meta
    pages = []
    h1_title = legal_title(re.search('<h1[^>]*title="([^"]*)"', html).groups()[0])
    status = Status(**init_info['videoData']['stat'])
    bvid = init_info['bvid']
    desc = init_info['videoData'].get('desc', '')
    tags = [i['tag_name'] for i in init_info['tags']]
    aid = init_info['aid']
    (p, cid), = init_info['cidMap'][bvid]['cids'].items()
    p = int(p) - 1
    title = legal_title(init_info['videoData']['title'])
    base_url = url.split('?')[0]
    for idx, i in enumerate(init_info['videoData']['pages']):
        p_url = f"{base_url}?p={idx + 1}"
        p_name = f"P{idx + 1}-{i['part']}" if len(init_info['videoData']['pages']) > 1 else ''
        pages.append(Page(p_name=p_name, p_url=p_url))
    # extract dash and flv_url
    
    play_info_response = await req_retry(client, f'https://api.bilibili.com/x/player/wbi/playurl?bvid={bvid}&cid={cid}')
    play_info = json.loads(play_info_response.text)['data']
    dash, other = None, []
    try:
        dash = Dash.from_dict(play_info)
    except KeyError:
        pass
    try:
        for i in play_info['durl']:
            suffix = re.search(r'\.([a-zA-Z0-9]+)\?', i['url']).group(1)
            other.append(Media(base_url=i['url'], backup_url=i['backup_url'], suffix=suffix))
    except KeyError:
        pass
    # extract img url
    img_url = re.search('property="og:image" content="([^"]*)"', html).groups()[0]
    if not img_url.startswith('http'):  # https://github.com/HFrost0/bilix/issues/52 just for some video
        img_url = 'http:' + img_url.split('@')[0]
    # construct data
    video_info = VideoInfo(title=title, aid=aid, cid=cid, status=status,
                           p=p, pages=pages, img_url=img_url, bvid=bvid, dash=dash, other=other,
                           desc=desc, tags=tags)
    return video_info
  1. _get_video_info_from_html函数
async def _get_video_info_from_html(client: httpx.AsyncClient, url: str) -> VideoInfo:
    res = await req_retry(client, url, follow_redirects=True)
    if str(res.url).startswith("https://www.bilibili.com/festival"):
        raise APIInvalidError("特殊节日页面", url)
    html = res.text
    if "window._riskdata_" in html:
        raise APIInvalidError("web 前端访问被风控", url)
    if "window.__INITIAL_STATE__" in html:
        return await _parse_bv_html(client,url, html)
    elif "__NEXT_DATA__" in html:
        video_info = _parse_ep_html(url, html)
        await _attach_ep_dash(client, video_info)
        return video_info
    else:
        raise APIUnsupportedError("未知页面类型", url)

太牛了,感谢, 救世主一样的瞬间出现.

我刚刚发现需要再加一个fnval=4048参数才能下载音频,所以又更新了下代码,希望能帮到你~

lvdongyi avatar Nov 28 '24 15:11 lvdongyi

因为我着急用,所以写了一个临时修复的方法,修改bilix/sites/bilibili/api.py中的两处代码 1. _parse_bv_html函数

async def _parse_bv_html(client,url, html: str) -> VideoInfo:
    init_info = re.search(r'<script>window.__INITIAL_STATE__=({.*?});\(', html).groups()[0]  # this line may raise
    init_info = json.loads(init_info)
    if len(init_info.get('error', {})) > 0:
        raise APIResourceError("视频已失效", url)  # 啊叻?视频不见了?在分区下载的时候可能产生
    # extract meta
    pages = []
    h1_title = legal_title(re.search('<h1[^>]*title="([^"]*)"', html).groups()[0])
    status = Status(**init_info['videoData']['stat'])
    bvid = init_info['bvid']
    desc = init_info['videoData'].get('desc', '')
    tags = [i['tag_name'] for i in init_info['tags']]
    aid = init_info['aid']
    (p, cid), = init_info['cidMap'][bvid]['cids'].items()
    p = int(p) - 1
    title = legal_title(init_info['videoData']['title'])
    base_url = url.split('?')[0]
    for idx, i in enumerate(init_info['videoData']['pages']):
        p_url = f"{base_url}?p={idx + 1}"
        p_name = f"P{idx + 1}-{i['part']}" if len(init_info['videoData']['pages']) > 1 else ''
        pages.append(Page(p_name=p_name, p_url=p_url))
    # extract dash and flv_url
    
    play_info_response = await req_retry(client, f'https://api.bilibili.com/x/player/wbi/playurl?bvid={bvid}&cid={cid}')
    play_info = json.loads(play_info_response.text)['data']
    dash, other = None, []
    try:
        dash = Dash.from_dict(play_info)
    except KeyError:
        pass
    try:
        for i in play_info['durl']:
            suffix = re.search(r'\.([a-zA-Z0-9]+)\?', i['url']).group(1)
            other.append(Media(base_url=i['url'], backup_url=i['backup_url'], suffix=suffix))
    except KeyError:
        pass
    # extract img url
    img_url = re.search('property="og:image" content="([^"]*)"', html).groups()[0]
    if not img_url.startswith('http'):  # https://github.com/HFrost0/bilix/issues/52 just for some video
        img_url = 'http:' + img_url.split('@')[0]
    # construct data
    video_info = VideoInfo(title=title, aid=aid, cid=cid, status=status,
                           p=p, pages=pages, img_url=img_url, bvid=bvid, dash=dash, other=other,
                           desc=desc, tags=tags)
    return video_info
  1. _get_video_info_from_html函数
async def _get_video_info_from_html(client: httpx.AsyncClient, url: str) -> VideoInfo:
    res = await req_retry(client, url, follow_redirects=True)
    if str(res.url).startswith("https://www.bilibili.com/festival"):
        raise APIInvalidError("特殊节日页面", url)
    html = res.text
    if "window._riskdata_" in html:
        raise APIInvalidError("web 前端访问被风控", url)
    if "window.__INITIAL_STATE__" in html:
        return await _parse_bv_html(client,url, html)
    elif "__NEXT_DATA__" in html:
        video_info = _parse_ep_html(url, html)
        await _attach_ep_dash(client, video_info)
        return video_info
    else:
        raise APIUnsupportedError("未知页面类型", url)

太牛了,感谢, 救世主一样的瞬间出现.

我刚刚发现需要再加一个fnval=4048参数才能下载音频,所以又更新了下代码,希望能帮到你~

给了我巨大的帮助,感谢老板,好人一生平安, 工作流回复正常了

9ihbd2DZSMjtsf7vecXjz avatar Nov 28 '24 16:11 9ihbd2DZSMjtsf7vecXjz