编程的奇妙之旅:Python多线程爬虫下载PDF教材

有一天,当我坐在电脑前,思考着编程的奇妙之处时,一种冒险的感觉涌上心头。我想,为什么不用编程来探索互联网的深处,下载一些有趣的教材呢?于是,我开始了这次奇妙的编程之旅,带着好奇心和激情,我将向你展示如何使用Python编写一个多线程爬虫,用于下载各种教材的PDF文件。

准备工作

在我们踏上这次冒险之前,我们需要做一些准备工作。

初始化日志

首先,我们需要设置日志记录,以便随时跟踪我们的编程冒险。

import logging

def init_logging():
    logging_format = '%(asctime)s\t[%(levelname)s]\t[%(name)s]\t[%(threadName)s]\t%(message)s'
    logging.basicConfig(format=logging_format, level=logging.INFO)

导入必要的库

为了让我们的编程冒险变得更加容易,我们需要使用requests库来进行网络请求和数据下载。如果你还没有安装这个库,请使用以下命令来安装它:

pip install requests

设置下载目录和线程数量

这两个变量将在后面的代码中使用,你可以根据需要进行调整。

download_base_dir = 'd:/book3'  # 设置下载文件保存的文件夹
thread_count = 8  # 设置线程数量

开始编写爬虫

请求数据版本

首先,我们需要获取教材数据的版本信息,这将有助于我们确定要下载的文件。

def request_data_version() -> [str]:
    # 目标URL
    data_url = "https://s-file-1.ykt.cbern.com.cn/zxx/ndrs/resources/tch_material/version/data_version.json"
    try:
        logging.info("开始请求URL: %s", data_url)
        response = requests.get(data_url)
        logging.info("状态码 %s, 响应内容: %s", response.status_code, response.text)
        if response.status_code == 200:
            data = response.json()
            return data["urls"].split(",")
        else:
            logging.warning("无法获得正确的响应,程序退出 -1。")
            sys.exit(-1)
    except requests.exceptions.RequestException as e:
        logging.exception("请求数据版本失败", e)

请求单个数据URL

接下来,我们将请求每个教材的数据URL,并解析教材信息。

def request_single_data_url(data_url: str) -> [dict]:
    try:
        logging.info("开始请求URL: %s", data_url)
        response = requests.get(data_url)
        logging.info("状态码 %s", response.status_code)
        if response.status_code == 200:
            data = response.json()
            books = []
            for d in data:
                book_data = parse_book_data(d)
                if len(book_data) != 0:
                    books.append(book_data)
                else:
                    logging.warning("跳过此教材: %s", d['title'])
            return books
    except requests.exceptions.RequestException as e:
        logging.exception("请求单个数据URL失败", e)

解析教材数据

我们将解析每本教材的数据,包括教材名称和标签。

def parse_book_data(data: dict) -> dict:
    book_id = data['id']
    book_name = data['title']
    book_tags = data['tag_list']
    book_tags = sorted(book_tags, key=custom_sort_tag_list, reverse=True)
    if '版' not in (book_tags[0]['tag_name']):
        return {}
    return {"id": book_id, "name": book_name, "dirs": get_dirs_from_tags(book_tags)}

获取目录信息

我们将从标签中提取目录信息,以便稍后创建文件夹。

def get_dirs_from_tags(book_tags) -> str:
    return f"{book_tags[0]['tag_name']}/{book_tags[1]['tag_name']}/{book_tags[2]['tag_name']}"

自定义标签排序

为了确保我们获取的是正确的教材版本,我们需要自定义排序标签。

def custom_sort_tag_list(tag):
    tag_name = tag['tag_name']
    school_level, edition, grade, semester, course_name = '', '', '', '', ''
    if "版" in tag_name:
        edition = tag_name
    elif "年级" in tag_name:
        grade = tag_name
    elif "册" in tag_name:
        semester = tag_name
    elif '小学' in tag_name or '初中' in tag_name or '高中' in tag_name:
        school_level = tag_name
    elif '教材' not in tag_name:
        course_name = tag_name

    return edition, school_level, grade, course_name, semester

下载教材

最后,我们将下载每本教材的PDF文件。

def download_book(book):
    file_dir = f"{download_base_dir}/{book['dirs']}"
    file_name = f"{book['name']}.pdf"
    file_full_path = os.path.join(file_dir, file_name)
    logging.info("开始下载教材: %s/%s", file_dir, file_name)
    try:
        if not os.path.exists(file_dir):
            os.makedirs(file_dir)
    except FileExistsError:
        logging.warning("文件夹 %s 已经存在", file_dir)

    download_url = f"https://r1-ndr.ykt.cbern.com.cn/edu_product/esp/assets_document/{book['id']}.pkg/pdf.pdf"
    response = requests.get(download_url)
    if response.status_code == 200:
        with open(os.path.join(file_dir, file_name), "wb") as file:
            file.write(response.content)
            logging.info("教材下载成功: %s", file_full_path)
    else:
        logging.warning("教材下载失败,状态码: %s", response.status_code)

多线程下载

我们将使用多线程来提高下载速度。

def multi_thread_download

_books(shared_queue: queue.Queue):
    try:
        while True:
            item = shared_queue.get_nowait()
            download_book(item)
            shared_queue.task_done()
    except queue.Empty:
        logging.warning("队列为空")

