爬虫-oiwiki

news2025/7/14 23:48:39

我们将BASE_URL 设置为 "https://oi-wiki.org/" 后脚本就会自动开始抓取该url及其子页面的所有内容,并将统一子页面的放在一个文件夹中

import requests
from bs4 import BeautifulSoup
from urllib.parse import urljoin, urlparse
import os
import pdfkit
from urllib3.exceptions import InsecureRequestWarning

# 禁用SSL警告
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)

# 配置wkhtmltopdf路径
config = pdfkit.configuration(wkhtmltopdf='/usr/local/bin/wkhtmltopdf')

BASE_URL = "https://oi-wiki.org/"
DOMAIN = urlparse(BASE_URL).netloc

headers = {
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36",
    "Accept-Language": "zh-CN,zh;q=0.9"
}

visited = set()
queue = [BASE_URL]

def is_valid_url(url):
    parsed = urlparse(url)
    return (
        parsed.netloc == DOMAIN and
        not parsed.fragment and
        not url.endswith(('.zip', '.pdf', '.jpg', '.png'))
    )

def extract_links(html, base_url):
    soup = BeautifulSoup(html, 'html.parser')
    links = []
    for a in soup.find_all('a', href=True):
        full_url = urljoin(base_url, a['href']).split('#')[0]
        if is_valid_url(full_url) and full_url not in visited:
            links.append(full_url)
            visited.add(full_url)
    return links

def fetch_page(url):
    try:
        print(f"[*] 抓取中: {url}")
        res = requests.get(url, headers=headers, verify=False, timeout=30)
        res.encoding = 'utf-8'
        return res.text
    except Exception as e:
        print(f"[!] 抓取失败: {url} - {str(e)}")
        return None

def clean_html(html, url):
    soup = BeautifulSoup(html, 'html.parser')
    
    # 移除所有顶部导航和侧边栏相关元素
    for tag in soup.select('.navbar, .page-toc, .sidebar, footer, .giscus, .page-footer, .page-actions'):
        tag.decompose()
    
    # 仅保留主内容区域
    main_content = soup.select_one('main article') or soup.select_one('article') or soup
    
    # 修正资源路径
    for tag in main_content.find_all(['img', 'a']):
        for attr in ['href', 'src']:
            if tag.has_attr(attr):
                tag[attr] = urljoin(url, tag[attr])
    
    # 获取有效标题(使用最后一个有效路径段)
    title_parts = urlparse(url).path.strip('/').split('/')
    title = title_parts[-1].replace('-', ' ').title() if title_parts else "Document"

    return f"""
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>{title}</title>
        <style>
            body {{ 
                font-family: 'Noto Sans CJK SC', Arial, sans-serif;
                line-height: 1.6;
                margin: 2em;
            }}
            /* 保持原有样式 */
        </style>
    </head>
    <body>
        <h1>{title}</h1>
        {main_content}
    </body>
    </html>
    """

def save_as_pdf(html, url):
    parsed = urlparse(url)
    path_segments = [seg for seg in parsed.path.strip('/').split('/') if seg]
    
    if len(path_segments) > 1:
        dir_path = os.path.join('output', *path_segments[:-1])
        filename = f"{path_segments[-1]}.pdf"
    else:
        dir_path = 'output'
        filename = "index.pdf"
    
    os.makedirs(dir_path, exist_ok=True)
    full_path = os.path.join(dir_path, filename)
    
    try:
        pdfkit.from_string(html, full_path, configuration=config, options={
            'encoding': "UTF-8",
            'enable-local-file-access': None,
            'quiet': ''  # 隐藏控制台输出
        })
        print(f"[√] 已保存: {full_path}")
    except Exception as e:
        print(f"[!] PDF生成失败: {full_path} - {str(e)}")

def crawl():
    while queue:
        current_url = queue.pop(0)
        html = fetch_page(current_url)
        if not html:
            continue
            
        new_links = extract_links(html, current_url)
        queue.extend(new_links)
        
        cleaned_html = clean_html(html, current_url)
        save_as_pdf(cleaned_html, current_url)

