• 手把手教你如何用Python获取爱奇艺电视剧弹幕数据


    本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理。

    以下文章来源于数据STUDIO ,作者龙哥带你飞

    Python分析抖音用户行为数据视频讲解地址

    https://www.bilibili.com/video/BV1yp4y1q7ZC/

    数据获取是数据分析中的重要的一步,数据获取的途径多种多样,在这个信息爆炸的时代,数据获取的代价也是越来越小。因此如此,仍然有很多小伙伴们无法如何获取有用信息。此处以最近的热播排行榜第一名的《流金岁月》为例,手把手教你如何获取爱奇艺电视剧弹幕数据。

     

    寻找弹幕信息

    爱奇艺的弹幕数据已通过.z形式的压缩文件存在,先通过以下步骤找到弹幕url, tvid列表,再获取压缩文件。利用工具对获取的压缩文件进行解压,处理,存储及分析。

     

    绝对,实行多页爬取,需要分析url规律,利用url规律循环请求并获取所需内容。

    此弹幕文件url地址为
    https://cmts.iqiyi.com/bullet/93/00/6024766870349300_300_1.z
    其中tvid = 6024766870349300

    url普适形式为
    url ='https:
    //cmts.iqiyi.com/bullet/{ }/{}/{ }_300_{}.z '其中第一个与第二个花括号内容是tvid后3、4位,,后1、2位。第三个花括号为tvid。第四个花括号为子文件序号,其不是一个无穷大的数,会根据不同的电视剧有不同的最大数。

     

    获取弹幕文件

    可以利用浏览器通过url直接请求,并获取结果。

     

    输入网址可获取弹幕内容的压缩文件文件。

     

    利用解压/压缩包zlib对下载下来的压缩文件进行解压查看。

    import zlib
    from bs4 import BeautifulSoup
    with open(r"C:UsersHPDownloads6024766870349300_300_10.z", 'rb') as fin:
        content = fin.read()
    btArr = bytearray(content)
    xml=zlib.decompress(btArr).decode('utf-8')
    bs = BeautifulSoup(xml,"xml")
    bs

    输出

     

    因此tvid只要获得就能轻松获取该电视剧的弹幕文件数据。

    import zlib
    from bs4 import BeautifulSoup
    import pandas as pd
    import requests
    def get_data(tv_name,tv_id):
        """
        获取每集的tvid
        :param tv_name: 集数,第1集、第2集...
        :param tv_id: 每集的tvid
        :return: DataFrame, 最终的数据
        """
        base_url = 'https://cmts.iqiyi.com/bullet/{}/{}/{}_300_{}.z'
        # 新建一个只有表头的DataFrame
        head_data = pd.DataFrame(columns=['uid','contentsId','contents','likeCount'])
        for i in range(1,20):
            url = base_url.format(tv_id[-4:-2],tv_id[-2:],tv_id,i)
            print(url)
            res = requests.get(url)
            if res.status_code == 200:
                btArr = bytearray(res.content) 
                xml=zlib.decompress(btArr).decode('utf-8') # 解压压缩文件
                bs = BeautifulSoup(xml,"xml") # BeautifulSoup网页解析
                data = pd.DataFrame(columns=['uid','contentsId','contents','likeCount'])
                data['uid'] = [i.text for i in bs.findAll('uid')]
                data['contentsId'] = [i.text for i in bs.findAll('contentId')]
                data['contents'] = [i.text for i in bs.findAll('content')]
                data['likeCount'] = [i.text for i in bs.findAll('likeCount')]
            else:
                break
            head_data = pd.concat([head_data,data],ignore_index = True)
        head_data['tv_name']= tv_name
        return head_data

    获取tvid

    上文已通过tvid获取到了弹幕文件数据,那么如何获取tvid又变成了一个问题。莫急,我们继续分析。直接Ctrl + F搜索tvid

     

    因此可以直接从返回结果中通过正则表达式获取tvid。

    from requests_html import HTMLSession, UserAgent
    from bs4 import BeautifulSoup
    import re
    def get_tvid(url):
        """
        获取每集的tvid
        :param url: 请求网址
        :return: str, 每集的tvid
        """
        session = HTMLSession()   #创建HTML会话对象
        user_agent = UserAgent().random  #创建随机请求头
        header = {"User-Agent": user_agent}
        res = session.get(url, headers=header)
        res.encoding='utf-8'
        bs = BeautifulSoup(res.text,"html.parser")
        pattern =re.compile(".*?tvid.*?(d{16}).*?") # 定义正则表达式
        text_list = bs.find_all(text=pattern) # 通过正则表达式获取内容
        for t in range(len(text_list)):
            res_list = pattern.findall(text_list[t])
            if not res_list:
                pass
            else:
                tvid = res_list[0]
        return tvid

    由此问题tvid。来每一集都有一个tvid,有多少集电视剧就可以获取多少个tvid。那么问题又来了:获取tvid时,是通过url发送请求,从返回结果中获取。而每一集的url又该如何获取呢。

    获取每集url

    通过元素选择工具定位到集数选择信息。通过硒模拟浏览器获取动态加载信息。

     

    有小伙伴会说,可以直接直接从返回内容中获取此href网址啊,你可以自己动手尝试下。

    云朵君尝试后得到的结果是href="javascript:void(0);",因此解决这一问题的方法之一是运用硒模拟浏览器获取js动态加载信息。

    def get_javascript0_links(url, class_name, class_name_father, sleep_time=0.02):
        """
        Selenium模拟用户点击爬取url
        :param url: 目标页面
        :param class_name: 模拟点击的类
        :param class_name_father: 模拟点击的类,此类为class_name的父类
        :param sleep_time: 留给页面后退的时间
        :return: list, 点击class为class_name进去的超链接
        """
    
        def wait(locator, timeout=15):
            """等到元素加载完成"""
            WebDriverWait(driver, timeout).until(EC.presence_of_element_located(locator))
    
        options = Options()
    #     options.add_argument("--headless")  # 无界面,若你需要查看界面内容,可以将此行注释掉
        driver = webdriver.Chrome(options=options)
        driver.get(url)
    
        locator = (By.CLASS_NAME, class_name)
        wait(locator)
        element = driver.find_elements_by_class_name(class_name_father)
        elements = driver.find_elements_by_class_name(class_name)
        link = []
        linkNum = len(elements)
        for j in range(len(element)):
            wait(locator)
            driver.execute_script("arguments[0].click();", element[j]) # 模拟用户点击
            for i in range(linkNum):
                print(i)
                wait(locator)
                elements = driver.find_elements_by_class_name(class_name) # 再次获取元素,预防StaleElementReferenceException
                driver.execute_script("arguments[0].click();", elements[i]) # 模拟用户点击
                time.sleep(sleep_time)
                link.append(driver.current_url)
                time.sleep(sleep_time)
                driver.back()
        driver.quit()
        return link
    
    if __name__ == "__main__":
        url = "https://www.iqiyi.com/v_1meaw5kgh3s.html"
        class_name = "qy-episode-num"
        link = get_javascript0_links(url, class_name, class_name_father="tab-bar")
        for i, _link in enumerate(link):
            print(i, _link)

    主函数

    接下来通过主函数将所有步骤串起。

    def main(sleep_second=0.02):
        url = "https://www.iqiyi.com/v_1meaw5kgh3s.html"
        class_name = "select-item"
        class_name_father = "bar-li"
        links = get_javascript0_links(url, class_name, class_name_father)
        head_data = pd.DataFrame(columns=['tv_name','uid','contentsId','contents','likeCount'])
        for num, link in enumerate(links):
            tv_name = f"第{num+1}集"
            tv_id = get_tvid(url=link)
            data = get_data(tv_name,tv_id)
            head_data = pd.concat([head_data,data],ignore_index = True)
            time.sleep(sleep_second)
        return head_data

    获取到的数据结果如下:

    >>> data = main()
    >>> data.info()
    """
    <class 'pandas.core.frame.DataFrame'>
    RangeIndex: 246716 entries, 0 to 246715
    Data columns (total 5 columns):
     #   Column      Non-Null Count   Dtype 
    ---  ------      --------------   ----- 
     0   tv_name     246716 non-null  object
     1   uid         246716 non-null  object
     2   contentsId  246716 non-null  object
     3   contents    246716 non-null  object
     4   likeCount   246716 non-null  object
    dtypes: object(5)
    memory usage: 9.4+ MB
    """
    >>> data.sample(10)

     

    词云图

    • 先分词

    运用中文分词库jieba分词,并去除撤销词。

    def get_cut_words(content_series):
        """
        :param content_series: 需要分词的内容
        :return: list, 点击class为class_name进去的超链接
        """
        # 读入停用词表
        import jieba 
        stop_words = [] 
        with open("stop_words.txt", 'r', encoding='utf-8') as f:
            lines = f.readlines()
            for line in lines:
                stop_words.append(line.strip())
        # 添加关键词
        my_words = ['倪妮', '刘诗诗', '锁锁', '蒋三岁', '陈道明']      
        for i in my_words:
            jieba.add_word(i) 
        # 自定义停用词
        my_stop_words = ['哈哈哈','哈哈哈哈', '真的']    
        stop_words.extend(my_stop_words)               
        # 分词
        word_num = jieba.lcut(content_series.str.cat(sep=''), cut_all=False)
        word_num_selected = [i for i in word_num if i not in stop_words and len(i)>=2] # 条件筛选
        
        return word_num_selected
    • 后画图

    运用升级版词云图库stylecloud可视化弹幕结果。

    import stylecloud
    from IPython.display import Image 
    text1 = get_cut_words(content_series=data.contents)
    stylecloud.gen_stylecloud(text=' '.join(text1), collocations=False,
                              font_path=r'‪C:WindowsFontsmsyh.ttc',
                              icon_name='fas fa-rocket',size=400,
                              output_name='流金岁月-词云.png')
    Image(filename='流金岁月-词云.png')
  • 相关阅读:
    ckeditor 上传图片
    过滤html else <p>
    目录遍历
    android小知识之SparseArray(HaspMap替换)
    android小知识之邮箱地址输入自动完成
    android小知识之EditText输入框之值监控以及类型限制(数字,英语字母,下划线,是否为星号密码)
    android小知识之圆角ListView
    android小知识之多个listview在同一界面只有item高亮
    如何取消一个本地svn目录与svn的联系(即恢复原有图标等)
    android小知识之意图(intent)
  • 原文地址:https://www.cnblogs.com/hhh188764/p/14264268.html
Copyright © 2020-2023  润新知