Python新手也能跑的三个爬虫:查高校排名、扒编程题、批量存网页图

该文章已生成可运行项目,

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:三个轻量级Python爬虫脚本,全部基于requests和BeautifulSoup4实现,不装浏览器、不配环境,Python 3.6+解压即用。大学排名.py直接解析网页HTML表格,实时打印国内主流高校排名结果,并附带运行效果截图‘大学排名运行结果.jpg’方便比对;下载题目.py专攻在线编程题库页面,自动提取题目标题和描述文本,整理成结构清晰的纯文本文件PATest.txt;下载图片.py遍历页面所有img标签,按出现顺序命名(1.jpg、2.jpg…),自动保存到本地‘下载的图片’文件夹。每个脚本都带逐行中文注释,关键步骤有说明,错误处理简单明确。资源包里包含完整可运行结构:源码文件、依赖声明requirements.txt、示例输出图、测试文本,以及预设的‘大学排名’‘下载题目’‘下载图片’等空目录,开箱就能调试验证功能是否正常。适合刚学完基础语法、想动手练爬虫的新手过渡到实战。

1. 这不是“教爬虫”,是给你递了一把能拧螺丝的扳手

你刚学完 print("Hello World"),又啃完了 for 循环和字典嵌套,心里痒痒想干点“真事”——但一搜“Python爬虫教程”,满屏都是 Selenium 启动浏览器、ChromeDriver 版本对不上、pip install 报错二十行、User-Agent 随机化还要配 fake_useragent……最后关掉页面,默默打开《Python编程:从入门到实践》第12章,翻了三遍还是没敢运行那个带 requests.get() 的例子。

别急。这组脚本,就是专为这个“卡在入门最后一厘米”的你写的。

它不讲 HTTP 状态码原理,不展开 robots.txt 法律边界,不分析反爬策略演进史;它只做三件事:查高校排名、扒编程题、存网页图。每个功能都对应一个 .py 文件,每个文件都控制在 80 行以内,全部用 requests 发请求、BeautifulSoup4 解析 HTML,零浏览器、零配置、零环境冲突。你解压后双击运行(或终端敲 python 大学排名.py),3 秒内就能看到控制台刷出清华、北大、浙大的名字和分数——不是“理论上可以”,是此刻就能看见结果

关键词里写的“bs4实战”,不是指“用 BeautifulSoup 做个 demo”,而是指:你马上要亲手解析真实网页 <table> 标签里的 <tr><td> 嵌套结构,要手动定位 <div class="problem-title"> 下的 <h2> 文本,要从 <img src="..."> 里抠出带 https:///static/img/ 的完整 URL。这些不是抽象概念,是你要一行行读、一行行改、一行行调试的真实 DOM 节点。资源包里那张 大学排名运行结果.jpg,不是装饰,是你运行后第一眼该去比对的“答案页”;那个空着的 下载的图片 文件夹,不是占位符,是你五分钟后会看到 1.jpg 2.jpg 3.jpg 自动塞进去的实打实成果。

它适合谁?适合你电脑上刚装好 Python 3.8、还没碰过 pip 以外命令的新手;适合你被 ImportError: No module named 'bs4' 卡住半小时、最后靠室友远程投屏才解决的初学者;适合你只想“先跑通一个,再琢磨为什么”的实干派。这不是爬虫课的期末考卷,这是你工具箱里第一把能拧紧螺丝的扳手——它不炫技,但绝不滑丝。

2. 整体设计思路:为什么只用 requests + bs4?为什么三个脚本各自独立?

2.1 工具链极简主义:拒绝“为了爬而爬”的技术堆砌

新手最常踩的坑,不是写不出代码,而是选错了起点。很多教程一上来就推 Scrapy 框架,理由是“企业级”“高性能”;或者强推 Selenium,理由是“能渲染 JS”。但现实是:你想查个软科中国大学排名,目标页面是纯静态 HTML 表格;你想下载 PAT(中国计算机学会编程能力测试)题库的题目描述,源码里 <div class="problem-content"> 标签早已把文本明文写死;你想保存某技术博客的配图,所有 <img>src 属性都是绝对路径或相对路径,根本不需要执行 JS 动态加载。

这时候硬上 Selenium,等于为了拧一颗 M3 螺丝,先买台 CNC 加工中心——不仅成本高(启动浏览器慢、内存占用大、安装依赖多),还徒增故障点(ChromeDriver 版本错配、无头模式黑屏、元素等待超时)。而 requests + BeautifulSoup4 组合,就像一把精钢镊子:轻、准、快。requests 负责干净利落地拿到原始 HTML 字节流,bs4 负责像翻书一样逐层展开 DOM 树。它不模拟人,只解析文;不追求“像人一样点”,只专注“把字儿揪出来”。

提示:本方案明确放弃 lxml 作为 bs4 解析器(尽管它更快),默认使用 html.parser。因为 lxml 需额外 pip install lxml,而 html.parser 是 Python 标准库内置,Python 3.6+ 开箱即用。牺牲一点解析速度(对百行 HTML 几乎无感),换来的是真正的“零配置”。