if __name__ == "__main__":
    print("🚀 启动爬虫,目标站点:", BASE_URL)
    visited.add(BASE_URL)
    crawl()
    print("✅ 所有内容已保存至 output/ 目录")

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2342289.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

强化学习核心原理及数学框架

1. 定义与核心思想 强化学习&#xff08;Reinforcement Learning, RL&#xff09;是一种通过智能体&#xff08;Agent&#xff09;与环境&#xff08;Environment&#xff09;的持续交互来学习最优决策策略的机器学习范式。其核心特征为&#xff1a; ​​试错学习​​&#x…

【技术派后端篇】技术派中 Session/Cookie 与 JWT 身份验证技术的应用及实现解析

在现代Web应用开发中&#xff0c;身份验证是保障系统安全的重要环节。技术派在身份验证领域采用了多种技术方案&#xff0c;其中Session/Cookie和JWT&#xff08;JSON Web Token&#xff09;是两种常用的实现方式。本文将详细介绍这两种身份验证技术在技术派中的应用及具体实现…

【基础】Node.js 介绍、安装及npm 和 npx功能了解

前言 后面安装n8n要用到&#xff0c;做一点技术储备。主要是它的两个工具&#xff1a;npm 和 npx。 Node.js介绍 Node.js 是一个免费的、开源的、跨平台的 JavaScript 运行时环境&#xff0c;允许开发人员在浏览器之外编写命令行工具和服务器端脚本&#xff0c;是一个基于 C…

助力网站优化利用AI批量生成文章工具提升质量

哎&#xff0c;有时候觉得写东西这事儿吧&#xff0c;真挺玄乎的。你看着那些大网站的优质内容&#xff0c;会不会突然冒出个念头——这些家伙到底怎么做到日更十篇还不秃头的&#xff1f;前阵子我蹲在咖啡馆里盯着屏幕发呆&#xff0c;突然刷到个帖子说现在用AI写文章能自动纠…

SpringBootTest报错

Unable to find a SpringBootConfiguration, you need to use ContextConfiguration or … 解决方案&#xff1a;在SpringTest注解中添加属性&#xff08;classes &#xff09;填写启动类 如我的启动类是MainApplication.class javax.websocket.server.ServerContainer no…

w~视觉~合集3

我自己的原文哦~ https://blog.51cto.com/whaosoft/12327888 #几个论文 Fast Charging of Energy-dense Lithium-ion Batteries Real-time Short Video Recommendation on Mobile Devices Semantic interpretation for convolutional neural networks: What makes a ca…

Redis安装及入门应用

应用资料&#xff1a;https://download.csdn.net/download/ly1h1/90685065 1.获取文件&#xff0c;并在该文件下执行cmd 2.输入redis-server-lucifer.exe redis.windows.conf&#xff0c;即可运行redis 3.安装redis客户端软件 4.安装后运行客户端软件&#xff0c;输入链接地址…

NODE_OPTIONS=--openssl-legacy-provider vue-cli-service serve

//"dev": " NODE_OPTIONS--openssl-legacy-provider vue-cli-service serve" // 修改后&#xff08;Windows 适用&#xff09; "dev": "vue-cli-service serve --openssl-legacy-provider" 升级 Node.js 到 v14&#xff0c;确保依赖…

上篇:深入剖析 BLE 底层物理层与链路层(约5000字)

