1. 准备工作:理解微博相册爬取的核心逻辑

微博相册爬取本质上是通过模拟浏览器行为,向微博服务器发送请求并解析返回的数据。这里有几个关键点需要理解:

首先,微博的相册数据是通过AJAX接口动态加载的,这意味着我们无法直接从网页源代码中获取图片链接。你需要找到微博用于加载相册数据的API接口。经过实测,https://weibo.com/ajax/profile/getImageWall这个接口就是我们要找的。

其次,微博采用了分页加载机制。每页最多显示20张图片,通过since_id参数控制分页。这个机制类似于翻书:你读完一页(获取20张图片),系统会告诉你下一页的标记(since_id),然后你用这个标记去获取下一页内容。

最后,微博图片有特殊的存储规则。每张图片都有一个唯一的pid(图片ID),通过这个pid可以构造出实际的图片URL。常见的格式是https://wx1.sinaimg.cn/large/{pid}.jpg,其中wx1可能根据图片服务器不同而变化。

2. 环境搭建与依赖安装

在开始编码前,我们需要准备好Python环境。我推荐使用Python 3.8或以上版本,因为这个版本的兼容性最好,各种库的支持也最完善。

首先安装必要的依赖库:

pip install requests

requests库是Python中最常用的HTTP请求库,相比urllib更加简单易用。它可以帮助我们发送HTTP请求和处理响应。

为了验证安装是否成功,可以打开Python解释器输入:

import requests
print(requests.__version__)

如果没有报错并显示版本号,说明安装成功。

3. 获取微博用户UID的三种方法

UID是微博用户的唯一标识符,是爬取相册的必要参数。这里分享三种获取UID的方法:

第一种是通过网页端直接查看。打开目标用户的微博主页,查看网页地址栏。例如:https://weibo.com/u/123456789,其中的123456789就是UID。

第二种方法适用于手机端。在微博APP中打开用户主页,点击右上角的"..."按钮,选择"复制链接",你会得到一个类似https://m.weibo.cn/u/123456789的链接,其中的数字就是UID。

第三种方法是通过开发者工具。在网页端按F12打开开发者工具,切换到Network(网络)选项卡,刷新页面,在请求列表中找到包含用户信息的请求,通常在响应数据中能找到uid字段。

4. 构建爬虫核心代码

现在我们来构建爬虫的核心部分。首先需要设置请求头,这是模拟浏览器行为的关键:

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36',
    'Cookie': '你的微博cookie'
}

获取cookie的方法:登录微博网页版后,按F12打开开发者工具,在Application > Cookies中找到微博的cookie,复制整个字符串。

接下来是获取since_id的函数,这个参数用于分页控制:

def get_sinceid(url):
    resp_json = requests.get(url, headers=headers).json()
    return resp_json['data']['since_id']

然后是获取图片pid列表的函数:

def get_pids(url):
    resp_json = requests.get(url, headers=headers).json()
    return [d['pid'] for d in resp_json['data']['list']]

5. 图片下载与存储管理

获取到图片pid后,我们需要构造实际的图片URL并下载。这里有几个注意事项:

首先,微博图片有不同的大小规格,通过在pid前添加不同前缀实现。例如:

  • 缩略图:https://wx1.sinaimg.cn/thumb150/{pid}.jpg
  • 中等大小:https://wx1.sinaimg.cn/mw690/{pid}.jpg
  • 原图:https://wx1.sinaimg.cn/large/{pid}.jpg

建议下载原图,所以使用large前缀。

下载图片的函数如下:

def download_img(img_urls, save_dir='weibo_images'):
    if not os.path.exists(save_dir):
        os.makedirs(save_dir)
    
    for img_url in img_urls:
        try:
            response = requests.get(img_url, headers=headers, stream=True)
            if response.status_code == 200:
                filename = os.path.join(save_dir, img_url.split('/')[-1])
                with open(filename, 'wb') as f:
                    for chunk in response.iter_content(1024):
                        f.write(chunk)
                print(f'下载成功: {filename}')
            else:
                print(f'下载失败: {img_url}')
        except Exception as e:
            print(f'下载出错: {e}')

