Python 爬虫实践:如何构建简书用户画像

最近用 Python 写了一个简书用户的爬虫,单机耗时 30 小时抓取了简书 30 万用户的数据(设置了适当的下载延迟,所以耗时较长。当然了,主要是避免简书服务器造成不必要的压力)。简书用户用户量我不知有多少,这 30 万只是其中相对活跃的一小部分,但个人认为这份数据还是具有一定的代表性

http://mp.weixin.qq.com/s/N_nomh6aHYIUS1FRFC067Q


Python 爬虫实践:如何构建简书用户画像

16 回复

玩 python 多久了,看着还挺有意思的嘛


我理解你想用Python爬取简书数据来分析用户画像。这个需求需要先获取用户数据,然后进行分析。下面是一个完整的爬虫示例,包含数据采集和基础分析:

import requests
import json
import pandas as pd
from datetime import datetime
import time
import random

class JianShuSpider:
    def __init__(self):
        self.headers = {
            'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
            'Referer': 'https://www.jianshu.com/'
        }
        self.session = requests.Session()
    
    def get_user_info(self, user_id):
        """获取用户基本信息"""
        url = f'https://www.jianshu.com/u/{user_id}'
        try:
            response = self.session.get(url, headers=self.headers, timeout=10)
            # 这里需要解析HTML页面,实际项目中建议使用BeautifulSoup
            # 简书页面结构可能变化,以下为示例解析逻辑
            user_data = {
                'user_id': user_id,
                'nickname': self._extract_nickname(response.text),
                'followers': self._extract_followers(response.text),
                'following': self._extract_following(response.text),
                'articles': self._extract_article_count(response.text),
                'words': self._extract_total_words(response.text),
                'likes': self._extract_likes(response.text)
            }
            return user_data
        except Exception as e:
            print(f"获取用户{user_id}信息失败: {e}")
            return None
    
    def get_user_articles(self, user_id, limit=20):
        """获取用户文章列表"""
        articles = []
        page = 1
        
        while len(articles) < limit:
            url = f'https://www.jianshu.com/u/{user_id}?order_by=shared_at&page={page}'
            try:
                response = self.session.get(url, headers=self.headers)
                # 解析文章列表
                page_articles = self._parse_articles(response.text)
                if not page_articles:
                    break
                articles.extend(page_articles)
                page += 1
                time.sleep(random.uniform(1, 2))  # 礼貌性延迟
            except Exception as e:
                print(f"获取文章列表失败: {e}")
                break
        
        return articles[:limit]
    
    def _extract_nickname(self, html):
        """从HTML中提取昵称(示例方法)"""
        # 实际需要根据页面结构编写解析逻辑
        import re
        match = re.search(r'<title>(.*?) - 简书</title>', html)
        return match.group(1) if match else ''
    
    def _extract_followers(self, html):
        """提取粉丝数"""
        import re
        match = re.search(r'粉丝.*?(\d+)', html)
        return int(match.group(1)) if match else 0
    
    # 其他提取方法类似,需要根据实际页面结构编写
    
    def _parse_articles(self, html):
        """解析文章列表"""
        articles = []
        # 这里需要根据实际HTML结构编写解析代码
        # 示例:使用BeautifulSoup解析
        from bs4 import BeautifulSoup
        soup = BeautifulSoup(html, 'html.parser')
        
        # 假设文章在特定的div中
        article_elements = soup.find_all('div', class_='content')  # 类名需要实际查看
        
        for elem in article_elements:
            article = {
                'title': elem.find('a', class_='title').text if elem.find('a', class_='title') else '',
                'publish_time': elem.find('time')['datetime'] if elem.find('time') else '',
                'read_count': self._extract_number(elem.find('span', class_='read-count')),
                'like_count': self._extract_number(elem.find('span', class_='like-count')),
                'comment_count': self._extract_number(elem.find('span', class_='comment-count'))
            }
            articles.append(article)
        
        return articles
    
    def _extract_number(self, element):
        """从元素中提取数字"""
        if element and element.text:
            import re
            match = re.search(r'\d+', element.text)
            return int(match.group()) if match else 0
        return 0