2.2 功能解耦:三个脚本 = 三个可验证的原子能力

新手学习最怕“一锅炖”——一个脚本里混着发请求、解析表格、存数据库、发邮件。一旦报错,你分不清是网络问题、解析逻辑错、还是路径写错了。所以这三个脚本,严格遵循“单一职责”:

  • 大学排名.py:只做一件事——从指定 URL 抓取 HTML → 定位 <table> → 提取 <tr> 行 → 读取 <td> 单元格文本 → 格式化打印到控制台。它不存文件、不画图、不联网查其他数据,输出就是屏幕上的文字列表。
  • 下载题目.py:只做一件事——抓取题库页面 HTML → 找到所有题目容器 <div class="problem-item"> → 从中提取 <h2> 标题和 <div class="description"> 描述 → 按序拼成纯文本块 → 写入 PATest.txt。它不处理图片、不登录、不提交答案,输出就是一个 .txt 文件。
  • 下载图片.py:只做一件事——抓取任意网页 HTML → 遍历所有 <img> 标签 → 提取 src 属性值 → 判断是否为有效 URL(含 http 或以 / 开头)→ 拼接完整 URL(若为相对路径)→ 下载二进制内容 → 以 1.jpg 2.jpg 命名保存到 下载的图片/ 文件夹。它不识别图片内容、不压缩、不分类,输出就是一串有序编号的 .jpg 文件。

这种拆分,让调试变得极其简单:运行 大学排名.py 报错?一定是 HTML 结构变了或网络不通;下载题目.py 生成的 PATest.txt 为空?一定是 CSS 选择器写错了或页面结构更新了;下载图片.py 文件夹里只有 1.jpg?大概率是前几张图片 URL 是 data:image/png;base64,... 这种内联 Base64,脚本已主动跳过(后面详解)。每个脚本都是一个独立的“能力单元”,你可以单独练熟一个,再碰下一个,毫无认知负担。

2.3 目录结构即教学逻辑:空文件夹不是摆设,是你的实验沙盒

资源包里的目录树,不是随意组织的,而是刻意设计的学习路径:

├── 大学排名/          # 专门存放大学排名相关文件(脚本、截图、未来可扩展的CSV)
├── 下载题目/          # 专门存放题目文本(PATest.txt)、题库页面HTML备份
├── 下载图片/          # 专门存放下载的图片(1.jpg, 2.jpg...),脚本自动创建
├── 下载的图片/        # 脚本实际保存图片的目标文件夹(注意中文名!)
├── 大学排名.py
├── 下载题目.py
├── 下载图片.py
├── requirements.txt
└── 大学排名运行结果.jpg

关键细节在于:下载的图片/ 是脚本硬编码的保存路径(含中文),而 下载图片/ 是你手动建的、用于存放 PATest.txt 的目录。这种命名差异,强迫你注意到“脚本里写的路径”和“你看到的文件夹名”必须一致——这是新手常犯的路径错误根源。当你第一次运行 下载图片.py,它会自动创建 下载的图片/ 文件夹(如果不存在),然后往里塞图。你不需要提前建,但必须理解:脚本的 os.makedirs('下载的图片', exist_ok=True) 这行代码,就是在为你铺路。同理,大学排名/下载题目/ 是预留的“升级接口”——等你熟悉了,可以把 大学排名.py 改成导出 CSV,把文件存进 大学排名/;可以把 下载题目.py 加上自动备份原 HTML 的功能,存进 下载题目/。目录结构本身,就是一张渐进式学习地图。

3. 核心细节解析与实操要点:逐行读懂,更要懂“为什么这么写”

3.1 大学排名.py:如何从 HTML 表格里“挖”出有效数据?