请求所有教材

最后,我们将请求所有教材的信息并将其添加到共享队列中。

def request_all_books_to_shared_queue():
    shared_queue = queue.Queue(maxsize=3000)
    urls = request_data_version()
    for url in urls:
        books = request_single_data_url(url)
        for book in books:
            shared_queue.put(book)
    return shared_queue

执行下载

现在,我们已准备好执行下载任务。

if __name__ == '__main__':
    init_logging()

    # 下载文件保存的文件夹
    download_base_dir = 'd:/book3'
    # 线程数量
    thread_count = 8

    all_books_queue = request_all_books_to_shared_queue()
    with concurrent.futures.ThreadPoolExecutor(max_workers=thread_count) as executor:
        futures = [executor.submit(multi_thread_download_books, all_books_queue) for i in range(thread_count)]
        concurrent.futures.wait(futures)

    logging.info("完成下载!!!")

结束语

通过这个教程,你已经学会了如何使用Python编写一个多线程爬虫,用于下载各种教材的PDF文件。这个项目不仅展示了编程的实际应用,还提供了深入探索网络资源的机会。希望你能充分利用这些技能,探索更多有趣的项目和挑战!

本文由作者 王大神 原创发布于 大神网的AI博客。

转载请注明作者:王大神

原文出处:编程的奇妙之旅:Python多线程爬虫下载PDF教材

(0)
打赏 微信扫一扫 微信扫一扫
上一篇 2023年10月4日
下一篇 2023年10月4日

相关推荐

  • python调用openai.api制作一个chatgpt对话机器人。

    在现代科技的快速发展下,人工智能领域的创新不断涌现,其中自然语言处理(NLP)领域的进步尤为引人注目。OpenAI作为NLP领域的领军者之一,提供了强大的API,使开发者能够轻松构建自己的自然语言处理应用。在本教程…

    2023年3月5日
    00
  • 打造神器:使用Python和Web3创建铭文批量铸造工具

    铭文在许多区块链游戏和应用中扮演着重要的角色。然而,如果你需要大量铸造铭文,手动完成这项任务将是一项繁重和耗时的工作。为了解决这个问题,我们可以使用Python和Web3库创建一个铭文批量铸造工具,以便更高效…

    2023年12月15日
    00
  • 抖音WebSocket二进制解码教程:解密抖音视频流的神秘面纱

    抖音,作为全球热门的社交媒体平台,吸引了数以亿计的用户,每天都在分享各种各样的视频内容。你可能曾经想过,这些视频是如何在抖音上实时传输和播放的?答案之一就是WebSocket。WebSocket是一种用于实时通信的协…

    2023年11月2日
    00
  • 动态类型语言中如何确定返回值类型:Python实践指南

    在Python的世界中,张三正面临一个挑战。他正在使用一个新的第三方库,但遇到了一个问题:每次调用函数,由于缺乏类型提示,他都不知道返回的数据类型是什么。看源码,但似乎很复杂,IDE没有给出有用的提示。张三开…

    2023年10月9日
    00
  • 盘点AIGC工具

    人工智能技术正在迅速崛起,改变了我们生活的方方面面。在创意领域,一种新的工具正逐渐崭露头角,那就是AIGC(Artificial Intelligence Generated Content)工具,它们利用人工智能技术生成文字、图片、视频、编程…

    2023年4月6日
    00
  • 教程:使用Python爬取抖音用户的视频

    嗨,各位技术爱好者和数据挖掘狂热者们!你是否曾经在抖音上看到一个超赞的用户,想要保存他们的视频以便离线观看?或者你是一位研究者,需要获取某个用户在一段时间内发布的所有视频?今天,我将带你进入一个令人…

    2023年10月24日
    00
  • LeetCode 算法汇总—链表

    开篇故事: 从小学开始,数学就伴随着我们的成长。而如今,在计算机科学领域,算法就如同数学一样重要。这就像我们每天面对的一道谜题,需要巧妙的思考和解决。在这篇文章中,我将向你展示算法中一个重要而又有趣的…

    2023年10月10日
    00
  • 开源:60行代码用openai代替人工写SEO文章发布到wordpress

    在今天的数字时代,拥有一个具有高质量内容的网站对于吸引流量和提高排名至关重要。然而,频繁地为网站创建新的SEO友好文章可能是一项耗时的任务。本教程将介绍如何使用OpenAI和Python来自动生成SEO文章,并将其发…

    2023年2月14日
    00
  • Python错误标准异常总结:让你成为异常处理大师

    嗨,亲爱的Python学习者们!在编写代码时,错误是不可避免的。Python提供了丰富的异常处理机制,以帮助你更好地理解和处理代码中的错误。本文将为你总结Python中的标准异常,让你成为异常处理的大师! 开篇故事 在…

    2023年9月25日
    00
  • 如何转换网易云音乐缓存文件为MP3格式

    在一个雨后的傍晚,我躺在窗前听着喜欢的音乐,感叹于网络的发达,我们可以轻松地在线听到任何我们想听的音乐。但突然之间,我想起一个问题:如果某一天我去了一个没有网络的地方,我还能听到这些音乐吗?当然,购…

    2023年10月10日
    00