ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Python爬虫实战:腾讯公益数据抓取与分析

Python爬虫实战:腾讯公益数据抓取与分析

1. 项目背景与价值解析

公益项目数据抓取这个需求源于我去年参与的一次志愿者活动。当时团队需要分析近三年国内主流平台的公益项目发展趋势,但发现腾讯公益这类平台虽然提供了网页展示,却没有开放完整的历史数据接口。手动记录上百个项目信息不仅耗时耗力,而且难以保证数据准确性。

这个Python爬虫项目正是为了解决这类问题而生。通过自动化抓取腾讯公益平台上的热门项目数据,我们能够获得包括项目名称、筹款目标、已筹金额、参与人数、发起机构等关键字段。这些结构化数据至少能带来三个层面的价值:

  1. 公益研究支持:学者或调研机构可以分析公众偏好的公益类型(教育助学、疾病救助、扶贫等)、不同季节的捐赠热度变化等趋势
  2. 项目优化参考:公益组织能对比同类项目的展示方式、筹款话术,优化自身项目设计
  3. 透明化监督:定期抓取数据可建立公益项目执行进度的历史档案,促进公益行业的透明度

重要提示:爬虫开发必须遵守Robots协议,腾讯公益的robots.txt文件明确允许对/public开头的路径进行爬取,这正是我们目标数据所在的目录。同时需要控制请求频率,建议设置为每3秒一次请求,避免对服务器造成压力。

2. 技术方案设计

2.1 目标数据定位

首先需要明确我们要获取哪些具体数据。通过分析腾讯公益官网,一个典型的公益项目卡片包含以下关键信息字段:

字段名HTML定位方式数据用途
项目标题div.title > a项目分类与关键词分析
筹款进度div.progress > span计算完成率与热门程度
目标金额div.target > em项目规模评估
已筹金额div.raised > em捐赠效果分析
捐赠人数div.donors > span公众参与度统计
项目封面图div.cover > img[src]宣传素材分析
项目链接a[href]后续深度爬取入口

2.2 技术栈选型

基于目标网站的现代前端架构(动态渲染、AJAX加载),我们采用以下技术组合:

  • 请求库:requests + requests_html
    • 普通页面用requests足够,但腾讯公益的部分数据通过滚动加载,需要requests_html处理动态内容
  • 解析工具:pyquery + parsel
    • pyquery的jQuery风格选择器更符合前端开发习惯
    • parsel支持XPath和CSS双模式,应对复杂嵌套结构
  • 存储方案:MongoDB
    • 公益项目数据存在字段变化可能(如新增"项目进展"板块)
    • 文档型数据库的schemaless特性更适配这种需求
# 典型依赖配置 requirements = [ 'requests>=2.26.0', 'requests-html>=0.10.0', 'pyquery>=1.4.3', 'pymongo>=4.0.1', 'fake-useragent>=0.1.11' ]

2.3 反爬应对策略

腾讯公益虽然没有严格的反爬机制,但仍需注意:

  1. 请求头伪装:轮换User-Agent,添加合理的Referer
    headers = { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36...', 'Referer': 'https://gongyi.qq.com/' }
  2. IP轮换:使用免费代理IP池(如github上的proxy-list)
    proxies = { 'http': 'http://110.243.5.102:9999', 'https': 'https://110.243.5.102:9999' }
  3. 请求间隔:随机延时2-5秒,避免触发频率限制
    time.sleep(random.uniform(2, 5))

3. 核心代码实现

3.1 页面抓取模块

腾讯公益的热门项目采用分页加载,通过分析发现其API接口规律:

https://gongyi.qq.com/succor/list.htm?page=2

实现分页抓取的要点:

def fetch_page(page_num): url = f'https://gongyi.qq.com/succor/list.htm?page={page_num}' try: session = HTMLSession() response = session.get(url, headers=headers, timeout=10) response.raise_for_status() # 渲染JavaScript执行后的内容 response.html.render(sleep=2) return response.html.html except Exception as e: print(f"Page {page_num} fetch failed: {str(e)}") return None

实测发现:直接请求HTML页面比调用内部API更稳定,虽然需要渲染JS会增加耗时,但避免了接口变动的维护成本。

3.2 数据解析模块

使用PyQuery提取关键数据的典型实现:

from pyquery import PyQuery as pq def parse_project(html): doc = pq(html) projects = [] for item in doc('.project-list .project-item').items(): project = { 'title': item.find('.title').text(), 'target': float(item.find('.target em').text()[1:].replace(',', '')), 'raised': float(item.find('.raised em').text()[1:].replace(',', '')), 'progress': item.find('.progress span').attr('style'), 'donors': int(item.find('.donors span').text()), 'cover': item.find('.cover img').attr('src'), 'url': 'https://gongyi.qq.com' + item.find('a').attr('href'), 'crawl_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S') } # 计算完成百分比 if project['target'] > 0: project['completion'] = round( project['raised'] / project['target'] * 100, 2 ) projects.append(project) return projects