我们以软科中国大学排名(假设 URL 为 https://www.shanghairanking.cn/rankings/bcur/2023)为例。打开网页源码,你会看到核心数据藏在一个 <table> 里,结构类似:

<table class="rk-table">
  <thead>
    <tr><th>排名</th><th>学校名称</th><th>总分</th></tr>
  </thead>
  <tbody>
    <tr><td>1</td><td>清华大学</td><td>952.3</td></tr>
    <tr><td>2</td><td>北京大学</td><td>937.1</td></tr>
  </tbody>
</table>

大学排名.py 的核心逻辑,就是精准定位这个 <table>,然后一层层“剥洋葱”:

# 第一步:发请求,获取HTML
response = requests.get(url, timeout=10)
response.raise_for_status()  # 关键!HTTP错误码直接抛异常,不静默失败
soup = BeautifulSoup(response.text, 'html.parser')

# 第二步:找表格(这里用class定位,比直接找table更稳妥)
table = soup.find('table', class_='rk-table')  # 注意:class_ 下划线是bs4语法!
if not table:
    print("⚠️ 未找到排名表格,请检查网页结构是否变化")
    exit(1)

# 第三步:找所有数据行(跳过<thead>,只取<tbody>里的<tr>)
rows = table.select('tbody tr')  # select()用CSS选择器,比find_all('tr')更直观

# 第四步:遍历每一行,提取<td>文本
for i, row in enumerate(rows[:10], 1):  # 只取前10名,避免刷屏
    tds = row.find_all('td')
    if len(tds) >= 3:  # 确保有足够列(排名、校名、分数)
        rank = tds[0].get_text(strip=True)      # .get_text(strip=True) 去除换行和空格
        name = tds[1].get_text(strip=True)
        score = tds[2].get_text(strip=True)
        print(f"{i:2d}. {name:<12} | 排名:{rank:>3} | 总分:{score}")

为什么这么写?经验之谈:

  • response.raise_for_status() 不是可选项。新手常忽略 HTTP 错误(如 403 Forbidden、404 Not Found),脚本静默返回空 soup,后续 find() 全是 None,最后报 AttributeError 却找不到源头。加这一行,错误立刻暴露在请求环节。
  • class_='rk-table' 中的下划线 class_ 是 bs4 强制要求(因为 class 是 Python 关键字)。新手复制网上代码时若漏掉 _,会直接语法报错。脚本里必须显式写出,形成肌肉记忆。
  • table.select('tbody tr') 优于 table.find_all('tr')。因为 <thead> 里的 <tr> 是表头,不是数据行。用 CSS 选择器 tbody tr 精准过滤,避免把“排名”“学校名称”当数据打印出来。
  • enumerate(rows[:10], 1)1 是起始序号。控制台输出 1. 2.0. 1. 更符合人类阅读习惯,且 [:10] 限流防止长列表刷屏——这是对新手终端体验的尊重。
  • get_text(strip=True)strip=True 至关重要。网页 <td> 里常有 \n \t 和空格,不清理会导致 “ 清华大学 ” 这种带空格的字符串,影响后续清洗或比较。

注意:脚本中预设的 URL 是一个稳定公开的测试页(非真实软科链接,避免法律风险)。实际使用时,你只需修改 url = "你的目标URL" 这一行。资源包里的 大学排名运行结果.jpg,就是用这个预设 URL 运行后截的图,你运行后对比,一眼可知是否成功。

3.2 下载题目.py:如何从复杂嵌套中“拎”出标题和描述?

以一个典型编程题页面为例,HTML 结构往往更深:

<div class="problem-list">
  <div class="problem-item" data-id="1001">
    <h2 class="problem-title">A+B Format</h2>
    <div class="problem-content">
      <p>Calculate a + b and output in standard format...</p>
      <p>Input consists of ...</p>
      <p>Output should be ...</p>
    </div>
  </div>
  <div class="problem-item" data-id="1002">
    <h2 class="problem-title">Spell It Right</h2>
    <div class="problem-content">
      <p>Given a non-negative integer N...</p>
    </div>
  </div>
</div>

下载题目.py 的策略是:先定位容器,再从容器里“向下挖”

# 找到所有题目容器
items = soup.select('div.problem-item')  # 用CSS选择器一次抓全
if not items:
    print("⚠️ 未找到题目容器,请检查页面结构或URL")
    exit(1)

# 遍历每个容器
for idx, item in enumerate(items, 1):
    # 从当前item里找标题(h2标签)
    title_tag = item.select_one('h2.problem-title')
    title = title_tag.get_text(strip=True) if title_tag else f"[无标题_{idx}]"

    # 从当前item里找描述(.problem-content下的所有<p>文本拼接)
    content_div = item.select_one('.problem-content')
    if content_div:
        # 获取所有<p>标签的文本,用两个换行分隔
        paragraphs = [p.get_text(strip=True) for p in content_div.find_all('p')]
        description = "\n\n".join(paragraphs)
    else:
        description = "[无描述]"

    # 写入文件(追加模式,避免覆盖)
    with open('PATest.txt', 'a', encoding='utf-8') as f:
        f.write(f"=== 题目 {idx} ===\n")
        f.write(f"标题:{title}\n")
        f.write(f"描述:\n{description}\n\n")

为什么这样设计?避坑指南:

  • soup.select('div.problem-item') 是核心。新手易犯错误是 soup.find_all('div', class_='problem-item'),但若页面有多个不同 class 的 div,可能匹配不准。CSS 选择器 div.problem-item 更精确,且 select() 返回列表,天然支持遍历。
  • item.select_one('h2.problem-title') 中的 select_one() 很关键。它确保每个题目只取第一个标题(避免重复),且返回 None 而非空列表,方便用 if title_tag else 判断。若用 find_all(),需判断 len()>0,代码冗长。
  • 描述部分用 "\n\n".join(paragraphs) 而非 "".join(paragraphs)。因为 <p> 是段落标签,自然换行是语义的一部分。合并时保留段间距,生成的 PATest.txt 才可读。
  • with open(..., 'a') 用追加模式('a')而非写入模式('w')。这是血泪教训:新手调试时反复运行脚本,若用 'w',每次都会清空之前内容,最后只剩最后一个题。'a' 保证每次运行都累加,方便验证。

提示:PATest.txt 文件名中的 PAT 是“Programming Ability Test”缩写,非特指某平台。脚本不绑定任何具体网站,你只需把目标题库页面的 URL 填进去,修改 CSS 选择器(如把 .problem-title 换成 .title-h2),就能复用。

3.3 下载图片.py:如何安全地批量下载,避开“假链接”和“防盗链”?

网页图片的 src 属性五花八门:绝对 URL(https://example.com/1.jpg)、相对 URL(/images/logo.png)、协议相对(//cdn.example.com/icon.svg)、Base64 内联(data:image/png;base64,iVBOR...)、甚至 JavaScript 动态生成(src="javascript:void(0)")。下载图片.py 的策略是:只下确定能用的,果断跳过可疑的

# 找所有img标签
img_tags = soup.find_all('img')
if not img_tags:
    print("⚠️ 页面未找到任何<img>标签")
    exit(1)

# 创建保存目录
os.makedirs('下载的图片', exist_ok=True)

downloaded_count = 0
for idx, img in enumerate(img_tags, 1):
    src = img.get('src', '').strip()
    if not src:
        continue  # 跳过src为空的img

    # 跳过data:image开头的Base64(无法直接下载)
    if src.startswith('data:image/'):
        continue

    # 构建完整URL
    if src.startswith(('http://', 'https://')):
        full_url = src
    elif src.startswith('//'):  # 协议相对URL
        parsed = urlparse(url)
        full_url = f"{parsed.scheme}:{src}"
    else:  # 相对路径,如 /images/1.jpg 或 images/2.png
        full_url = urljoin(url, src)

    # 尝试下载
    try:
        img_response = requests.get(full_url, timeout=15)
        img_response.raise_for_status()

        # 猜测文件扩展名(从Content-Type或URL后缀)
        content_type = img_response.headers.get('content-type', '')
        if 'jpeg' in content_type or 'jpg' in content_type:
            ext = '.jpg'
        elif 'png' in content_type:
            ext = '.png'
        elif 'gif' in content_type:
            ext = '.gif'
        else:
            # 退而求其次,从URL后缀猜
            ext = os.path.splitext(src)[1].lower() or '.jpg'

        # 保存文件
        filename = f"下载的图片/{idx}{ext}"
        with open(filename, 'wb') as f:
            f.write(img_response.content)
        downloaded_count += 1
        print(f"✅ 已下载:{filename} ({len(img_response.content)//1024} KB)")

    except Exception as e:
        print(f"❌ 下载失败 {full_url}:{type(e).__name__}")

print(f"\n🎉 总计成功下载 {downloaded_count} 张图片")

为什么这样处理?实战心得:

  • 坚决跳过 data:image/:这是新手最容易卡住的地方。看到 <img src="data:image/png;base64,...">,本能想“这是图片啊,得下!”,但 Base64 是编码后的字符串,不是 URL。requests.get() 对它会直接报错。脚本主动 continue,避免中断。
  • urljoin() 是相对路径救星requests 不会自动补全相对路径。urljoin('https://a.com/page/', '/img/1.jpg') 返回 'https://a.com/img/1.jpg',而 urljoin('https://a.com/page/', 'img/2.png') 返回 'https://a.com/page/img/2.png'。不用自己拼字符串,安全可靠。
  • 扩展名智能猜测:不硬编码 .jpg。先看响应头 Content-Type(服务器告诉你是啥),再 fallback 到 URL 后缀。这样 https://cdn.com/logo(无后缀)也能根据 image/svg+xml 正确存为 .svg
  • timeout=15try/except 包裹下载:图片服务器响应慢或临时不可达很常见。设超时避免卡死,except Exception 捕获所有网络异常(ConnectionError, Timeout, InvalidURL),单个失败不影响整体流程。打印具体错误类型(type(e).__name__),比 except: 更利于排查。
  • 文件名用 idx 而非 src:避免 src="/images/user%20avatar.jpg" 这种含空格、特殊字符的 URL 导致保存失败。统一用 1.jpg 2.jpg,简洁可控。

注意:脚本默认不处理防盗链(Referer)。真实场景中,有些网站会检查 Referer 头,拒绝非本站来源的请求。本脚本暂不加入 headers={'Referer': url},因为:1)多数教育类、文档类网站无此限制;2)加了反而可能触发其他反爬;3)新手应先掌握基础流程,防盗链属于进阶技巧。若你遇到大量 403,再针对性添加 Referer 即可。

