Python爬虫使用实例-mdrama
一个Python爬虫使用实例:主要用于下载指定的剧集音频。分别从网页和json文件中获取剧集的title和剧集中所存在音频的id,调用you-get,最后自动重命名下载文件夹为剧集名title。
目标网址:
https://www.missevan.com/mdrama/×××××
其中×××××为drama的id,例如28021
- 从getdrama的json文件中获取drama列表中的id和name
- 用you-get获取音乐【
1.使用pyautogui调用you-get,2. 使用 os.system() 直接调用 you-get 命令行工具】 - drama文件夹自动重命名为剧集名
- 下载多个drama
一、获取资源
首先明确需求,需要获取音频的id和name分别作为data和title,查看源码,发现看不到:
可以从getdrama的json文件中获取id和name(不,用you-get的话只需要id)。
以28021为例,0-276,共277个music
小细节:听剧的时候一般是不会出现drama的,他一般是出现sound/player,点大标题可以跳转到drama,或者去源码查看(关于url)
二、发送请求
获取json文件
json_url = f'https://www.missevan.com/dramaapi/getdrama?drama_id={drama_id}'
# 将获取的 JSON 数据写入文件
with open('getdrama.json', 'w', encoding='utf-8') as f:f.write(response.text)
读取json文件
# 读取 getdrama.json 文件
with open('getdrama.json', 'r', encoding='utf-8') as f:content = f.read()
因为用的是you-get,需要提前切换到西文状态下,而文件夹名可能含有中文字符,所以在download之后才重命名为title。
# 下载音频文件
download_dir = 'D:/drama1/'
if not os.path.exists(download_dir):os.mkdir(download_dir)(省略部分代码....)download()
os.rename(download_dir, f'D:/{title}/')
三、数据解析
获取drama的id
drama_id = re.search(r'/mdrama/(\d+)', url).group(1) # 提取 drama_id
获取drama的名称:
title = selector.css('.title-content::text').get().replace(' ','').replace('\n','')
获取drama中音频的id
# 匹配包含 "id" 和 "sound_id" 的 JSON 对象
pattern = r'\{"id":.*?\}' # 正则表达式匹配模式
matches = re.findall(pattern, content)# 提取匹配的 JSON 对象中的所需字段
results = []for match in matches:try:data_dict = json.loads(match)sound_id = data_dict.get("sound_id")if sound_id:results.append({"name": data_dict.get("name"), "sound_id": sound_id})except json.JSONDecodeError:continue # 忽略 JSON 解码错误# 将结果列表转换为 DataFrame
df = pd.DataFrame(results)
四、保存数据
you-get -o {download_dir} {url}
五、代码实现
1/ 单个剧集
单个drama可以自动改文件名 ,drama列表暂时不能
# 单个剧集mdrama 下载后自动改文件名
import os
import re
import json
import time
import requests
import parsel
import pyautogui
import pyperclip
import pandas as pdurl ='https://www.missevan.com/mdrama/23468'
headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.139 Safari/537.36'}
drama_id = re.search(r'/mdrama/(\d+)', url).group(1) # 提取 drama_id
json_url = f'https://www.missevan.com/dramaapi/getdrama?drama_id={drama_id}'
response = requests.get(json_url)
response1 = requests.get(url=url,headers=headers)
selector = parsel.Selector(response1.text)
title = selector.css('.title-content::text').get().replace(' ','').replace('\n','')
invalid_chars_pattern = r'[\/:*?"<>|]' # 定义需要替换的非法字符
title = re.sub(invalid_chars_pattern, '|', title) # 将非法字符替换为空格
print(title)
# 将获取的 JSON 数据写入文件
with open('getdrama.json', 'w', encoding='utf-8') as f:f.write(response.text)# 读取 getdrama.json 文件
with open('getdrama.json', 'r', encoding='utf-8') as f:content = f.read()# 匹配包含 "id" 和 "sound_id" 的 JSON 对象
pattern = r'\{"id":.*?\}' # 正则表达式匹配模式
matches = re.findall(pattern, content)# 提取匹配的 JSON 对象中的所需字段
results = []
for match in matches:try:data_dict = json.loads(match)sound_id = data_dict.get("sound_id")if sound_id:results.append({"name": data_dict.get("name"), "sound_id": sound_id})except json.JSONDecodeError:continue # 忽略 JSON 解码错误# 将结果列表转换为 DataFrame
df = pd.DataFrame(results)# 下载音频文件
download_dir = 'D:/drama1/'
if not os.path.exists(download_dir):os.mkdir(download_dir)# 打开命令提示符
pyautogui.hotkey("win", "r")
pyautogui.typewrite('cmd')
time.sleep(3)
pyautogui.press('enter')
time.sleep(3)def download():for sound_id in df['sound_id']:url = f"https://www.missevan.com/sound/player?id={sound_id}"pyautogui.typewrite(f'you-get -o {download_dir} {url}')# pyperclip.copy(url)# pyautogui.hotkey("ctrl", "v")pyautogui.press("enter")download()
os.rename(download_dir, f'D:/{title}/')
2/ 剧集列表
url是列表:
import os
import re
import json
import time
import requests
import parsel
import pyautogui
import pyperclip
import pandas as pd# 设置多个剧集的 URL 列表
urls = ['https://www.missevan.com/mdrama/23468','https://www.missevan.com/mdrama/1003', # 添加更多剧集的 URL# 可以继续添加其他 URL
]
# 遍历每个 URL 进行处理
for url in urls:headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.139 Safari/537.36'}drama_id = re.search(r'/mdrama/(\d+)', url).group(1) # 提取 drama_idjson_url = f'https://www.missevan.com/dramaapi/getdrama?drama_id={drama_id}'response = requests.get(json_url)response1 = requests.get(url=url, headers=headers)selector = parsel.Selector(response1.text)title = selector.css('.title-content::text').get().replace(' ', '').replace('\n', '')invalid_chars_pattern = r'[\/:*?"<>|]' # 定义需要替换的非法字符title = re.sub(invalid_chars_pattern, '|', title) # 将非法字符替换为空格print(title)# 将获取的 JSON 数据写入文件with open('getdrama.json', 'w', encoding='utf-8') as f:f.write(response.text)# 读取 getdrama.json 文件with open('getdrama.json', 'r', encoding='utf-8') as f:content = f.read()# 匹配包含 "id" 和 "sound_id" 的 JSON 对象pattern = r'\{"id":.*?\}' # 正则表达式匹配模式matches = re.findall(pattern, content)# 提取匹配的 JSON 对象中的所需字段results = []for match in matches:try:data_dict = json.loads(match)sound_id = data_dict.get("sound_id")if sound_id:results.append({"name": data_dict.get("name"), "sound_id": sound_id})except json.JSONDecodeError:continue # 忽略 JSON 解码错误# 将结果列表转换为 DataFramedf = pd.DataFrame(results)# 下载音频文件download_dir = f'D:/drama_{drama_id}/' # download_dir = 'D:/drama1/'if not os.path.exists(download_dir):os.mkdir(download_dir)# 打开命令提示符pyautogui.hotkey("win", "r")pyautogui.typewrite('cmd')time.sleep(3)pyautogui.press('enter')time.sleep(3)def download():for sound_id in df['sound_id']:url = f"https://www.missevan.com/sound/player?id={sound_id}"pyautogui.typewrite(f'you-get -o {download_dir} {url}')# pyperclip.copy(url)# pyautogui.hotkey("ctrl", "v")pyautogui.press("enter")download()
3/ 继续优化
优化代码:
-
删除 pyautogui 部分:使用 os.system() 直接调用 you-get 命令行工具,这消除了对图形界面的依赖并提高了代码的效率与稳定性。(原来使用的是pyautogui打开cmd命令提示符来使用you-get)
-
批量重命名:在所有下载完成后,再统一进行批量重命名,添加了 download_directories 列表,用于存储每个 download_dir 和对应的 title。
出现了一个可能导致文件夹重命名失败的问题。title 变量在for循环中要在循环体内获取,每次URL处理时应动态产生,而不是在群体内静态不变。调整代码:
4/ 最新代码
# 配合urls.txt使用
import os
import re
import json
import time
import parsel
import requests# 读取 URL 列表
url_file = 'urls.txt' # 假设 URL 列表存储在这个文本文件中
with open(url_file, 'r', encoding='utf-8') as f:urls = [line.strip() for line in f if line.strip()] # 逐行读取并去除空行headers = {'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.139 Safari/537.36'
}# 整体结果列表
total_results = []
download_directories = [] # 用于存储下载目录和标题的对应关系# 遍历每个 URL
for url in urls:drama_id = re.search(r'/mdrama/(\d+)', url).group(1) # 提取 drama_idjson_url = f'https://www.missevan.com/dramaapi/getdrama?drama_id={drama_id}'response = requests.get(json_url)# 从响应获取标题response_text = response.text#pattern_title = r'"title":"(.*?)"' # 假设标题在 JSON 中#title_match = re.search(pattern_title, response_text)#title = title_match.group(1).replace(' ', '').replace('\n', '') if title_match else f'drama_{drama_id}'selector = parsel.Selector(requests.get(url, headers=headers).text)title = selector.css('.title-content::text').get().replace(' ', '').replace('\n', '')invalid_chars_pattern = r'[\/:*?"<>|]' # 定义需要替换的非法字符title = re.sub(invalid_chars_pattern, '|', title) # 将非法字符替换为空格print(title)# 将获取的 JSON 数据写入文件json_filename = f'{drama_id}_getdrama.json'with open(json_filename, 'w', encoding='utf-8') as f:f.write(response_text)# 读取 JSON 文件with open(json_filename, 'r', encoding='utf-8') as f:content = f.read()# 匹配包含 "id" 和 "sound_id" 的 JSON 对象pattern = r'\{"id":.*?\}' # 正则表达式匹配模式matches = re.findall(pattern, content)# 提取匹配的 JSON 对象中的所需字段results = []for match in matches:try:data_dict = json.loads(match)sound_id = data_dict.get("sound_id")if sound_id:results.append({"name": data_dict.get("name"), "sound_id": sound_id})except json.JSONDecodeError:continue # 忽略 JSON 解码错误# 将结果添加到总结果列表total_results.extend(results)# 下载音频文件download_dir = f'D:/drama_{drama_id}/'download_directories.append((download_dir, title)) # 保存目录和标题if not os.path.exists(download_dir):os.makedirs(download_dir)# 下载音频文件(使用 you-get 命令行工具)for sound in results:sound_id = sound['sound_id']download_url = f"https://www.missevan.com/sound/player?id={sound_id}"os.system(f'you-get -o "{download_dir}" {download_url}')time.sleep(2) # 添加延时,避免命令太快# 所有下载完成后,批量重命名文件夹
for download_dir, title in download_directories:target_dir = f'D:/{title}/'if not os.path.exists(target_dir): # 检查重命名目标文件夹是否存在os.rename(download_dir, target_dir)print(f"文件夹 '{download_dir}' 已重命名为 '{target_dir}'")else:print(f"目标文件夹 '{target_dir}' 已存在,跳过重命名。")
运行结果: