异步Python爬虫:实战某视频网站凡人修仙传 (附完整代码) 🚀✨
在这篇充满魔法的教程中,我们将一起探索如何使用Python的异步编程特性,以闪电般的速度从网络上爬取并下载资源。想象一下,我们将如何轻松地从网站上捕获视频文件,并将它们异步下载到我们的宝库中。准备好了吗?让我们开始这场冒险吧!🧙♂️🔮
开始之前 📚🔧
在我们跳入代码的海洋之前,请确保你的Python环境中已经安装了以下神奇的工具:
aiohttp
:闪电般发送HTTP请求的法杖。aiofiles
:异步操作文件的魔法书。lxml
:解析HTML的神秘卷轴。asyncio
:控制时间流速的时光轮。
如果在安装aiotqdm
(一个展示进度的魔镜)时遇到难题,不用担心!我们可以选择其他方式来观察进度,或者干脆让这部分保持神秘。🔍💡
爬虫逻辑解析 🕵️♂️📖
我们的冒险之旅包含几个关键步骤:
- 获取列表页数据:首先,我们会向目标网站发出信号,请求列表页的内容。
- 解析列表页数据:在列表页的海洋中寻找通往详情页的航标。
- 获取详情页数据:通过异步飞毯访问每个详情页,获取它们的秘密。
- 解析详情页数据:在详情页中找到隐藏的视频文件URL。
- 异步下载视频文件:像空中的燕子一样,轻盈快速地下载每个视频文件,并将它们安全带回家。
代码实现 🧑💻📝
我们的代码像一本古老的魔法书,里面记载了召唤异步爬虫的咒语:
# 异步获取详情页数据
async def parse_detail_data(session, url):
# 在这里,我们将揭开详情页的秘密...
# 异步下载视频文件
async def download_ts_file(session, ts_url, ts_sequence, buffer_dict):
# 使用异步飞毯,将视频文件一片片地带回家...
# 主函数
async def main(url):
# 用你的法杖触碰这段代码,启动整个魔法仪式...
完整代码
"""
http://www.gxychina.com/
http://www.gxychina.com/chi/69.html 凡人修仙传列表
http://www.gxychina.com/na/69-1-85.html 凡人修仙传第一章 1-85
ERROR: Could not find a version that satisfies the requirement aiotqdm (from versions: none)
ERROR: No matching distribution found for aiotqdm 找不到满足aiotqdm要求的版本(来自版本:none)错误:未找到aioqdm的匹配分发
"""
import aiofiles
import aiohttp
import asyncio
import requests
from lxml import etree
import os
import time
import pprint
import re
from tqdm import tqdm
# 域名
prefix_url = "http://www.gxychina.com"
headers = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"
}
# 获取列表页数据
def get_list_data(url):
requests_get = requests.get(url=url, headers=headers)
requests_get.encoding = "UTF-8"
return requests_get.text
# 解析数据
def parse_list_data(html):
etree_html = etree.HTML(html)
playUrls = etree_html.xpath("//ul[@class='myui-content__list sort-list clearfix']/li/a/@href")
return playUrls
# 获取剧名
def get_name(html):
etree_html = etree.HTML(html)
name = etree_html.xpath("//h1[@class='title text-fff']/text()")
return name
# 获取标题名
def getTitles(html):
etree_html = etree.HTML(html)
Titles = etree_html.xpath("//ul[@class='myui-content__list sort-list clearfix']/li/a/@title")
return Titles
# 获取详情页
async def parse_detail_data(session, url):
async with session.get(url) as response:
response.encoding = "UTF-8"
text = await response.text()
# 获页面中的m3u8地址
search = re.search(';var now="(.*?)";var', text)
return search
# 访问ts文件url拿到文件保存
async def get_ts_data(session, url):
async with session.get(url) as response:
response.encoding = "UTF-8"
ts_data = await response.text() # 加上圆括号来调用方法
# 文本处理 从,开始 到#E结束
ts_urls = re.findall(',\n(.*?)\n#E', ts_data, re.S)
return ts_urls
def create_directory(directory_path):
try:
# 如果目录不存在,则创建它
if not os.path.exists(directory_path):
os.makedirs(directory_path)
print(f"目录 '{directory_path}' 已创建。")
except Exception as e:
print(f"创建目录时发生错误: {e}")
async def download_ts_file(session, ts_url, ts_sequence, buffer_dict):
async with session.get(ts_url) as response:
response.encoding = "UTF-8"
ts_data = await response.content.read()
buffer_dict[ts_sequence] = ts_data
async def download_ts_data(session, ts_urls, name, title):
path_name = name[0].strip()
create_directory(path_name)
file_path = f"./{path_name}/{title}.mp4"
print("保存路径:", file_path, "开始下载 ts 文件")
buffer_dict = {}
async with aiofiles.open(file_path, mode="ab") as f:
tasks = [asyncio.create_task(download_ts_file(session, ts_url, i, buffer_dict))
for i, ts_url in enumerate(ts_urls)]
await asyncio.gather(*tasks)
# 确保按正确的顺序写入文件
for i in range(len(ts_urls)):
await f.write(buffer_dict[i])
print("所有下载任务已完成")
async def download_detail_data(session, url, title, name):
detail_data = await parse_detail_data(session, url)
m3u8 = detail_data.group(1)
# https://new.1080pzy.co/20211029/Oil0h7pU/index.m3u8 这是详情页返回的m3u8地址
# https://new.1080pzy.co/20211029/Oil0h7pU/1400kb/hls/index.m3u8 通过页面上关键字查找,发现ts文件来自这个接口
# 处理获取ts的链接
ts_url = m3u8.replace("index.m3u8", "1400kb/hls/index.m3u8") # 这里的1400kb是固定的,不知道是什么意思 直接替换
ts_data = await get_ts_data(session, ts_url)
# 下载ts文件
await download_ts_data(session, ts_data, name, title)
async def main(url):
# 创建统一的session
async with aiohttp.ClientSession() as session:
# 获取列表页数据
list_data = get_list_data(url)
data = parse_list_data(list_data)
name = get_name(list_data)
titles = getTitles(list_data)
tasks = []
for url, title in zip(data, titles):
play_url = prefix_url + url
print(play_url)
task = asyncio.create_task(download_detail_data(session, play_url, title, name))
tasks.append(task)
break # 测试只下载一集
await asyncio.wait(tasks)
if __name__ == '__main__':
# 计时
start_time = time.time()
# url
url = "http://www.gxychina.com/chi/69.html"
# 然后在某个异步上下文中调用 main 函数
asyncio.run(main(url))
end_time = time.time()
print(f"耗时:{end_time - start_time}") # 60.40073251724243
结语 🌟💫
通过这篇充满魔力的教程,你现在已经掌握了如何使用Python的异步编程特性来提高爬虫的效率。不过记得,伟大的力量伴随着伟大的责任,使用这些技能时,请确保你的行为符合法律法规和道德准则。
免责声明 🛡️⚖️
本教程提供的信息和代码仅供学习和研究目的使用,不得用于任何非法用途。作者不对因使用本教程内容造成的任何直接或间接损失负责。
如果你喜欢这次冒险,请给我一颗星星作为鼓励吧!🌟 你的支持是我不断探索和分享更多魔法的动力。如果你有任何疑问或想要交流更多的魔法知识,欢迎留言讨论。直到下次冒险,保持好奇,继续探索!🚀📚