4. 实操过程与核心环节实现:从解压到结果,每一步都可验证

4.1 环境准备:真的只需要三步

别被“Python环境”吓住。只要你电脑上装了 Python(确认方法:终端输入 python --versionpython3 --version,显示 3.6+ 即可),接下来就是三步:

第一步:解压资源包
- 下载 ZIP 包,右键“解压到当前文件夹”(Windows)或双击(macOS)。
- 解压后,你会看到 大学排名.py 等文件平铺在文件夹里,以及 下载的图片/ 这个空文件夹。

第二步:安装依赖(仅一次)
- 打开终端(Windows:CMD 或 PowerShell;macOS/Linux:Terminal)。
- cd 进入解压后的文件夹(例如 cd Downloads/Python爬虫新手包)。
- 运行:
bash pip install -r requirements.txt
requirements.txt 内容极简:
requests==2.31.0 beautifulsoup4==4.12.2
这会安装指定版本的 requestsbs4。版本锁定是为了避免新版本引入不兼容变更(比如 bs4 4.13 某些方法行为微调)。如果你已装过,pip 会提示“已满足要求”,直接跳过。

第三步:运行任一脚本
- 在同一终端窗口,运行:
bash python 大学排名.py
- 你会看到控制台快速刷出类似:
1. 清华大学 | 排名:1 | 总分:952.3 2. 北京大学 | 排名:2 | 总分:937.1 3. 浙江大学 | 排名:3 | 总分:925.8 ...
- 同时,对比资源包里的 大学排名运行结果.jpg,内容应完全一致。这就是你的第一个“成功信号”。

