AgenBlog

爬虫教程(一)-基础知识与简单爬取

2026/02/07
36
0

声明!本教程旨在进行技术了解与学习,不可用于恶意获取隐私信息、破坏互联网绿色环境的违法行为!!!违者后果自负

引言

“在互联网的世界里,数据就像散落在沙滩上的珍珠。”我们通过浏览器访问网页,就像是用眼睛在看;而爬虫(Web Scraper),本质上是一段模拟人类行为的代码,它自动化地敲开目标网站的大门,将那些珍珠收集并整理到我们的口袋里。简单来说,爬虫就是‘自动化数据采集脚本’。

而最常见的爬虫编写语言就是Python,究其原因,并不是因为其他语言做不到,而是因为 Python 把这件事做到了极致的‘优雅’与‘简单’。Python 拥有一个极其庞大的生态圈,就像是一个随取随用的百宝箱:

  • Requests库让网络请求变得像写英语句子一样自然;

  • BeautifulSouplxml赋予了我们像外科医生手术刀一样精准提取数据的能力;

  • 更不用说处理海量数据的 Pandas

在 Python 的世界里,你不需要去关心底层的网络套接字或繁琐的内存管理,你只需要关注你的目标:数据在哪里,以及如何拿到它。

前期准备

在正式开启爬虫之旅前,我们需要搭建一个稳定、高效的开发环境。

  • Python 解释器: 建议安装 Python 3.8 或更高版本。

  • 编辑器(IDE): 推荐使用 VS Code 或 PyCharm。

  • 核心库安装:

    pip install requests lxml beautifulsoup4

其他相关技术

在编写爬虫之前,我们必须学会如何利用浏览器的开发者工具(F12)来透视网页的本质。

1. 寻找数据的坐标

右键点击网页上你想要抓取的文字,选择“检查(Inspect)”。在弹出的 Elements 面板中,你会看到这行文字被包裹在层层叠叠的 HTML 标签里。

  • 标签名(Tag): 如 <h1>, <p>, <a>

  • 类名与 ID(Class/ID):这是我们定位数据的关键索引,就像是门牌号。

2. 网络监听站(Network)

点击开发者工具顶部的 Network 选项卡,然后刷新页面。你会看到密密麻麻的请求列表。

找到入口:找到那个与网页 URL 同名的请求,查看它的 Response。如果里面包含了网页的文字内容,说明这是一款“静态网页”,我们的第一关就选它。

从实战中学习

我们通过一个最简单的页面抓取来了解爬虫的工作原理:

import requests
from bs4 import BeautifulSoup
​
# 1. 目标地址(以博客链接为例)
url = 'https://blog.agenblog.cn/archives/hello-halo' 
​
# 2. 伪装请求头
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
}
​
# 3. 发送请求
response = requests.get(url, headers=headers)
​
if response.status_code == 200:
    # 4. 解析网页
    soup = BeautifulSoup(response.text, 'html.parser')
    
    # 5. 提取标题(保持原样)
    title = soup.title.string
    print(f"【文章标题】: {title}\n")
​
    # 6. 提取正文内容 
    # 注意:这里的 'article' 或 'div' 需要根据实际网页结构调整
    # 大多数博客的正文在 <article> 标签或类名为 content 的 div 中
    # 我们这里的博客采用的halo,正文内容实际上被包裹在一个 <section> 标签里,并且带有一个特定的 CSS 类名 prose,具体可通过查看页面源代码获知
    content_area = soup.find('section', class_='prose')
    
    if content:
        # get_text() 会过滤掉所有 HTML 标签,只留下纯文字
        # strip=True 自动去除首尾多余的空格和换行
        print("【文章正文】:")
        print(content.get_text(strip=True))
    else:
        print("未能找到正文标签,请检查网页结构。")
else:
    print(f"请求失败,状态码:{response.status_code}")

在默认情况下,Python 的 requests 库会诚实地告诉服务器:“嘿,我是一段脚本”。很多服务器出于安全考虑会直接拒绝这种请求。通过在代码中加入 User-Agent,我们将自己伪装成了一个普通的 Chrome 浏览器用户,从而大幅提高爬取的成功率。

有时候,当你满怀信心跑通代码,却发现抓回来的中文变成了‘火星文’(如 æ­¤æ­),别慌,你遇到了爬虫路上的第一只拦路虎:字符编码不匹配。为什么会这样?因为计算机并不认识中文,它只认识 0 和 1。中文在存储时需要经过一套规则(如 UTF-8)编码,而读取时必须用同样的规则解码。如果服务器发给你的是‘苹果’(UTF-8),你的代码却用‘香蕉’(ISO-8859-1)的规则去读,结果就是满屏乱码。因此在 Python 中,我们只需要在获取响应后增加一行简单的逻辑: response.encoding = response.apparent_encoding 。这行代码就像给爬虫配了一副‘自动对焦眼镜’,它会扫描网页内容,识别出正确的字符集,让中文瞬间‘原形毕露’。

我们还可以在原有的排版上进行优化,让它的输出格式尽可能和原有的一样,同时还可以保存在本地以方便查阅,为此我们

import requests
from bs4 import BeautifulSoup
​
url = 'https://blog.agenblog.cn/archives/hello-halo' 
​
headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
}
​
try:
    response = requests.get(url, headers=headers, timeout=10)
    response.encoding = response.apparent_encoding 
​
    if response.status_code == 200:
        soup = BeautifulSoup(response.text, 'html.parser')
        title = soup.title.string if soup.title else "未命名文章"
        print(f"【文章标题】: {title}\n")
        content_area = soup.find('section', class_='prose')
        
        if content_area:
            # 使用 separator="\n" 在标签转换时插入换行,保持易读性
            final_text = content_area.get_text(separator="\n", strip=True)
            
            print("【文章正文】:")
            print(final_text)
            print("\n" + "="*30)
            
            # 8. 持久化存储:保存到本地文件
            # 过滤掉标题中可能存在的非法文件名字符
            safe_title = "".join([c for c in title if c not in r'\/:*?"<>|']).strip()
            with open(f"{safe_title}.txt", "w", encoding="utf-8") as f:
                f.write(f"标题: {title}\n")
                f.write(f"来源: {url}\n")
                f.write("-" * 20 + "\n")
                f.write(final_text)
            print(f"文件已保存为: {safe_title}.txt")
            
        else:
            print("未能找到正文标签(section.prose),请检查网页结构。")
    else:
        print(f"请求失败,状态码:{response.status_code}")
​
except Exception as e:
    print(f"发生错误: {e}")

很多人在抓取正文时会发现,原本错落有致的文章,抓下来后却变成了密不透风的‘文字墙’。这是因为 HTML 中的换行是由 <p><br> 等标签实现的,纯粹的 get_text() 会无情地抹杀这些结构。

为了还原阅读体验,我们有两个绝招:

  1. 间隔符(Separator):在调用 get_text() 时,传入 separator="\n"。这相当于告诉爬虫:‘每当你跳过一个 HTML 标签时,请帮我按一下回车键。’

  2. 标签过滤法:通过 find_all 精准提取 <h2>(标题)、<p>(段落)和 <blockquote>(引用)。将它们存入列表后,再用 "\n\n".join() 重新缝合。

这样抓下来的数据,不仅是冷冰冰的信息,更是一篇赏心悦目的文章。