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

简介:Python-weibospider是一个基于Python开发的微博数据爬取工具,旨在自动化采集用户信息、微博内容、点赞数、评论数等公开数据。项目利用requests发送网络请求,结合BeautifulSoup或lxml解析页面,并通过Selenium处理动态加载内容。为应对反爬机制,集成IP代理池、验证码识别与会话管理技术。支持多线程或异步加速抓取,数据可存储至CSV、JSON或数据库中。该项目涵盖Web爬虫核心技术,适合作为Python爬虫学习与实战的完整案例。

Python Web爬虫实战:从零构建微博数据采集系统 🚀

你有没有试过想抓取微博热搜榜的数据做分析,结果发现页面是动态加载的?或者刚写好一个简单的爬虫,跑不了几次就被封IP了?😅 别急,这几乎是每个爬虫新手都会踩的坑。今天咱们就来一次把这件事说透—— 如何用Python打造一个稳定、高效、能长期运行的微博爬虫系统 。

我们不光讲“怎么写代码”,更要深入理解背后的机制:为什么微博这么难爬?JavaScript渲染、登录验证、反爬策略……这些到底是怎么工作的?又该如何一一破解?

准备好了吗?☕️ 倒杯咖啡,咱们从最基础的HTTP请求开始,一步步搭建属于你的“微博数据工厂”。


🔧 爬虫的本质:不只是 requests.get()

很多人以为爬虫就是发个 requests.get(url) ,然后 response.text 拿回来解析一下完事。但现实往往更复杂。

想象一下,你在浏览器里打开 weibo.com ,看到的是什么?是一个完整的社交平台界面。可你知道吗?这个页面背后可能涉及 十几个独立的API调用 ,每条微博、每个评论、每张图片,都是通过Ajax异步拉取的。更别说还有登录态、Token、加密参数这些“门禁系统”拦在前面。

所以,真正的Web爬虫其实是这样一套协同工作的系统:

  • 请求调度器 :控制谁先谁后,要不要重试;
  • HTML/JSON解析器 :精准提取目标数据;
  • 会话管理模块 :维持登录状态,像真人一样浏览;
  • 反爬对抗层 :伪装成浏览器、换IP、控频率;
  • 数据存储管道 :把抓到的信息存下来,结构化处理。

今天我们就要围绕这几个核心模块,手把手带你打通全流程。🎯


📡 用 requests 构建可靠的网络通信基石

要说Python爬虫界的“瑞士军刀”,那必须是 requests 库。它简洁、强大、社区支持完善,几乎成了所有爬虫项目的标配。

import requests

response = requests.get("https://httpbin.org/get")
print(response.json())

就这么几行代码,就已经完成了一次完整的HTTP交互。但别小看这简单的接口,它的背后藏着很多可以精细操控的空间。

GET vs POST:你真的知道什么时候该用哪个吗?

HTTP协议定义了多种请求方法,其中最常用的两个就是 GET 和 POST 。

特性 GET POST
参数位置 URL 查询字符串 请求体(body)
数据长度限制 有限(通常2048字符以内) 几乎无限制
安全性 较低(参数暴露在URL中) 相对较高
幂等性 是(多次请求效果相同) 否(可能触发多次动作)
缓存支持 可被浏览器缓存 不缓存
典型应用场景 搜索、分页、静态资源获取 登录、注册、评论提交

举个例子🌰:你想爬微博热搜榜。

打开浏览器开发者工具(F12),切换到 Network 标签页,刷新页面,你会发现有一个请求长这样:

https://m.weibo.cn/api/container/getIndex?type=wbtopclick&class=pl_top_click_v6

→ 这就是一个典型的 GET 请求 ,参数都在URL上,返回JSON格式数据。

我们可以轻松复现:

url = "https://m.weibo.cn/api/container/getIndex"
params = {
    "type": "wbtopclick",
    "class": "pl_top_click_v6"
}

response = requests.get(url, params=params)
if response.status_code == 200:
    data = response.json()
    for item in data['data']['cards'][0]['card_group']:
        print(item['desc'], item['mblog']['attitudes_count'])

但如果要模拟用户登录呢?这时候就必须用 POST 了。

login_url = "https://login.sina.com.cn/sso/login.php"
payload = {
    "username": "your_username",
    "password": "your_password",
    "entry": "sso"
}
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
}

session = requests.Session()
response = session.post(login_url, data=payload, headers=headers)