提示:若报错 ModuleNotFoundError: No module named 'requests',说明 pip install 没成功。请检查是否在正确目录下运行,或尝试 python -m pip install requests beautifulsoup4(强制用当前 Python 的 pip)。

4.2 修改目标 URL:把脚本变成你的工具

三个脚本的 URL 都写在文件顶部,形如:

# 大学排名.py 第5行
url = "https://example.com/rankings.html"  # ← 修改这里!

# 下载题目.py 第6行
url = "https://example.com/problem-set"   # ← 修改这里!

# 下载图片.py 第7行
url = "https://example.com/blog-post"       # ← 修改这里!

如何找真实 URL?手把手教你:

  1. 打开目标网页(如你想扒的编程题库页面)。
  2. 右键 → “查看网页源代码”(Chrome/Firefox)。
  3. Ctrl+F(Windows)或 Cmd+F(macOS),搜索 <table>(查排名)、<div class="problem(扒题目)、<img(存图片)
  4. 确认源码里确实有你要的数据(不是 JS 渲染出来的)。
  5. 复制浏览器地址栏的完整 URL,粘贴替换脚本里的 url = "..."

关键验证点:
- 查排名:源码里必须有 <table> 标签,且包含 <tr> <td>
- 抓题目:源码里必须有 <div class="xxx"> 包裹 <h2><p>
- 存图片:源码里 <img>src 必须是真实路径(不是 src="javascript:void(0)")。

如果源码里找不到数据,说明页面是 JS 渲染的(如 React/Vue 应用),此时 requests 无法获取,需要 Selenium——但这已超出本套件范围。本套件只服务“静态 HTML”场景,明确边界,不画大饼。

4.3 调试技巧:当脚本没按预期工作时,怎么快速定位?

新手调试的黄金法则:把“黑盒”变“透明盒”。不要猜,要亲眼看见中间结果。

技巧一:打印原始 HTML 片段
大学排名.pysoup = BeautifulSoup(...) 后,加一行:

print("前500字符HTML:", response.text[:500])

运行后,你会看到真实的 HTML 开头。如果连 <table> 都没有,说明请求没拿到正确页面(可能是重定向、404、或网站反爬返回了错误页)。

技巧二:打印解析结果
table = soup.find(...) 后,加:

print("找到的table:", table is not None)
if table:
    print("table前100字符:", str(table)[:100])

如果 table is not NoneFalse,说明 find() 失败,CSS 选择器写错了,回去检查网页源码的 class 名。

技巧三:用 soup.prettify() 看结构
在解析后,加:

with open('debug.html', 'w', encoding='utf-8') as f:
    f.write(soup.prettify())

运行后,会生成 debug.html 文件。用浏览器打开它,就能像看正常网页一样,高亮查看 <table> 在哪、<div class="problem-item"> 是否存在、<img>src 是什么——这是最直观的 DOM 检查方式。

技巧四:逐行注释法
如果某段逻辑报错,把 for 循环里的内容注释掉,只留 print(idx),确认循环能跑;再放开一行,加 print(tds),确认 find_all('td') 有结果……像搭积木一样,一层层验证。

实操心得:我第一次用这套脚本扒某高校官网时,大学排名.py 一直打印“未找到表格”。用 prettify() 生成 debug.html 打开一看,原来官网把排名放在 <div id="ranking-list"> 里,用 <ul><li> 列表展示,不是 <table>!我立刻把 soup.find('table', class_='...') 改成 soup.select('#ranking-list li'),再调整 get_text() 逻辑,5 分钟搞定。调试不是玄学,是“看见”。

4.4 运行效果与输出物详解

每个脚本的输出,都是可立即验证的实体:

脚本输出位置输出内容如何验证
大学排名.py控制台(Terminal)格式化文本列表,含排名、校名、分数对照 大学排名运行结果.jpg,逐行比对
下载题目.py当前目录 PATest.txt纯文本文件,含 === 题目 1 ===标题:...描述:...用记事本/VS Code 打开,检查是否有乱码(应为 UTF-8)、段落是否清晰
下载图片.py当前目录 下载的图片/ 文件夹1.jpg, 2.jpg, 3.jpg打开文件夹,双击任意一张图,确认能正常显示

