|
|
|
@ -0,0 +1,286 @@
|
|
|
|
|
import requests
|
|
|
|
|
import re
|
|
|
|
|
import pandas as pd
|
|
|
|
|
from collections import Counter
|
|
|
|
|
import jieba
|
|
|
|
|
import wordcloud
|
|
|
|
|
|
|
|
|
|
def extract_ids_from_url(url, head, output_file='aid.txt'):
|
|
|
|
|
"""
|
|
|
|
|
从给定的 URL 中提取 IDs 并将其保存到指定的文件中。
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
url (str): 要请求的 URL。
|
|
|
|
|
head (dict): 请求头,用于发起 HTTP 请求。
|
|
|
|
|
output_file (str): 存储提取的 ID 的文件路径,默认为 'aid.txt'。
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 发起 GET 请求
|
|
|
|
|
response = requests.get(url=url, headers=head)
|
|
|
|
|
# 确保请求成功,状态码在 200 到 299 之间
|
|
|
|
|
response.raise_for_status()
|
|
|
|
|
|
|
|
|
|
# 将响应内容解析为 JSON 格式
|
|
|
|
|
data = response.json()
|
|
|
|
|
|
|
|
|
|
# 检查响应数据是否包含 'data' 和 'result' 键
|
|
|
|
|
if 'data' in data and 'result' in data['data']:
|
|
|
|
|
items = data['data']['result']
|
|
|
|
|
# 提取每个条目的 'id' 字段
|
|
|
|
|
ids = [item['id'] for item in items]
|
|
|
|
|
|
|
|
|
|
# 以追加模式打开文件,并写入每个 ID
|
|
|
|
|
with open(output_file, 'a') as file:
|
|
|
|
|
for aid in ids:
|
|
|
|
|
file.write(f"{aid}\n")
|
|
|
|
|
|
|
|
|
|
print(f"IDs have been saved to {output_file}")
|
|
|
|
|
else:
|
|
|
|
|
print("Unexpected response format") # 如果响应格式不符合预期,输出提示信息
|
|
|
|
|
|
|
|
|
|
except requests.RequestException as e:
|
|
|
|
|
# 捕获并打印请求相关的错误
|
|
|
|
|
print(f"Request error: {e}")
|
|
|
|
|
except KeyError as e:
|
|
|
|
|
# 捕获并打印键错误
|
|
|
|
|
print(f"Key error: {e}")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
# 捕获并打印其他类型的异常
|
|
|
|
|
print(f"An error occurred: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def process_urls(urls1, headers1, output_file='aid.txt'):
|
|
|
|
|
"""
|
|
|
|
|
遍历 URL 列表,并对每个 URL 调用 extract_ids_from_url 函数进行处理。
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
urls1 (list): 包含 URL 的列表。
|
|
|
|
|
headers1 (dict): 请求头,用于发起 HTTP 请求。
|
|
|
|
|
output_file (str): 存储提取的 ID 的文件路径,默认为 'aid.txt'。
|
|
|
|
|
"""
|
|
|
|
|
for url in urls1:
|
|
|
|
|
extract_ids_from_url(url, headers1, output_file)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def process_aid_and_cid(aid_file_path, cid_file_path, headers):
|
|
|
|
|
# 打开 aid 文件,并读取其中的所有 aid
|
|
|
|
|
with open(aid_file_path, 'r') as file:
|
|
|
|
|
aids = [line.strip() for line in file if line.strip()]
|
|
|
|
|
|
|
|
|
|
count = 0
|
|
|
|
|
# 打开 cid 文件(以追加模式),准备写入 cid 数据
|
|
|
|
|
with open(cid_file_path, 'a') as file:
|
|
|
|
|
# 遍历每个 aid,构造请求 URL 并获取对应的数据
|
|
|
|
|
for aid in aids:
|
|
|
|
|
url = f'https://api.bilibili.com/x/player/pagelist?aid={aid}'
|
|
|
|
|
response = requests.get(url=url, headers=headers).json()
|
|
|
|
|
# 遍历响应数据中的每个条目,提取 cid
|
|
|
|
|
for item in response.get('data', []):
|
|
|
|
|
cid = item['cid']
|
|
|
|
|
# 将 cid 写入文件
|
|
|
|
|
file.write(f"{cid}\n")
|
|
|
|
|
count += 1
|
|
|
|
|
# 输出处理进度
|
|
|
|
|
print(f"Processed: {count} CIDs")
|
|
|
|
|
|
|
|
|
|
def remove_duplicates(file_path):
|
|
|
|
|
# 读取 cid 文件中的所有 cid
|
|
|
|
|
with open(file_path, 'r') as file:
|
|
|
|
|
cids = [line.strip() for line in file if line.strip()]
|
|
|
|
|
# 使用字典去除重复的 cid
|
|
|
|
|
unique_cids = list(dict.fromkeys(cids))
|
|
|
|
|
# 将去重后的 cid 写回文件
|
|
|
|
|
with open(file_path, 'w') as file:
|
|
|
|
|
for cid in unique_cids:
|
|
|
|
|
file.write(cid + '\n')
|
|
|
|
|
# 输出去重完成的提示
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 调用 remove_duplicates 函数,去除 cid 文件中的重复项
|
|
|
|
|
remove_duplicates(cid_file_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def fetch_danmu():
|
|
|
|
|
# 读取 cid 文件
|
|
|
|
|
print("开始爬取弹幕")
|
|
|
|
|
with open('cid.txt', 'r') as file:
|
|
|
|
|
cids = [line.strip() for line in file if line.strip()]
|
|
|
|
|
|
|
|
|
|
for cid in cids:
|
|
|
|
|
url = f'https://api.bilibili.com/x/v2/dm/web/history/seg.so?type=1&oid={cid}&date=2024-08-31'
|
|
|
|
|
response = requests.get(url=url, headers=headers)
|
|
|
|
|
response.encoding = 'utf-8'
|
|
|
|
|
# 匹配弹幕内容
|
|
|
|
|
content_list = re.findall('[\u4e00-\u9fa5]+', response.text)
|
|
|
|
|
content = '\n'.join(content_list)
|
|
|
|
|
# 将弹幕写入 comment.txt
|
|
|
|
|
with open('comment.txt', mode='a', encoding='utf-8') as f:
|
|
|
|
|
f.write(content + '\n')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 定义需要过滤的关键词或短语
|
|
|
|
|
keywords_to_remove = [
|
|
|
|
|
'出错啦',
|
|
|
|
|
'错误号',
|
|
|
|
|
'由于触发哔哩哔哩安全风控策略',
|
|
|
|
|
'该次访问请求被拒绝'
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
# 定义一个正则表达式模式,用于匹配需要删除的内容
|
|
|
|
|
pattern = re.compile('|'.join(re.escape(keyword) for keyword in keywords_to_remove))
|
|
|
|
|
|
|
|
|
|
def clean_file(input_file, output_file):
|
|
|
|
|
with open(input_file, 'r', encoding='utf-8') as infile, open(output_file, 'w', encoding='utf-8') as outfile:
|
|
|
|
|
for line in infile:
|
|
|
|
|
# 如果行中不包含需要过滤的关键词,则写入输出文件
|
|
|
|
|
if not pattern.search(line):
|
|
|
|
|
outfile.write(line)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def analyze_keywords_in_comments(comments_file, keywords_file, output_excel_file):
|
|
|
|
|
# 读取评论文件
|
|
|
|
|
with open(comments_file, 'r', encoding='utf-8') as file:
|
|
|
|
|
comments = file.readlines()
|
|
|
|
|
|
|
|
|
|
# 读取关键词列表
|
|
|
|
|
with open(keywords_file, 'r', encoding='utf-8') as file:
|
|
|
|
|
keywords = [line.strip() for line in file]
|
|
|
|
|
|
|
|
|
|
# 定义一个列表用于存储评论中的 AI 技术应用
|
|
|
|
|
ai_technologies = []
|
|
|
|
|
|
|
|
|
|
# 遍历评论,统计每个关键词的出现次数
|
|
|
|
|
for comment in comments:
|
|
|
|
|
for keyword in keywords:
|
|
|
|
|
if keyword in comment:
|
|
|
|
|
ai_technologies.append(keyword)
|
|
|
|
|
|
|
|
|
|
# 统计每个技术的出现次数
|
|
|
|
|
tech_counts = Counter(ai_technologies)
|
|
|
|
|
|
|
|
|
|
# 将统计结果转换为 DataFrame
|
|
|
|
|
df = pd.DataFrame(tech_counts.items(), columns=['AI Technology', 'Count'])
|
|
|
|
|
|
|
|
|
|
# 将 DataFrame 写入 Excel 文件
|
|
|
|
|
df.to_excel(output_excel_file, index=False)
|
|
|
|
|
|
|
|
|
|
# 排序并提取前 8 名的数据
|
|
|
|
|
top_8 = df.sort_values(by='Count', ascending=False).head(8)
|
|
|
|
|
|
|
|
|
|
# 输出前 8 名的数据
|
|
|
|
|
print(top_8)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_wordcloud(text_file, stopwords_file, output_image_file, font_path='msyh.ttc'):
|
|
|
|
|
# 加载停用词
|
|
|
|
|
def load_stopwords(file_path):
|
|
|
|
|
with open(file_path, encoding='utf-8') as f:
|
|
|
|
|
stopwords = set(f.read().strip().split('\n'))
|
|
|
|
|
return stopwords
|
|
|
|
|
|
|
|
|
|
# 读取停用词
|
|
|
|
|
stopwords = load_stopwords(stopwords_file)
|
|
|
|
|
|
|
|
|
|
# 读取文本文件
|
|
|
|
|
with open(text_file, encoding='utf-8') as f:
|
|
|
|
|
txt = f.read()
|
|
|
|
|
|
|
|
|
|
# 分词并过滤停用词
|
|
|
|
|
words = jieba.lcut(txt)
|
|
|
|
|
filtered_words = [word for word in words if word not in stopwords]
|
|
|
|
|
|
|
|
|
|
# 将处理后的词汇拼接成字符串
|
|
|
|
|
word_string = ' '.join(filtered_words)
|
|
|
|
|
|
|
|
|
|
# 生成词云
|
|
|
|
|
wc = wordcloud.WordCloud(
|
|
|
|
|
width=700,
|
|
|
|
|
height=700,
|
|
|
|
|
background_color='white',
|
|
|
|
|
font_path=font_path
|
|
|
|
|
)
|
|
|
|
|
wc.generate(word_string)
|
|
|
|
|
|
|
|
|
|
# 保存词云图
|
|
|
|
|
wc.to_file(output_image_file)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
urls = ['https://api.bilibili.com/x/web-interface/wbi/search/type?category_id=&search_type=video&ad_resource=5654&__refresh__=true&_extra=&context=&page=10&page_size=42&from_source=&from_spmid=333.337&platform=pc&highlight=1&single_column=0&keyword=2024%E5%B7%B4%E9%BB%8E%E5%A5%A5%E8%BF%90%E4%BC%9A&qv_id=2vsSEmfb9hpudunbhxw9KMMxggdECjVp&source_tag=3&gaia_vtoken=&dynamic_offset=324&web_location=1430654&w_rid=420b5e834d7dd54d76f4fba1b7b1e665&wts=1725152144',
|
|
|
|
|
'https://api.bilibili.com/x/web-interface/wbi/search/type?category_id=&search_type=video&ad_resource=5654&__refresh__=true&_extra=&context=&page=8&page_size=42&from_source=&from_spmid=333.337&platform=pc&highlight=1&single_column=0&keyword=2024%E5%B7%B4%E9%BB%8E%E5%A5%A5%E8%BF%90%E4%BC%9A&qv_id=2vsSEmfb9hpudunbhxw9KMMxggdECjVp&source_tag=3&gaia_vtoken=&dynamic_offset=252&web_location=1430654&w_rid=7fdf1d4b3f7d534c993f50173d02de3f&wts=1725152135',
|
|
|
|
|
'https://api.bilibili.com/x/web-interface/wbi/search/type?category_id=&search_type=video&ad_resource=5654&__refresh__=true&_extra=&context=&page=7&page_size=42&from_source=&from_spmid=333.337&platform=pc&highlight=1&single_column=0&keyword=2024%E5%B7%B4%E9%BB%8E%E5%A5%A5%E8%BF%90%E4%BC%9A&qv_id=2vsSEmfb9hpudunbhxw9KMMxggdECjVp&source_tag=3&gaia_vtoken=&dynamic_offset=216&web_location=1430654&w_rid=6749123b8b393589cc7c80c1e93ada58&wts=1725152132',
|
|
|
|
|
'https://api.bilibili.com/x/web-interface/wbi/search/type?category_id=&search_type=video&ad_resource=5654&__refresh__=true&_extra=&context=&page=6&page_size=42&from_source=&from_spmid=333.337&platform=pc&highlight=1&single_column=0&keyword=2024%E5%B7%B4%E9%BB%8E%E5%A5%A5%E8%BF%90%E4%BC%9A&qv_id=2vsSEmfb9hpudunbhxw9KMMxggdECjVp&source_tag=3&gaia_vtoken=&dynamic_offset=180&web_location=1430654&w_rid=74f00cf5195a9ec7ef3d57e347704770&wts=1725152128',
|
|
|
|
|
'https://api.bilibili.com/x/web-interface/wbi/search/type?category_id=&search_type=video&ad_resource=5654&__refresh__=true&_extra=&context=&page=5&page_size=42&from_source=&from_spmid=333.337&platform=pc&highlight=1&single_column=0&keyword=2024%E5%B7%B4%E9%BB%8E%E5%A5%A5%E8%BF%90%E4%BC%9A&qv_id=2vsSEmfb9hpudunbhxw9KMMxggdECjVp&source_tag=3&gaia_vtoken=&dynamic_offset=144&web_location=1430654&w_rid=e914e50a0da59031c553d631ac5f1fde&wts=1725152124',
|
|
|
|
|
'https://api.bilibili.com/x/web-interface/wbi/search/type?category_id=&search_type=video&ad_resource=5654&__refresh__=true&_extra=&context=&page=4&page_size=42&from_source=&from_spmid=333.337&platform=pc&highlight=1&single_column=0&keyword=2024%E5%B7%B4%E9%BB%8E%E5%A5%A5%E8%BF%90%E4%BC%9A&qv_id=2vsSEmfb9hpudunbhxw9KMMxggdECjVp&source_tag=3&gaia_vtoken=&dynamic_offset=108&web_location=1430654&w_rid=c622f59f9e1360765b62f0e0bc858fa1&wts=1725152121',
|
|
|
|
|
'https://api.bilibili.com/x/web-interface/wbi/search/type?category_id=&search_type=video&ad_resource=5654&__refresh__=true&_extra=&context=&page=3&page_size=42&from_source=&from_spmid=333.337&platform=pc&highlight=1&single_column=0&keyword=2024%E5%B7%B4%E9%BB%8E%E5%A5%A5%E8%BF%90%E4%BC%9A&qv_id=2vsSEmfb9hpudunbhxw9KMMxggdECjVp&source_tag=3&gaia_vtoken=&dynamic_offset=72&web_location=1430654&w_rid=a60e99a470fa19919a071c865dd1583f&wts=1725152115',
|
|
|
|
|
'https://api.bilibili.com/x/web-interface/wbi/search/type?category_id=&search_type=video&ad_resource=5654&__refresh__=true&_extra=&context=&page=2&page_size=42&from_source=&from_spmid=333.337&platform=pc&highlight=1&single_column=0&keyword=2024%E5%B7%B4%E9%BB%8E%E5%A5%A5%E8%BF%90%E4%BC%9A&qv_id=2vsSEmfb9hpudunbhxw9KMMxggdECjVp&source_tag=3&gaia_vtoken=&dynamic_offset=36&web_location=1430654&w_rid=8fc24d10311ce5e5730a84daadbbb6b3&wts=1725152102',
|
|
|
|
|
'https://api.bilibili.com/x/web-interface/wbi/search/type?category_id=&search_type=video&ad_resource=5654&__refresh__=true&_extra=&context=&page=9&page_size=42&from_source=&from_spmid=333.337&platform=pc&highlight=1&single_column=0&keyword=2024%E5%B7%B4%E9%BB%8E%E5%A5%A5%E8%BF%90%E4%BC%9A&qv_id=2vsSEmfb9hpudunbhxw9KMMxggdECjVp&source_tag=3&gaia_vtoken=&dynamic_offset=288&web_location=1430654&w_rid=a9cbd6c813f6d27561d5f0d583c0ed76&wts=1725153457',
|
|
|
|
|
] # 替换为实际的URL
|
|
|
|
|
headers = {
|
|
|
|
|
|
|
|
|
|
'cookie':'buvid4=686CE350-75FA-4921-C069-8D0E582FF02993159-024082507-y91msXDi8JTSAtvVtdhJkQ%3D%3D; buvid3=313C6A34-4C14-0939-EBE8-332F809D2EF655028infoc; b_nut=1725087454; CURRENT_FNVAL=4048; _uuid=10E7EC991-7B18-9A8B-78AA-C95F55102347103610infoc; rpdid=|(JlklRl)~Y|0J\'u~kl|)~l|l; header_theme_version=CLOSE; enable_web_push=DISABLE; is-2022-channel=1; fingerprint=f90b71618c196fb8806f458403d943fb; buvid_fp_plain=undefined; bili_ticket=eyJhbGciOiJIUzI1NiIsImtpZCI6InMwMyIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MjU4Njk1NzEsImlhdCI6MTcyNTYxMDMxMSwicGx0IjotMX0.x0CsQ6o6lx4IcK82uHYJjDq_WMedyzoqa081au5YPug; bili_ticket_expires=1725869511; bp_t_offset_1074062089=974427929414991872; buvid_fp=f90b71618c196fb8806f458403d943fb; SESSDATA=e74a05df%2C1741267229%2Ce876a%2A91CjDqLgub8fAVML6ADiSzb56IvMh3z61KnSnawN0g_c1h5emTp3cU9qrpFxgDEzzpawASVkpfc01rblFpaUxDRHViNXpJdGhweEdNY2VDdEJ0N1hvMU92SWdLcG5Dclg5dlZmV29aMWZfX2ZSWHJ5VVN3ZHRkc0ZaLU9COHdmeDR2T0tmSXlvdmt3IIEC; bili_jct=addb604342937a4322aa12322c11bc2c; DedeUserID=3546758143544046; DedeUserID__ckMd5=65316417021aa6ed; sid=7yti0jp9; b_lsid=D810C241D_191CEE2FE76; bsource=search_bing; home_feed_column=5; browser_resolution=1455-699',
|
|
|
|
|
'user-agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 Edg/128.0.0.0'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
|
#获取视频aid
|
|
|
|
|
process_urls(urls, headers)
|
|
|
|
|
print("获取视频aid完毕")
|
|
|
|
|
#将视频aid转换成cid
|
|
|
|
|
process_aid_and_cid('aid.txt', 'cid.txt', headers)
|
|
|
|
|
print("将aid转换成cid完毕,cid去重完成,结果已写回文件。")
|
|
|
|
|
#获取视频弹幕
|
|
|
|
|
fetch_danmu()
|
|
|
|
|
print("弹幕爬取完成。")
|
|
|
|
|
# 调用函数进行文件清理
|
|
|
|
|
print('开始清洗弹幕')
|
|
|
|
|
clean_file('comment.txt', 'cleaned_comment.txt')
|
|
|
|
|
print("弹幕清洗完毕")
|
|
|
|
|
#数据统计输出
|
|
|
|
|
print('开始数据统计')
|
|
|
|
|
analyze_keywords_in_comments('cleaned_comment.txt', 'keywords.txt', 'ai_technologies_count.xlsx')
|
|
|
|
|
#输出词云图
|
|
|
|
|
print("开始构建词云图")
|
|
|
|
|
generate_wordcloud('cleaned_comment.txt', 'stopwords.txt', '词云.png')
|
|
|
|
|
print("构建词云图完毕")
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
main()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|