这个函数使用了流式下载,可以避免大文件占用过多内存。同时加入了异常处理,使程序更加健壮。

6. 完整爬虫实现与分页控制

现在我们把所有部分组合起来,实现完整的爬虫功能:

import requests
import os

def main():
    uid = input('请输入微博用户UID: ')
    pages = int(input('请输入要爬取的页数(每页最多20张): '))
    
    save_dir = f'weibo_images_{uid}'
    base_url = f'https://weibo.com/ajax/profile/getImageWall?uid={uid}&sinceid=0&has_album=true'
    
    since_id = 0
    for page in range(1, pages + 1):
        try:
            # 获取当前页的图片pid
            pids = get_pids(base_url)
            if not pids:
                print(f'第{page}页没有图片,停止爬取')
                break
                
            # 构造图片URL并下载
            img_urls = [f'https://wx1.sinaimg.cn/large/{pid}.jpg' for pid in pids]
            download_img(img_urls, save_dir)
            
            # 获取下一页的since_id
            since_id = get_sinceid(base_url)
            if since_id == 0:
                print('已到达最后一页,停止爬取')
                break
                
            # 更新base_url用于下一页
            base_url = f'https://weibo.com/ajax/profile/getImageWall?uid={uid}&sinceid={since_id}&has_album=true'
            print(f'第{page}页下载完成')
            
        except Exception as e:
            print(f'第{page}页处理出错: {e}')
            break

if __name__ == '__main__':
    main()

这个脚本会循环爬取指定页数的图片,直到达到指定页数或者没有更多图片为止。每次请求都会更新since_id,确保能获取到下一页的数据。

7. 常见问题与解决方案

在实际使用中,你可能会遇到以下问题:

  1. 请求被拒绝(403错误) 这通常是因为请求头不够完善或者cookie失效。解决方案:

    • 更新cookie
    • 添加更多请求头字段,如Referer
    • 添加请求延迟,避免频率过高
  2. 获取的图片数量少于预期 微博相册可能有隐私设置,部分图片需要登录才能查看。确保:

    • 使用有效的cookie
    • 检查目标相册是否为公开可见
  3. 下载的图片损坏 可能是网络问题导致下载不完整。可以:

    • 增加重试机制
    • 验证下载文件的完整性
  4. 被封IP 如果请求太频繁,可能会触发微博的反爬机制。建议:

    • 在请求间添加随机延迟
    • 使用代理IP(需谨慎合规使用)

8. 优化与扩展建议

如果你需要更强大的爬虫,可以考虑以下优化:

  1. 多线程下载 使用concurrent.futures模块实现并行下载,大幅提高效率:

    from concurrent.futures import ThreadPoolExecutor
    
    def download_all(img_urls, save_dir, max_workers=5):
        with ThreadPoolExecutor(max_workers=max_workers) as executor:
            executor.map(lambda url: download_img([url], save_dir), img_urls)
    
  2. 断点续爬 将已经下载的图片记录到文件,程序重启后可以继续未完成的部分。

  3. 图片去重 使用hash算法检查图片内容,避免重复下载相同图片。

  4. 元数据保存 除了图片本身,还可以保存图片的发布时间、描述等信息,方便后续分析。

  5. 图形界面 使用PyQt或Tkinter为脚本添加图形界面,使其更易用。

在实际项目中,我发现合理设置请求间隔(如3-5秒)和良好的异常处理是保证爬虫稳定运行的关键。另外,建议将配置信息(如cookie、保存路径等)单独放在配置文件中,方便维护。

Logo

码道开发者社区,聚焦华为云码道 CodeArts 代码智能体,沉淀 Agent、Skill、鸿蒙开发实战内容,供开发者查阅资料、交流技术、分享工程实践

更多推荐