12、用类写出更可控、更易扩展的爬虫框架🕷

0 阅读2分钟

写爬虫不是 copy 一段 requests.get(),而是写出能自动重试、统一解析、多站适配、可扩展的结构。今天,我们用 面向对象的思想,写一个迷你爬虫框架。


✅ 本文目标

  • 用类封装爬虫逻辑,做到高内聚低耦合
  • 支持多站点适配(抽象 + 子类继承)
  • 实现请求封装、解析模块、自动重试、通用日志
  • 最终支持:python crawl.py zhihupython crawl.py juejin

🧠 一、从最基础的爬虫说起

import requests
from bs4 import BeautifulSoup

resp = requests.get("https://juejin.cn")
soup = BeautifulSoup(resp.text, "html.parser")
print(soup.title.text)

问题:逻辑分散、复用性差、站点切换困难。


🏗 二、抽象出通用爬虫类(Spider)

import requests
from abc import ABC, abstractmethod
from bs4 import BeautifulSoup

class BaseSpider(ABC):
    name = "base"

    def fetch(self, url):
        try:
            print(f"📥 正在请求:{url}")
            resp = requests.get(url, timeout=5)
            resp.raise_for_status()
            return resp.text
        except Exception as e:
            print(f"❌ 请求失败:{e}")
            return ""

    def parse_html(self, html):
        return BeautifulSoup(html, "html.parser")

    @abstractmethod
    def parse(self, html):
        pass

    def run(self):
        url = self.get_url()
        html = self.fetch(url)
        if html:
            self.parse(html)

    @abstractmethod
    def get_url(self):
        pass

🧩 三、继承实现不同站点爬虫

📘 示例 1:知乎首页标题

class ZhihuSpider(BaseSpider):
    name = "zhihu"

    def get_url(self):
        return "https://www.zhihu.com"

    def parse(self, html):
        soup = self.parse_html(html)
        title = soup.title.text.strip()
        print(f"知乎首页标题:{title}")

💻 示例 2:掘金首页标题

class JuejinSpider(BaseSpider):
    name = "juejin"

    def get_url(self):
        return "https://juejin.cn"

    def parse(self, html):
        soup = self.parse_html(html)
        title = soup.title.text.strip()
        print(f"掘金首页标题:{title}")

🧪 四、命令行选择站点并运行

# crawl.py  
import argparse  
from spider import ZhihuSpider, JuejinSpider # ✅ 从 spider.py 中导入类  
  
def main():  
spiders = {  
"zhihu": ZhihuSpider(),  
"juejin": JuejinSpider()  
}  
  
parser = argparse.ArgumentParser()  
parser.add_argument("site", help="站点名,如:zhihu / juejin")  
args = parser.parse_args()  
  
spider = spiders.get(args.site)  
if not spider:  
print(f"❌ 不支持的站点:{args.site}")  
return  
  
spider.run()  
  
if __name__ == "__main__":  
main()

运行:

python crawl.py zhihu
python crawl.py juejin

image.png


✅ 最终结构建议

project/
├── spiders/
│   ├── base.py
│   ├── zhihu.py
│   └── juejin.py
├── crawl.py

使用方式不变,模块更清晰,方便扩展新站点。


🔧 Bonus:添加日志、重试机制

fetch() 中加入 retry 重试逻辑:

def fetch(self, url, retry=3):
    for i in range(retry):
        try:
            print(f"📥 第{i+1}次请求:{url}")
            resp = requests.get(url, timeout=5)
            resp.raise_for_status()
            return resp.text
        except Exception as e:
            print(f"⚠️ 第{i+1}次失败:{e}")
    print("❌ 最终失败")
    return ""

💡 拓展挑战

  1. 添加代理池支持
  2. 解析具体内容如:知乎热榜、掘金推荐文章列表
  3. 写一个通用 save() 方法导出为 Markdown 或 JSON

🧠 总结

面向对象不只是 OOP 理论,它是你写出真正工程化爬虫系统的第一步。