特别注意 下载的图片/ 文件夹:
- 它是脚本自动创建的,无需你手动建。
- 文件名是纯数字 1.jpg,不包含原始 URL 中的复杂路径(如 user-avatar-2023-v2.jpg),避免中文、空格、特殊字符导致保存失败。
- 图片大小在控制台有显示(如 124 KB),帮你快速识别是否下载了空文件(0 KB)或小图标(<1 KB)。

5. 常见问题与排查技巧实录:那些我没写在注释里的坑

5.1 常见问题速查表

问题现象可能原因排查步骤解决方案
大学排名.py 运行后只打印“未找到排名表格”1. 目标网页结构已变(class名改了)
2. URL 错误(404)
3. 网站反爬返回了非HTML内容
1. 用浏览器打开 URL,右键“查看源代码”,搜 <table>
2. 在脚本里加 print(response.status_code)
3. 加 print(response.text[:200]) 看返回内容
1. 更新 soup.find('table', class_='新class名')
2. 检查 URL 拼写
3. 若返回的是登录页 HTML,说明需登录,本脚本不支持
下载题目.py 生成的 PATest.txt 是空的1. CSS 选择器不匹配(.problem-item 写错)
2. 页面实际用 <article> 而非 <div>
1. 用 soup.select('div.problem-item') 后加 print(len(items))
2. 用 soup.find_all(['div', 'article'], class_=...) 宽泛匹配
1. 在浏览器源码中精确复制 class 名(注意空格、连字符)
2. 用 soup.select('article.problem-item') 尝试
下载图片.py 下载的图片打不开/是空白1. 图片 URL 是 data:image/(已被跳过)
2. 服务器返回了 403(防盗链)
3. 文件扩展名猜错了(如 .jpg 实际是 .png
1. 查看控制台是否打印“跳过 data:image”
2. 检查下载失败日志是否含 403
3. 用 file 1.jpg(macOS/Linux)或属性查看(Windows)确认真实格式
1. 本脚本设计如此,无需处理
2. 在 requests.get() 中添加 headers={'Referer': url}
3. 手动重命名文件(如 1.jpg1.png
运行时报 UnicodeEncodeError: 'gbk' codec can't encode character(Windows)Windows 默认终端编码是 GBK,但网页是 UTF-8在脚本开头加:
import io
import sys
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
已在资源包脚本中内置此修复(针对 Windows 用户)
pip install 报错 PermissionError权限不足(尤其 macOS/Linux)运行 pip install --user -r requirements.txt--user 参数将包安装到用户目录,无需 sudo

5.2 独家避坑技巧:来自真实踩坑现场

技巧一:“先看后爬”原则
永远先手动打开目标 URL,右键“查看网页源代码”,搜索你要的数据关键词(如“清华大学”、“A+B Format”、“<img”)。如果源码里没有,requests 就不可能拿到。这是 90% 失败的根源。不要幻想“代码能变魔术”。

技巧二:timeout 参数不是摆设
我曾因没设 timeout,脚本卡在某个失效的图片 URL 上长达 2 分钟。后来所有 requests.get() 都加上 timeout=10(DNS 解析+连接+读取总时长),并配合 try/except,让失败变“可预期”。

技巧三:os.makedirs(..., exist_ok=True) 是你的朋友
新手常因忘记建 下载的图片/ 文件夹而报错 FileNotFoundErrorexist_ok=Truemakedirs 在文件夹存在时不报错,不存在时自动创建——一行代码,省去所有路径焦虑。

技巧四:用 print() 替代“我以为”
不要想当然认为 soup.find('table') 一定有结果。在关键节点后加 print(f"找到 {len(rows)} 行"),让程序替你说话。调试的本质,是让隐藏状态显性化。

技巧五:版本锁定 requirements.txt
requests 2.32 版本移除了某些旧参数,若你用新版本运行老脚本,可能报错。资源包里的 requirements.txt 锁定已验证版本,确保“解压即用”。升级前,务必本地测试。

5.3 进阶扩展建议:当你跑通第一个脚本后

这套脚本是起点,不是终点。跑通后,你可以轻松升级:

  • 导出 Excel:在 大学排名.py 末尾,用 pandas.DataFrame(data).to_excel('rankings.xlsx') 替代 print()
  • 自动重试:给 requests.get()for i in range(3): try: ... break except: time.sleep(1),应对偶发网络抖动。
  • 并发下载图片:用 concurrent.futures.ThreadPoolExecutor 并行下载,速度提升 3-5 倍(需加锁写文件)。
  • GUI 界面:用 tkinter 包一层,让用户点按钮选 URL、点按钮运行,告别终端。

但请记住:先让一把扳手拧紧一颗螺丝,再考虑买电钻。这三个脚本的价值,不在于它们多强大,而在于它们足够小、足够确定、足够让你在 5 分钟内获得正向反馈。这种“我能行”的感觉,比任何理论都珍贵。

6. 最后分享一个小技巧:如何用这组脚本,悄悄提升你的工程素养

很多人学爬虫,只盯着“数据怎么拿”,却忽略了更重要的东西:可维护性、可读性、可协作性。而这组脚本,其实在无声地示范这些。

你看 大学排名.py 的注释,不是写“发送HTTP请求”,而是写“# 用requests获取网页HTML,设置10秒超时防卡死”;下载图片.pyurljoin() 的调用,旁边注释是“# 用urljoin安全拼接相对路径,避免手动字符串拼接出错”。这些注释,不是解释 Python 语法,而是在传递一种工程师思维:每一个函数调用,都要清楚它的目的、它的风险、它的替代方案为何不选。

再看目录结构:下载的图片/ 是脚本硬编码的路径,下载图片/ 是你手动管理的目录。这种“代码路径”和“人工路径”的分离,其实在教你环境隔离——脚本只负责生成,你负责归档和备份。未来你把它集成到更大项目里,下载的图片/ 可以指向云存储桶,而 下载图片/ 仍是你本地的整理区。

还有 requirements.txt。它不只是一行 pip install 命令,它是这个项目的“DNA 身份证”。三年后你重装系统,只要 pip install -r requirements.txt,就能还原出今天一模一样的运行环境。这叫可重现性,是专业开发的基石。

所以,当你运行 python 下载图片.py,看到 1.jpg 落进文件夹时,你收获的不仅是图片,更是对“确定性”的掌控感。这种感觉,会从爬虫延伸到你写的每一个脚本、每一个函数、每一个项目。它不声不响,但足够扎实。

现在,去解压那个 ZIP 包吧。打开终端,敲下 python 大学排名.py。三秒后,清华、北大、浙大的名字会出现在你眼前——不是幻觉,不是教程里的假设,是此刻,你亲手启动的、真实世界的数据流。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:三个轻量级Python爬虫脚本,全部基于requests和BeautifulSoup4实现,不装浏览器、不配环境,Python 3.6+解压即用。大学排名.py直接解析网页HTML表格,实时打印国内主流高校排名结果,并附带运行效果截图‘大学排名运行结果.jpg’方便比对;下载题目.py专攻在线编程题库页面,自动提取题目标题和描述文本,整理成结构清晰的纯文本文件PATest.txt;下载图片.py遍历页面所有img标签,按出现顺序命名(1.jpg、2.jpg…),自动保存到本地‘下载的图片’文件夹。每个脚本都带逐行中文注释,关键步骤有说明,错误处理简单明确。资源包里包含完整可运行结构:源码文件、依赖声明requirements.txt、示例输出图、测试文本,以及预设的‘大学排名’‘下载题目’‘下载图片’等空目录,开箱就能调试验证功能是否正常。适合刚学完基础语法、想动手练爬虫的新手过渡到实战。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

本文章已经生成可运行项目
重要提示】本资源设置为0积分下载,若非0积分请勿轻易下载 亲爱的CSDN用户: 首先感谢你点进这个资源页面。我需要提前说明一个重要情况: 本资源原本已设置为“0积分下载”,即作者希望完全免费共享。但CSDN平台有时会根据文件的下载热度、文件大小、用户权限等因素,自动将部分资源的积分调整为非0数值(如1积分、2积分、5积分等)。这是平台系统的自动行为,而非作者本人的设定。 因此,如果你当前看到该资源的下载所需积分不是0(例如显示为1、2、3……),请谨慎决定是否下载。 如果你按照非0积分支付并下载后发现资源内容不符合预期、链接失效,或者实际上该资源本应是免费的,作者无法为此承担积分损失或退还操作。强烈建议:仅在页面显示为0积分时进行下载。 另外,本资源描述中并未直接提供具体的下载地址或外部链接,因为它本身是一个通过CSDN官方上传通道提交的文件/内容包。如果你看到描述中没有外部网盘地址,这是正常的——资源文件应通过CSDN内置的“下载”按钮获取。若因平台积分显示异常导致你支付了积分,请优先联系CSDN客服咨询积分退还政策,作者没有权限修改平台自动设定的积分值。 感谢你的理解与支持。技术分享本应开放,但受限于平台规则,特此提醒如上。祝学习进步!
重要提示】本资源设置为0积分下载,若非0积分请勿轻易下载 亲爱的CSDN用户: 首先感谢你点进这个资源页面。我需要提前说明一个重要情况: 本资源原本已设置为“0积分下载”,即作者希望完全免费共享。但CSDN平台有时会根据文件的下载热度、文件大小、用户权限等因素,自动将部分资源的积分调整为非0数值(如1积分、2积分、5积分等)。这是平台系统的自动行为,而非作者本人的设定。 因此,如果你当前看到该资源的下载所需积分不是0(例如显示为1、2、3……),请谨慎决定是否下载。 如果你按照非0积分支付并下载后发现资源内容不符合预期、链接失效,或者实际上该资源本应是免费的,作者无法为此承担积分损失或退还操作。强烈建议:仅在页面显示为0积分时进行下载。 另外,本资源描述中并未直接提供具体的下载地址或外部链接,因为它本身是一个通过CSDN官方上传通道提交的文件/内容包。如果你看到描述中没有外部网盘地址,这是正常的——资源文件应通过CSDN内置的“下载”按钮获取。若因平台积分显示异常导致你支付了积分,请优先联系CSDN客服咨询积分退还政策,作者没有权限修改平台自动设定的积分值。 感谢你的理解与支持。技术分享本应开放,但受限于平台规则,特此提醒如上。祝学习进步!
内容概要:本文研究基于模型预测算法的混合储能微电网双层能量管理系统,提出一种结合优化调度与实时控制的能量管理策略。通过构建上层长期优化与下层实时调整相结合的双层协同架构,采用模型预测控制(MPC)算法对微电网中的可再生能源出力、储能系统充放电行为及负荷需求进行多时间尺度的协同优化,有效提升系统运行的经济性、稳定性和能源利用效率。研究详细阐述了系统建模方法、运行约束条件设定、多目标优化函数设计以及Matlab仿真代码的具体实现流程,通过仿真验证了该方法在降低综合运行成本、平抑功率波动、增强系统灵活性和应对不确定性方面的优越性能; 适合人群:具备电力系统、自动化、电气工程或能源系统等相关专业背景,熟悉Matlab/Simulink仿真环境,从事微电网、综合能源系统、智能电网优化调度等方向研究的研究生、科研人员及工程技术人员; 使用场景及目标:①用于微电网能量管理系统的设计与教学仿真;②为含多种储能形式的综合能源系统提供优化调度方案的技术参考;③支撑科研课题、学术论文撰写及工程项目中的算法验证与性能评估; 阅读建议:建议读者结合提供的Matlab代码逐模块分析,重点理解双层架构的设计逻辑、MPC滚动优化机制及约束处理技巧,可进一步拓展应用于含电动汽车、氢能储能或多元负荷的复杂微网系统中进行二次开发与创新研究。
内容概要:本文围绕三相逆变器模型仿真及软开关技术展开研究,基于Simulink平台构建了完整的系统仿真模型,深入分析了三相逆变器的拓扑结构、工作原理与动态响应特性。研究重点聚焦于软开关技术(如零电压开关ZVS、零电流开关ZCS)在逆变器中的应用,通过仿真验证其在降低开关损耗、提高转换效率、减小电磁干扰等方面的显著优势。文章详细阐述了软开关的实现条件与控制策略设计,结合LCL滤波器优化与PWM调制技术,提升了系统整体性能。通过对电压、电流波形及功率因数等关键指标的仿真分析,验证了所提出方案的有效性与可行性,为高性能逆变器的设计与优化提供了理论依据和技术支撑。; 适合人群:具备电力电子、电气工程及其自动化等相关专业背景,熟悉Simulink仿真环境,从事新能源发电、电力变换器设计、微电网控制或电能质量治理等领域研究的科研人员、工程技术人员及研究生。; 使用场景及目标:①用于高校电力电子课程教学与实验,辅助学生理解逆变器工作机理及软开关技术原理;②为工业界高效率逆变电源、光伏并网逆变器、储能变流器等产品的研发提供技术参考;③支持相关领域科研人员开展新型拓扑与先进控制算法的仿真验证与学术论文撰写。; 阅读建议:建议读者结合文中所述Simulink模型进行动手实践,重点关注软开关触发时序、谐振参数设计与系统稳定性之间的关系,同时可延伸学习死区效应补偿、锁相环控制、孤岛检测等相关技术以构建完整的逆变系统知识体系。
内容概要:本文提出了一种基于粒子群优化算法(PSO)优化长短期记忆网络(LSTM)的电力负荷预测方法,并配套提供了完整的Python代码实现。该方法通过PSO算法自动搜索LSTM模型的关键超参数(如隐层节点数、学习率、迭代次数等),以克服传统手动调参效率低、易陷入局部最优的问题,从而提升模型在电力负荷预测任务中的预测精度与泛化能力。文中系统阐述了PSO-LSTM混合模型的架构设计、数据预处理流程、参数优化机制、模型训练与评估方法,重点解决了电力负荷数据所具有的强时序性、非线性及周期性波动等挑战,适用于短期与中期负荷预测场景。; 适合人群:具备一定Python编程基础和机器学习理论知识,从事电力系统分析、能源管理、智能电网或相关领域研究的研发人员、工程技术人员及高校研究生。; 使用场景及目标:①应用于电网调度、电力市场运营等环节,提升负荷预测准确性,保障供电可靠性与经济性;②为综合能源系统、需求侧响应、储能优化配置等提供高精度的负荷输入数据;③作为深度学习与智能优化算法融合的典型案例,为解决其他复杂时序预测问题(如风电、光伏出力预测)提供技术参考与实现范式。; 阅读建议:建议读者结合所提供的代码进行动手实践,深入理解PSO算法如何引导LSTM超参数寻优的全过程,重点关注适应度函数设计、参数编码方式与模型集成逻辑,并可在不同地区、不同时间粒度的负荷数据集上进行迁移验证,以全面掌握该混合模型的调优策略与适用边界。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值