本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理。
以下文章来源于数据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:\Users\HP\Downloads\6024766870349300_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(".*") # 定义正则表达式 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);" rel="external nofollow" ,因此解决这一问题的方法之一是运用硒模拟浏览器获取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:\Windows\Fonts\msyh.ttc', icon_name='fas fa-rocket',size=400, output_name='流金岁月-词云.png') Image(filename='流金岁月-词云.png')