注意这里用了 Session() 对象!它是维持登录状态的关键——接下来的所有请求都会自动带上服务器返回的 Cookie,就像你登录后继续浏览网页一样自然。

💡 小贴士:如果接口接受 JSON 格式的 body(比如现代REST API),记得用 json= 而不是 data= :

python requests.post(api_url, json={"key": "value"}, headers=headers)

这样会自动设置 Content-Type: application/json ,并序列化字典为JSON字符串发送。


🕵️‍♂️ 请求头伪造:让服务器相信你是“真人”

你以为发个请求就能成功?Too young too simple 😏

现在的网站早就学会了识别“机器人”。最常见的手段就是检查 User-Agent 。

默认情况下, requests 的 UA 是这样的:

python-requests/2.28.1

一眼就能看出是脚本行为。解决办法很简单:换一个看起来像真实浏览器的UA。

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
                  "AppleWebKit/537.36 (KHTML, like Gecko) "
                  "Chrome/124.0.0.0 Safari/537.36",
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
    "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8",
    "Accept-Encoding": "gzip, deflate",
    "Connection": "keep-alive",
    "Upgrade-Insecure-Requests": "1",
    "Cache-Control": "max-age=0"
}

这些字段可不是随便写的:

  • User-Agent :声明客户端类型,建议轮换主流浏览器UA;
  • Accept-* :告诉服务器你能处理哪些内容;
  • Connection: keep-alive :启用持久连接,减少TCP握手开销;
  • Referer :某些资源防盗链时需要指定来源页面;
  • X-Requested-With :标识这是Ajax请求(常用于绕过简单反爬);

为了提高隐蔽性,最好建立一个 UA池 ,每次随机选一个:

import random

USER_AGENTS = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36...",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36...",
    "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36..."
]

def get_random_ua():
    return random.choice(USER_AGENTS)

headers["User-Agent"] = get_random_ua()

甚至可以结合 fake_useragent 库实现全自动轮换:

pip install fake-useragent
from fake_useragent import UserAgent
ua = UserAgent()
headers["User-Agent"] = ua.random

这样一来,你的请求看起来就跟普通用户没两样了 👀


🛑 状态码与响应处理:别让异常中断整个流程

网络世界充满不确定性。一次失败的请求可能导致整个爬虫崩溃。因此,我们必须对各种情况做好预案。

常见的HTTP状态码及其含义如下:

状态码 含义 处理建议
200 OK 请求成功 正常解析
301/302 重定向 自动跟随或手动处理
400 Bad Request 请求语法错误 检查参数格式
401 Unauthorized 未授权 补全认证信息
403 Forbidden 禁止访问 更换IP/UA,可能是反爬触发
404 Not Found 资源不存在 忽略或记录日志
500+ 服务端错误 延迟重试

我们可以封装一个带重试机制的请求函数:

import time
import requests

def fetch_with_retry(url, headers=None, max_retries=3):
    for i in range(max_retries):
        try:
            response = requests.get(url, headers=headers, timeout=10)
            if response.status_code == 200:
                return response
            elif response.status_code == 403:
                print(f"🚫 访问被拒 (403),正在第 {i+1}/{max_retries} 次重试...")
                time.sleep(2 ** i)  # 指数退避
            elif response.status_code == 404:
                print("❌ 资源不存在")
                return None
            else:
                print(f"⚠️ 未知状态码: {response.status_code}")
                time.sleep(1)
        except requests.exceptions.RequestException as e:
            print(f"💥 请求失败: {e}")
            time.sleep(2)
    return None

同时,还要根据响应头判断内容类型,灵活解析:

content_type = response.headers.get('Content-Type', '')

if 'application/json' in content_type:
    data = response.json()
elif 'text/html' in content_type:
    html = response.text
elif 'image/' in content_type:
    image_data = response.content  # 二进制流
else:
    raw = response.content

🤔 提醒: response.text 是解码后的字符串(基于响应编码),而 response.content 是原始字节流,适合下载文件或处理乱码问题。

下面这张图清晰展示了整个决策流程:

graph TD
    A[发起HTTP请求] --> B{状态码判断}
    B -->|200| C[解析响应内容]
    B -->|3xx| D[处理重定向]
    B -->|403| E[休眠并重试]
    B -->|404| F[记录缺失资源]
    B -->|5xx| G[服务端错误,延迟重试]
    C --> H{Content-Type}
    H -->|JSON| I[调用.json()]
    H -->|HTML| J[调用.text]
    H -->|Binary| K[调用.content]