3.3 数据存储模块

MongoDB存储的优化实践:

from pymongo import MongoClient from pymongo.errors import DuplicateKeyError class MongoDBPipeline: def __init__(self, mongo_uri, mongo_db): self.mongo_uri = mongo_uri self.mongo_db = mongo_db @classmethod def from_crawler(cls, crawler): return cls( mongo_uri='mongodb://localhost:27017', mongo_db='tencent_public_welfare' ) def process_item(self, item, spider): try: # 根据项目URL创建唯一索引避免重复 self.db['projects'].update_one( {'url': item['url']}, {'$set': dict(item)}, upsert=True ) except DuplicateKeyError: pass return item

4. 实战技巧与避坑指南

4.1 动态内容处理经验

腾讯公益在2023年改版后,部分数据改为滚动加载。我们通过Chrome开发者工具分析发现:

  1. 首屏数据在初始HTML中
  2. 后续数据通过/api/project/list接口加载
  3. 接口需要携带x-requested-with: XMLHttpRequest头

解决方案:

async def fetch_ajax(page): ajax_url = 'https://gongyi.qq.com/api/project/list' params = { 'page': page, 'size': 20, 'type': 'recommend' } async with aiohttp.ClientSession() as session: async with session.get(ajax_url, params=params, headers={'x-requested-with': 'XMLHttpRequest'}) as resp: return await resp.json()

4.2 数据清洗要点

原始数据需要特别注意:

  • 金额字段包含"¥"符号和逗号分隔(如"¥1,234.56")
  • 进度条是CSS宽度(如"width: 78.23%;")
  • 部分项目可能有"已结项"特殊标记

清洗函数示例:

def clean_data(project): # 处理金额 project['raised'] = float( project['raised'].replace('¥', '').replace(',', '') ) # 提取进度百分比 if 'progress' in project: match = re.search(r'width:\s*([\d.]+)%', project['progress']) if match: project['progress_pct'] = float(match.group(1)) # 标记特殊状态 project['is_ended'] = '已结项' in project.get('status', '') return project

4.3 定时任务优化

建议使用APScheduler实现定时抓取:

from apscheduler.schedulers.blocking import BlockingScheduler scheduler = BlockingScheduler() @scheduler.scheduled_job('cron', hour=3, minute=30) def daily_crawl(): for page in range(1, 6): # 抓取前5页 html = fetch_page(page) projects = parse_project(html) save_to_mongo(projects) scheduler.start()

5. 数据分析应用示例

获取数据只是第一步,如何利用这些数据创造价值才是关键。以下是两个实际应用场景:

5.1 公益类型热度分析

import pandas as pd from collections import Counter # 从MongoDB加载数据 projects = list(db.projects.find()) # 提取标题关键词 keywords = [] for p in projects: if '助学' in p['title']: keywords.append('教育助学') elif '医疗' in p['title'] or '疾病' in p['title']: keywords.append('医疗救助') elif '乡村' in p['title'] or '扶贫' in p['title']: keywords.append('扶贫发展') # 统计分布 pd.DataFrame.from_dict( Counter(keywords), orient='index', columns=['count'] ).sort_values('count').plot.barh()

5.2 捐赠效果影响因素分析

使用Scikit-learn分析哪些因素影响筹款完成率:

from sklearn.ensemble import RandomForestRegressor # 准备特征矩阵 X = pd.DataFrame([{ 'target_amount': p['target'], 'donors': p['donors'], 'has_cover': int(bool(p['cover'])), 'title_len': len(p['title']) } for p in projects]) # 目标变量 y = [p['completion'] for p in projects] # 训练模型 model = RandomForestRegressor() model.fit(X, y) # 特征重要性 pd.Series(model.feature_importances_, index=X.columns).sort_values()

6. 法律合规与道德考量

虽然技术上是可行的,但爬取公益数据需要特别注意:

  1. 数据使用范围:仅用于分析研究,不得用于商业用途
  2. 隐私保护:绝对不抓取捐赠人个人信息
  3. 数据公开:如果分享分析结果,需去除敏感字段
  4. 服务器负载:严格遵守爬虫间隔时间,避免影响正常用户访问

建议在代码开头添加如下声明:

""" 腾讯公益数据爬虫 - 使用声明 1. 本工具仅用于技术学习与研究目的 2. 数据抓取频率控制在每分钟不超过20次请求 3. 不存储、不传播任何捐赠人个人信息 4. 分析结果如公开需注明数据来源 """

这个项目让我深刻体会到,技术可以成为公益事业的加速器。通过合理使用爬虫技术,我们能够帮助公益组织更高效地获取行业洞察,最终让每一份爱心都能更精准地传递到需要的地方。后续可以考虑增加自动生成公益项目分析报告的功能,用数据可视化让公益效果更透明可见。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进