class UserProfileAnalyzer:
    """用户画像分析器"""
    
    def __init__(self, user_data, articles_data):
        self.user_data = user_data
        self.articles_data = articles_data
    
    def generate_profile(self):
        """生成用户画像"""
        profile = {
            '基础信息': {
                '用户ID': self.user_data.get('user_id'),
                '昵称': self.user_data.get('nickname'),
                '粉丝数': self.user_data.get('followers'),
                '关注数': self.user_data.get('following')
            },
            '创作能力': {
                '文章总数': self.user_data.get('articles'),
                '总字数': self.user_data.get('words'),
                '平均阅读量': self._calculate_avg_reads(),
                '平均点赞数': self._calculate_avg_likes()
            },
            '活跃度': {
                '最近发文时间': self._get_latest_article_time(),
                '发文频率': self._calculate_post_frequency(),
                '互动率': self._calculate_engagement_rate()
            },
            '内容偏好': {
                '热门标签': self._extract_top_tags(),
                '平均文章长度': self._calculate_avg_article_length()
            }
        }
        return profile
    
    def _calculate_avg_reads(self):
        """计算平均阅读量"""
        if not self.articles_data:
            return 0
        reads = [article.get('read_count', 0) for article in self.articles_data]
        return sum(reads) / len(reads)
    
    def _calculate_avg_likes(self):
        """计算平均点赞数"""
        if not self.articles_data:
            return 0
        likes = [article.get('like_count', 0) for article in self.articles_data]
        return sum(likes) / len(likes)
    
    def _get_latest_article_time(self):
        """获取最新文章时间"""
        if not self.articles_data:
            return None
        times = [article.get('publish_time') for article in self.articles_data if article.get('publish_time')]
        return max(times) if times else None
    
    def _calculate_post_frequency(self):
        """计算发文频率"""
        if len(self.articles_data) < 2:
            return '数据不足'
        
        # 计算时间间隔
        times = [datetime.fromisoformat(t.replace('Z', '+00:00')) 
                for t in [a.get('publish_time') for a in self.articles_data if a.get('publish_time')]]
        
        if len(times) < 2:
            return '数据不足'
        
        time_diff = (max(times) - min(times)).days
        if time_diff == 0:
            return '高频发文'
        
        frequency = len(times) / time_diff
        if frequency > 1:
            return f'每日{frequency:.1f}篇'
        elif frequency > 0.1:
            return f'每{1/frequency:.0f}天1篇'
        else:
            return '低频发文'
    
    def _calculate_engagement_rate(self):
        """计算互动率(点赞/阅读)"""
        avg_reads = self._calculate_avg_reads()
        avg_likes = self._calculate_avg_likes()
        
        if avg_reads == 0:
            return 0
        return (avg_likes / avg_reads) * 100
    
    def _extract_top_tags(self):
        """提取热门标签(需要文章内容分析)"""
        # 这里需要实际的文章内容来分析标签
        # 示例返回
        return ['Python', '爬虫', '数据分析']
    
    def _calculate_avg_article_length(self):
        """计算平均文章长度"""
        # 需要获取文章正文内容
        return '需要正文内容分析'

# 使用示例
def main():
    # 初始化爬虫
    spider = JianShuSpider()
    
    # 获取用户数据(示例用户ID)
    user_id = "example_user_id"
    
    print("正在爬取用户数据...")
    user_info = spider.get_user_info(user_id)
    
    if user_info:
        print("正在爬取文章数据...")
        articles = spider.get_user_articles(user_id, limit=10)
        
        # 分析用户画像
        analyzer = UserProfileAnalyzer(user_info, articles)
        profile = analyzer.generate_profile()
        
        # 输出结果
        print("\n=== 用户画像分析结果 ===")
        for category, data in profile.items():
            print(f"\n{category}:")
            for key, value in data.items():
                print(f"  {key}: {value}")
        
        # 保存数据
        with open(f'user_profile_{user_id}.json', 'w', encoding='utf-8') as f:
            json.dump(profile, f, ensure_ascii=False, indent=2)
        print(f"\n数据已保存到 user_profile_{user_id}.json")
    else:
        print("无法获取用户数据")

if __name__ == "__main__":
    main()

这个爬虫框架包含了:

  1. 数据采集层JianShuSpider类负责爬取用户基本信息和文章列表
  2. 数据分析层UserProfileAnalyzer类对采集的数据进行分析,生成用户画像
  3. 画像维度:包括基础信息、创作能力、活跃度、内容偏好等

需要注意的几点

  • 简书页面结构可能变化,需要根据实际情况调整解析逻辑
  • 需要处理反爬机制,可以添加代理、更复杂的请求头等
  • 获取文章正文需要额外的请求和解析
  • 用户画像可以进一步扩展:情感分析、主题建模、社交网络分析等

建议:先小规模测试,确保爬虫稳定后再扩大采集范围。

这个应该算是用户统计,还谈不上画像。

但是能爬了这么多数据也挺有意思的……

我现在想抓微博的网红和模特和 COSER 然后抓他们发的套图。 你们有没有思路?

然后做一个瀑布流网站。

有没有思路

有点意思

努力成长的菜鸟一枚

第一步获取数据( github 有开源的微博爬虫),第二步用 web 框架( django、flask )搭建一个网站将抓的数据展示出来。

就是没有思路怎么整理数据(如何知道他是女模特或者 COSER )

只抓指定的几个人的 feed,或者关联的就再抓上他关注的人的 feed。然后拉回本地分析一下图。

画图用的是什么?

我目前的思路也是先抓 1 人再抓剩下的人。这种思路。

同问怎么画图的

回到顶部