这套逻辑不仅能提升成功率,还能让你的日志更有价值,方便后期排查问题。


🔐 模拟登录:破解微博的“第一道防线”

对于微博这类平台,大部分有价值的数据都藏在“登录之后”。所以我们必须掌握 模拟登录 技术。

但微博的登录流程可不简单,它采用了多步校验 + 密码加密机制。直接传明文密码?做梦 😅

第一步:预登录获取加密参数

微博使用RSA非对称加密保护密码传输安全。你需要先向预登录接口请求一些关键参数:

pre_login_url = "https://login.sina.com.cn/sso/prelogin.php"
params = {
    'entry': 'weibo',
    'callback': 'sinaSSOController.preloginCallBack',
    'su': base64.b64encode(username.encode()).decode(),
    'client': 'ssologin.js(v1.4.19)',
    '_': int(time.time() * 1000)
}

response = session.get(pre_login_url, params=params)
json_match = re.search(r'\((.*?)\)', response.text)
if json_match:
    pre_data = json.loads(json_match.group(1))  # 推荐用json而不是eval!
    servertime = pre_data['servertime']
    nonce = pre_data['nonce']
    pubkey = pre_data['pubkey']

拿到 servertime , nonce , pubkey 后,就可以进行密码加密了。

第二步:密码加密(RSA + Base64)

构造待加密字符串: password + '\t' + str(servertime) + '\n' + nonce

然后用RSA公钥加密,并Base64编码:

import rsa
import base64

def encrypt_password(password, pubkey, servertime, nonce):
    raw_str = f'{password}\t{servertime}\n{nonce}'
    n = int(pubkey, 16)
    e = 0x10001
    public_key = rsa.PublicKey(n, e)
    encrypted_data = rsa.encrypt(raw_str.encode('utf-8'), public_key)
    return base64.b64encode(encrypted_data).decode('utf-8')

最终拼成表单数据提交:

form_data = {
    "username": username,
    "sp": encrypt_password(password, pubkey, servertime, nonce),
    "servertime": servertime,
    "nonce": nonce,
    "rsakv": rsakv,
    "encoding": "UTF-8",
    "entry": "weibo",
    "from": "",
    "gateway": "1",
    "pagerefer": "",
    "vsnf": "1",
    "su": base64.b64encode(username.encode()).decode(),
    "service": "sso",
    "pcid": "",
    "door": ""  # 如果有验证码,填在这里
}

第三步:应对图形验证码

当系统检测到异常行为时,会弹出验证码。这时我们需要OCR技术来识别。

推荐组合: Pillow + pytesseract

from PIL import Image
import pytesseract
import cv2
import numpy as np

def preprocess_captcha(img_path):
    img = cv2.imread(img_path)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)
    kernel = np.ones((2,2), np.uint8)
    cleaned = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, kernel)
    return cleaned

def recognize_captcha(image_bytes):
    img = Image.open(io.BytesIO(image_bytes))
    processed = preprocess_captcha(img)
    text = pytesseract.image_to_string(
        processed,
        config='--psm 8 --oem 3 -c tessedit_char_whitelist=0123456789'
    )
    return text.strip()

不过Tesseract对复杂扭曲字体识别率一般,生产环境建议接入第三方打码平台(如若快、云打码)。

整个流程可以用一张图概括:

graph TD
    A[发起登录请求] --> B{是否返回验证码?}
    B -- 是 --> C[下载验证码图片]
    C --> D[图像预处理: 灰度+二值化]
    D --> E[调用Tesseract OCR识别]
    E --> F[输入识别结果并重试登录]
    F --> G{登录成功?}
    G -- 否 --> H[刷新验证码重新识别]
    G -- 是 --> I[进入主页面]
    B -- 否 --> I

🌀 动态内容抓取:绕过JavaScript渲染陷阱

现在越来越多网站采用前端框架(Vue/React)做SPA应用,初始HTML里啥都没有,全是JS生成的内容。

微博就是典型代表。你用 requests 直接请求主页,得到的是一堆 <script> 标签和占位符。真正的数据在哪?在XHR请求里!

方法一:追踪XHR/Ajax接口(首选)