引言 在无线通信领域,Bluetooth Low Energy(BLE)以其超低功耗、灵活的连接模式和良好的生态支持,成为 IoT 与可穿戴设备的首选技术。要想在实际项目中优化性能、控制功耗、保证可靠通信,必须对 BLE 协议栈的底层细节有深入了解。本篇将重点围绕物理层(PHY)与链路层(Li…

2025 年“泰迪杯”数据挖掘挑战赛B题——基于穿戴装备的身体活动监测问题分析

摘要 本文聚焦于基于穿戴设备采集的加速度计数据,深入研究志愿者在日常活动中的行为特征,构建了多个数学建模框架,实现从身体活动监测、能耗预测、睡眠阶段识别到久坐预警等多个目标。我们依托于多源数据融合与机器学习模型,对人体活动状态进行识别与分析,为健康管理、行…

Linux424 chage密码信息 gpasswd 附属组

https://chat.deepseek.com/a/chat/s/e55a5e85-de97-450d-a19e-2c48f6669234

自定义指令简介及用法(vue3)

一介绍 防抖与节流&#xff0c;应用场景有很多&#xff0c;例如&#xff1a;禁止重复提交数据的场景、搜索框输入搜索条件&#xff0c;待输入停止后再开始搜索。 防抖 点击button按钮&#xff0c;设置定时器&#xff0c;在规定的时间内再次点击会重置定时器重新计时&#xf…

【Spring Boot】深入解析:#{} 和 ${}

1.#{} 和 ${}的使用 1.1数据准备 1.1.1.MySQL数据准备 &#xff08;1&#xff09;创建数据库&#xff1a; CREATE DATABASE mybatis_study DEFAULT CHARACTER SET utf8mb4;&#xff08;2&#xff09;使用数据库 -- 使⽤数据数据 USE mybatis_study;&#xff08;3&#xff…

从实验室到产业端:解码 GPU 服务器的八大核心应用场景​

一、深度学习与人工智能的基石​ 在深度学习领域&#xff0c;GPU 服务器的并行计算架构成为训练大规模模型的核心引擎 —— 传统 CPU 集群训练千亿参数模型需数月&#xff0c;而基于某国际知名芯片厂商 H100 的 GPU 服务器可将周期缩短至数周&#xff0c;国内科技巨头 910B 芯…

java—12 kafka

目录 一、消息队列的优缺点 二、常用MQ 1. Kafka 2. RocketMQ 3. RabbitMQ 4. ActiveMQ 5. ZeroMQ 6. MQ选型对比 适用场景——从公司基础建设力量角度出发 适用场景——从业务场景角度出发 四、基本概念和操作 1. kafka常用术语 2. kafka常用指令 3. 单播消息&a…

数据库-数据类型、约束 和 DQL语言

标题目录 数据类型数字类型INT 型BIGINT 型DOUBLE 类型 字符类型定长字符串变长字符串 日期类型 约束主键约束非空约束唯一性约束检查约束外键约束 DQL 语言WHERE 子句连接多个条件IN (列表)NOT IN (列表)BETWEEN...AND...DISTINCT多字段去重 模糊查询NULL 值判断排序&#xff…

Dify升级-linux环境下使用zip离线安装方式部署升级

Dify安装时Linux服务器到github网络不好&#xff0c;git clone拉去不下来代码。使用本地windows电脑下载zip包形式上传进行了安装。但是随着dfiy版本升级&#xff0c;本地使用最新版本的&#xff0c;也需要进行下升级。参考升级指导以及自己环境情况&#xff0c;升级步骤如下。…

基于SpringBoot+Vue的影视系统(源码+lw+部署文档+讲解),源码可白嫖!

摘要 时代在飞速进步&#xff0c;每个行业都在努力发展现在先进技术&#xff0c;通过这些先进的技术来提高自己的水平和优势&#xff0c;影视推荐系统当然不能排除在外。影视系统是在实际应用和软件工程的开发原理之上&#xff0c;运用Java语言以及Spring Boot、VUE框架进行开…

搭建Stable Diffusion图像生成系统实现通过网址访问(Ngrok+Flask实现项目系统公网测试,轻量易部署)

目录 前言 背景与需求 &#x1f3af; 需求分析 核心功能 网络优化 方案确认 1. 安装 Flask 和 Ngrok 2. 构建 Flask 应用 3. 使用 Ngrok 实现内网穿透 4. 测试图像生成接口 技术栈 实现流程 优化目标 实现细节 1. 迁移到Flask 2. 持久化提示词 3. 图像下载功能 …

差分信号抗噪声原理:

差分信号抗噪声原理&#xff1a; 差分信号除了能很好地解决发送和接收参考点电位不同的问题外&#xff0c;差分信号的另一个重要优势就是在一定条件下其抗干扰能力比单端信号更强。对于单端信号传输&#xff0c;外界对它的干扰噪声直接叠加在信号上&#xff0c;接收端直接检测输…