打开DevTools → Network → XHR/Fetch,刷新页面,找返回JSON的请求。

比如热搜榜的真实接口是:

GET https://weibo.com/ajax/side/hotSearch

响应示例:

{
  "data": {
    "realtime": [
      {"word": "春晚节目单", "num": "1.2亿"},
      {"word": "AI律师靠谱吗", "num": "8900万"}
    ]
  }
}

复现起来非常简单:

headers = {
    'X-Requested-With': 'XMLHttpRequest',
    'Referer': 'https://s.weibo.com/',
    'User-Agent': 'Mozilla/5.0...'
}

ajax_url = "https://weibo.com/ajax/side/hotSearch"
response = session.get(ajax_url, headers=headers)
hot_search_data = response.json()

这种方法效率高、资源消耗低, 只要能找到API,就尽量走这条路 !

方法二:Selenium 模拟真实浏览器

有些页面没有暴露API,或者初始化逻辑太复杂,那就只能祭出大杀器: Selenium 。

安装:

pip install selenium
# 下载对应版本chromedriver

启动无头浏览器:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options

options = Options()
options.add_argument('--headless')
options.add_argument('--disable-gpu')
options.add_argument('--no-sandbox')

driver = webdriver.Chrome(options=options)
driver.get("https://weibo.com")

# 等待元素加载
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

wait = WebDriverWait(driver, 10)
username_input = wait.until(EC.presence_of_element_located((By.NAME, "username")))
username_input.send_keys("your_account")

常用等待条件:

  • EC.visibility_of_element_located :元素可见
  • EC.element_to_be_clickable :可点击
  • EC.url_changes :URL发生变化
  • EC.alert_is_present :弹窗出现

定位方式对比:

定位方式 语法 性能 推荐指数
ID By.ID, "loginName" ⭐⭐⭐⭐⭐ ✅ 优先
NAME By.NAME, "password" ⭐⭐⭐⭐ ✅
CLASS_NAME By.CLASS_NAME, "W_btn_a" ⭐⭐⭐ ⚠️ 易冲突
XPATH By.XPATH, '//div[@class="card"]/a' ⭐⭐ ❌ 尽量不用
CSS_SELECTOR By.CSS_SELECTOR, "form input[type=text]" ⭐⭐⭐⭐ ✅

虽然功能强大,但Selenium资源占用高、速度慢,建议仅作为备选方案。


🛡️ 反爬对抗:让你的爬虫“活得更久”

你以为写了登录就能一直跑下去?天真了 😅

微博有一整套反爬体系,包括:

  • IP封禁(高频访问)
  • 频率检测(固定间隔)
  • 行为指纹(鼠标轨迹、滚动节奏)

怎么办?逐个击破!

1. IP代理池:防止IP被封

建一个可用代理池,定期检测存活状态:

[
  {"ip": "123.45.67.89", "port": 8080, "type": "HTTP", "score": 5},
  {"ip": "23.45.67.89", "port": 3128, "type": "HTTPS", "score": 4}
]

使用代理:

proxies = {
    'http': 'http://123.45.67.89:8080',
    'https': 'https://123.45.67.89:8080'
}
requests.get(url, proxies=proxies, timeout=5)

可以写个守护进程定时清洗低分代理,保证质量。

2. 访问频率控制:模拟人类操作节奏

不要用 time.sleep(1) 这种机械间隔!容易被识别。

改用正态分布模拟真实用户行为:

import random
import time

def human_delay(base=1.5, variation=0.8):
    delay = max(0.5, random.normalvariate(base, variation))
    time.sleep(delay)

for url in urls:
    resp = session.get(url)
    parse(resp)
    human_delay()

失败时用指数退避:

for i in range(3):
    try:
        resp = requests.get(url, timeout=10)
        break
    except:
        time.sleep((2 ** i) + random.uniform(0, 1))

3. 行为指纹规避:模拟鼠标和滚动

高级反爬会分析你的操作模式。直线滚动?秒开页面?too robot!

加入自然滚动:

def human_like_scroll(driver, total=5000):
    current = 0
    while current < total:
        step = random.randint(50, 200)
        current += step
        driver.execute_script(f"window.scrollBy(0, {step});")
        time.sleep(random.uniform(0.1, 0.4))

未来还可以引入贝塞尔曲线生成平滑鼠标轨迹,彻底模仿真人操作。


💾 数据解析与存储:让信息真正为你所用

抓到了数据,下一步就是清洗、结构化、持久化。

HTML解析:BeautifulSoup vs lxml

特性 BeautifulSoup lxml + XPath
速度 慢 快(C底层)
内存 高 低
易用性 极高 中等
容错性 强 较弱
场景 调试 生产

推荐搭配使用:调试用BS,上线用lxml。

from lxml import etree

tree = etree.HTML(html)
contents = tree.xpath('//div[@class="card-feed"]//div[@class="content"]/text()')
times = tree.xpath('//div[@class="card-feed"]//span[@class="time"]/text()')

文本清洗:去除噪声

微博内容常含表情、@、#话题、链接等干扰项:

import re

def clean_weibo_text(text):
    text = re.sub(r'#.*?#', '', text)                    # 去除话题
    text = re.sub(r'@[\u4e00-\u9fa5\w]+', '', text)     # 去除@用户
    text = re.sub(r'https?://[^\s]+', '', text)         # 去除URL
    text = re.sub(r'\s+', ' ', text).strip()            # 统一空白
    return text

存储方案对比

方式 性能 扩展性 适用场景
CSV 低 无 临时导出
JSON 低 无 API对接
SQLite 中 单机 本地缓存
MySQL 高 强 分布式部署

生产环境强烈建议上数据库,便于查询、去重、监控。


⚙️ 性能优化与工程化:从小脚本到工业级系统

最后一步,把你的爬虫从“玩具”变成“武器”。

并发模型对比

方法 耗时(100页) CPU 并发上限
单线程 180s 低 1
多线程(5) 40s 中 ~20
异步IO(aiohttp) 25s 高 >100

推荐异步方案:

import aiohttp
import asyncio

async def fetch(session, url):
    async with session.get(url) as resp:
        return await resp.text()

async def main():
    async with aiohttp.ClientSession() as session:
        tasks = [fetch(session, url) for url in urls]
        return await asyncio.gather(*tasks)

asyncio.run(main())

使用 Scrapy 框架(大型项目首选)

Scrapy 提供完整生态:中间件、管道、调度器、日志系统。

class WeiboSpider(scrapy.Spider):
    name = 'weibo'
    start_urls = ['https://weibo.com/login']

    def parse(self, response):
        yield scrapy.FormRequest.from_response(
            response,
            formdata={'username': 'xxx', 'password': 'yyy'},
            callback=self.after_login
        )

    def after_login(self, response):
        yield scrapy.Request('https://weibo.com/u/123456', self.parse_profile)

集成自动重试、限速、Cookie管理,非常适合长期监控任务。

日志与CI/CD

别忘了加日志:

import logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s',
    handlers=[logging.FileHandler('crawler.log'), logging.StreamHandler()]
)

再配上Git + GitHub Actions做自动化测试:

name: Run Tests
on: [push]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up Python
        uses: actions/setup-python@v4
      - run: pip install -r requirements.txt
      - run: python -m unittest discover

最终形成闭环:

graph LR
A[采集] --> B[解析]
B --> C[存储]
C --> D[测试]
D --> E[部署]
E --> F[监控]
F --> A

🎯 结语:爬虫不仅是技术,更是艺术

看到这里,你应该已经明白: 一个好的爬虫,不仅仅是“能跑起来”的代码,而是一个具备自我修复、持续进化能力的数据引擎 。

它要懂网络协议,会伪装身份,能处理异常,还要适应变化。就像一位潜入敌后的特工,既要完成任务,又要全身而退。

而这一切的核心,是对系统的深刻理解 + 对细节的极致把控。

希望这篇文章能成为你通往“爬虫高手之路”的起点 🚀

如果你觉得有用,不妨点赞收藏,也欢迎分享给正在 struggling 的朋友 ❤️

有什么问题,留言区见~咱们一起把数据的力量握在手中!💪

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

简介:Python-weibospider是一个基于Python开发的微博数据爬取工具,旨在自动化采集用户信息、微博内容、点赞数、评论数等公开数据。项目利用requests发送网络请求,结合BeautifulSoup或lxml解析页面,并通过Selenium处理动态加载内容。为应对反爬机制,集成IP代理池、验证码识别与会话管理技术。支持多线程或异步加速抓取,数据可存储至CSV、JSON或数据库中。该项目涵盖Web爬虫核心技术,适合作为Python爬虫学习与实战的完整案例。


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

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