1637加盟网数据实战(数分可视化)

一、项目需求分析

大家好!今天我们来实战一个加盟品牌信息爬虫 项目 目标网站是1637加盟网(www.1637.com),这是一个提供各种品牌加盟信息的平台 通过这个项目,你将掌握如何:

爬取目标:

  1. 多页面翻页爬取(1-10页数据)

  2. 结构化信息提取(品牌名称、分类、公司、发布时间等8个字段)

  3. 复杂文本处理(使用正则表达式提取特定信息)

  4. 数据分页保存(每页保存为独立的Excel文件)

技术要点:

  • XPath选择器使用技巧

  • 正则表达式匹配提取

  • 反爬虫策略应对

  • 数据存储与组织

二、网页分析与目标确认

首先我们需要分析目标网站的页面结构,确定要爬取哪些信息

2.1 网站页面结构分析

通过浏览器开发者工具(F12)分析,我们发现:

  • 每个品牌信息都在 <li class="brandItem white"> 标签中

  • 品牌名称在 //dl[@class="dl2 fl"]/h4/a/text() 路径下

  • 详细信息需要从 //dd[@class="fr"] 中提取

2.2 确定要爬取的数据字段

我们需要提取以下8个关键信息:

三、爬虫代码实战

OK,分析完页面结构后,我们开始写代码。我会一步一步带你完成这个爬虫

3.1 环境准备与导入库

python 复制代码
import os.path
import re
import pandas as pd
import requests
from lxml import etree

库说明:

  • os.path:用于检查文件夹是否存在,创建保存目录

  • re:正则表达式,用于提取复杂文本信息

  • pandas:数据处理,保存为Excel格式

  • requests:发送HTTP请求,获取网页内容

  • lxml:解析HTML,使用XPath提取数据

3.2 创建保存目录

在开始爬取前,我们先创建一个文件夹来保存数据:

python 复制代码
# 检查'project'文件夹是否存在,如果不存在就创建
if not os.path.exists('project'):
    os.makedirs('project')
print("✅ 保存目录创建成功:project/")

这样做的目的是把爬取的数据统一放在一个文件夹里,方便管理

3.3 分析请求头(反爬虫策略)

网站一般会有反爬虫机制,我们需要模拟浏览器发送请求 查看浏览器Network面板,复制完整的请求头

python 复制代码
headers = {
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
    "accept-encoding": "gzip, deflate",
    "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
    "cache-control": "no-cache",
    "cookie": "PHPSESSID=o5tlqfe0rs855r6a4fvandktt3; Hm_lvt_cdd780d3637898d7f0a0e5c236ab2fcc=1768797176; HMACCOUNT=F3D34F6543BC6B34; ELU_PHPSE_INPUT=1; Hm_lpvt_cdd780d3637898d7f0a0e5c236ab2fcc=1768797193",
    "host": "www.1637.com",
    "pragma": "no-cache",
    "proxy-connection": "keep-alive",
    "referer": "http://www.1637.com/",
    "upgrade-insecure-requests": "1",
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0"
}

关键字段说明:

  • User-Agent:告诉服务器我们使用的是Chrome浏览器

  • Referer:表示请求来自哪个页面,有些网站会检查这个

  • Cookie:维持登录状态,有些数据需要登录才能访问

  • Accept-Encoding:告诉服务器我们可以接受压缩的数据,节省流量

3.4 主爬取循环(多页爬取)

我们要爬取第1页到第10页的数据,观察URL规律

第1页:http://www.1637.com/xm/p1.html

第2页:http://www.1637.com/xm/p2.html

第3页:http://www.1637.com/xm/p3.html

...

第10页:http://www.1637.com/xm/p10.html

可以看出,页码就在URL的 p{page}.html 部分 我们使用for循环来实现

python 复制代码
for page in range(1, 11):  # range(1, 11) 生成 1-10 的数字
    # 构建每一页的URL
    url = f'http://www.1637.com/xm/p{page}.html'
    print(f"🚀 开始爬取第 {page} 页:{url}")
    
    # 发送GET请求,带上我们准备好的请求头
    resp = requests.get(url, headers=headers)
    
    # 检查请求是否成功(状态码200表示成功)
    if resp.status_code == 200:
        print(f"✅ 第 {page} 页请求成功")
    else:
        print(f"❌ 第 {page} 页请求失败,状态码:{resp.status_code}")
        continue  # 跳过这一页,继续下一页

3.5 解析网页内容(XPath使用)

获取到网页HTML后,我们需要用lxml来解析它

python 复制代码
# 将响应文本转换为lxml可以解析的格式
html = etree.HTML(resp.text)

# 使用XPath找到所有品牌信息的li标签
lis = html.xpath('.//li[@class="brandItem white"]')

print(f"📊 第 {page} 页找到 {len(lis)} 个品牌")

XPath语法说明:

  • .//li[@class="brandItem white"]:查找所有class属性为"brandItem white"的li标签

  • // 表示从任意位置开始查找

  • [@class="brandItem white"] 是属性条件

3.6 提取单个品牌信息

现在我们进入最核心的部分------从每个li标签中提取我们需要的数据:

python 复制代码
# 创建一个空列表,用于保存本页的所有品牌数据
data = []

# 遍历每个li标签(每个品牌)
for li in lis:
    # 1. 提取品牌名称
    name = ''.join(li.xpath('.//dl[@class="dl2 fl"]/h4/a/text()'))
    
    # 2. 提取品牌分类(可能有多个分类,用|连接)
    category = '|'.join(li.xpath('.//a[@target="_self"]//text()'))
    
    # 3. 提取所属公司
    company = ''.join(li.xpath('.//dt[@class="fl"]/p[3]/a/span/text()'))
    
    # 4. 提取发布时间
    publish_time = ''.join(li.xpath('.//dt[@class="fl"]/p[4]/span/text()'))

XPath技巧说明:

  • ''.join(...):将提取的文本列表合并成一个字符串

  • '|'.join(...):用竖线连接多个分类,方便后续分析

  • //text():获取元素下的所有文本,包括子元素的文本

3.7 使用正则表达式提取详细信息

这里有个难点:投资金额、加盟指数等信息都在一个复杂的文本块中,我们需要用正则表达式来提取:

python 复制代码
  # 5. 获取详细信息文本块
    info_text = li.xpath('string(.//dd[@class="fr"])')
    
    # 清理文本:去掉换行符和多余空格
    info_text = info_text.replace('\n', '').replace(' ', '')
    
    # 使用正则表达式提取关键信息
    pattern = r'投资金额:(?P<price>.*?)加盟指数:(?P<join_number>.*?)咨询人数:(?P<pople>.*?)经营产品:(?P<product>.*?)'
    obj = re.compile(pattern)
    
    # 查找所有匹配项
    matches = obj.finditer(info_text)
    
    # 遍历匹配结果(实际上每个品牌只有一个匹配)
    for match in matches:
        invest_price = match.group('price')
        join_number = match.group('join_number')
        pople = match.group('pople')
        product = match.group('product')

正则表达式详细解释:

python 复制代码
r'投资金额:(?P<price>.*?)加盟指数:(?P<join_number>.*?)咨询人数:(?P<pople>.*?)经营产品:(?P<product>.*)'
(?P<price>.*?):命名捕获组,匹配"投资金额:"后面到"加盟指数:"之前的所有内容

.*?:非贪婪匹配,匹配任意字符,尽可能少匹配

这样我们可以一次性提取出4个字段的信息

3.8 组织数据并保存

提取完所有信息后,我们把它组织成字典格式,方便后续保存

python 复制代码
        # 创建数据字典
        brand_data = {
            'name': name,
            'category': category,
            'company': company,
            'publish_time': publish_time,
            'invest_price': invest_price,
            'join_number': join_number,
            'pople': pople,
            'product': product
        }
        
        # 添加到数据列表
        data.append(brand_data)

# 将本页数据保存为Excel文件
df = pd.DataFrame(data)
output_file = f'project/page_{page}.xlsx'
df.to_excel(output_file, index=False)

print(f"💾 第 {page} 页数据已保存:{output_file},共 {len(data)} 条记录")

保存策略说明:

  • 每页保存为一个独立的Excel文件

  • 文件名格式:page_1.xlsxpage_2.xlsx...

  • 不保存索引列(index=False

  • 统一保存在project文件夹中

  • 保存至 csv 以及mysql 如下

python 复制代码
import csv
csv_writer = csv.DictWriter(open(f'project/p_{page}.csv', 'w', encoding='utf-8-sig', newline=''), fieldnames=[
        'name',
        'category',
        'company',
        'publish_time',
        'invest_price',
        'join_number',
        'pople',
        'product',
    ])
csv_writer.writeheader()

        csv_writer.writerow(dit)

数据库:

python 复制代码
connection = pymysql.connect(
    user='root',
    password="",
    host='localhost',
    database='spider',
)
cursor = connection.cursor()

    sql = 'insert into project values (%s,%s,%s,%s,%s,%s,%s,%s)'
            cursor.execute(sql, tuple(dit.values()))
            connection.commit()
cursor.close()
connection.close()

四、完整代码整合

下面是完整的爬虫代码,你可以直接复制运行:

python 复制代码
import os.path
import re
import pandas as pd
import requests
from lxml import etree

# 1. 创建保存目录
if not os.path.exists('project'):
    os.makedirs('project')

# 2. 请求头设置(模拟浏览器)
headers = {
    "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
    "accept-encoding": "gzip, deflate",
    "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
    "cache-control": "no-cache",
    "cookie": "PHPSESSID=o5tlqfe0rs855r6a4fvandktt3; Hm_lvt_cdd780d3637898d7f0a0e5c236ab2fcc=1768797176; HMACCOUNT=F3D34F6543BC6B34; ELU_PHPSE_INPUT=1; Hm_lpvt_cdd780d3637898d7f0a0e5c236ab2fcc=1768797193",
    "host": "www.1637.com",
    "pragma": "no-cache",
    "proxy-connection": "keep-alive",
    "referer": "http://www.1637.com/",
    "upgrade-insecure-requests": "1",
    "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0"
}

# 3. 主爬取循环(1-10页)
for page in range(1, 11):
    # 构建URL
    url = f'http://www.1637.com/xm/p{page}.html'
    print(f"\n{'='*50}")
    print(f"🚀 开始爬取第 {page} 页:{url}")
    
    # 发送请求
    resp = requests.get(url, headers=headers)
    
    # 检查响应状态
    if resp.status_code != 200:
        print(f"❌ 请求失败,状态码:{resp.status_code}")
        continue
    
    # 解析HTML
    html = etree.HTML(resp.text)
    
    # 查找所有品牌项目
    lis = html.xpath('.//li[@class="brandItem white"]')
    print(f"📊 找到 {len(lis)} 个品牌")
    
    # 数据存储列表
    data = []
    
    # 4. 提取每个品牌的信息
    for li in lis:
        # 提取基础信息
        name = ''.join(li.xpath('.//dl[@class="dl2 fl"]/h4/a/text()'))
        category = '|'.join(li.xpath('.//a[@target="_self"]//text()'))
        company = ''.join(li.xpath('.//dt[@class="fl"]/p[3]/a/span/text()'))
        publish_time = ''.join(li.xpath('.//dt[@class="fl"]/p[4]/span/text()'))
        
        # 提取详细信息(使用正则表达式)
        info = li.xpath('string(.//dd[@class="fr"])')
        info = info.replace('\n', '').replace(' ', '')
        
        # 正则匹配
        pattern = r'投资金额:(?P<price>.*?)加盟指数:(?P<join_number>.*?)咨询人数:(?P<pople>.*?)经营产品:(?P<product>.*)'
        obj = re.compile(pattern)
        matches = obj.finditer(info)
        
        for match in matches:
            # 组织数据字典
            brand_dict = {
                'name': name,
                'category': category,
                'company': company,
                'publish_time': publish_time,
                'invest_price': match.group('price'),
                'join_number': match.group('join_number'),
                'pople': match.group('pople'),
                'product': match.group('product')
            }
            data.append(brand_dict)
    
    # 5. 保存为Excel
    if data:  # 只有当有数据时才保存
        df = pd.DataFrame(data)
        filename = f'project/page_{page}.xlsx'
        df.to_excel(filename, index=False)
        print(f"💾 数据已保存:{filename},共 {len(data)} 条记录")
    else:
        print(f"⚠️ 第 {page} 页没有提取到数据")

print("\n" + "="*50)
print("✅ 所有页面爬取完成!")
print(f"📁 数据保存在 'project' 文件夹中")

五、运行结果展示

运行上面的代码,你会看到如下输出:

==================================================

🚀 开始爬取第 1 页:http://www.1637.com/xm/p1.html

📊 找到 20 个品牌

💾 数据已保存:project/page_1.xlsx,共 20 条记录

==================================================

🚀 开始爬取第 2 页:http://www.1637.com/xm/p2.html

📊 找到 20 个品牌

💾 数据已保存:project/page_2.xlsx,共 20 条记录

...(中间省略)...

==================================================

🚀 开始爬取第 10 页:http://www.1637.com/xm/p10.html

📊 找到 20 个品牌

💾 数据已保存:project/page_10.xlsx,共 20 条记录

==================================================

✅ 所有页面爬取完成!

📁 数据保存在 'project' 文件夹中

打开其中一个Excel文件,你会看到这样的数据结构:


六.数据清洗

6.1 环境准备与数据加载

python 复制代码
import re
import numpy as np
import pandas as pd

# 设置Pandas显示选项,便于查看完整数据
pd.set_option('display.max_columns', None)  # 显示所有列
pd.set_option('display.width', 1000)        # 设置显示宽度为1000字符

# 加载原始数据
df = pd.read_excel('./project/page_all.xlsx')
print(f"数据形状: {df.shape}")
print(f"数据预览:\n{df.head()}")

6.2 缺失值处理

虽然本次数据中所有字段都没有缺失值,但在实际项目中,缺失值处理是数据清洗的重要步骤:

python 复制代码
# 检查各字段缺失情况
missing_info = df.isna().sum()
print("缺失值统计:")
print(missing_info)

# 缺失值处理策略:
# 1. 数值字段:用均值、中位数填充或删除
# 2. 分类字段:用众数填充或创建"未知"类别
# 3. 文本字段:填充为空字符串或删除

6.3 发布时间处理

原始发布时间字段格式不一,需要提取数字部分并转换为标准时间格式

python 复制代码
def deal_publish_time(p_str):
    """
    处理发布时间,提取数字部分
    
    参数:
        p_str: 原始发布时间字符串
        
    返回:
        提取的数字字符串或NaN
    """
    if pd.isna(p_str):
        return np.nan  # 如果是缺失值,返回NaN
    
    # 使用正则表达式提取所有数字
    result = re.findall(r'\d+', str(p_str))
    
    if result:
        return result[0]  # 返回第一个匹配的数字串
    else:
        return np.nan  # 如果没有找到数字,返回NaN

# 应用处理函数
df['publish_time'] = df['publish_time'].apply(deal_publish_time)
print("发布时间处理示例:")
print(df['publish_time'].head())

6.4 投资价格处理

投资价格字段包含范围值(如"5-10万")和单个数值,需要统一处理:

python 复制代码
def deal_invest_price(i_str):
    """
    处理投资价格,支持范围值(取平均值)和单个数值
    
    参数:
        i_str: 原始投资价格字符串
        
    返回:
        处理后的数值或NaN
    """
    if pd.isna(i_str):
        return np.nan
    
    # 处理范围值(使用~~分隔)
    if '~~' in i_str:
        # 提取两个数值
        result = re.findall(r'(\d+)~~(\d+)', str(i_str))
        if result:
            # 计算平均值
            return (int(result[0][0]) + int(result[0][1])) / 2
    else:
        # 提取单个数值
        result = re.findall(r'(\d+)', str(i_str))
        if result:
            return result[0]
    
    return np.nan  # 如果都不匹配,返回NaN

# 应用处理函数
df['invest_price'] = df['invest_price'].apply(deal_invest_price)
print("投资价格处理示例:")
print(df['invest_price'].head())

6.5 人数字段处理

人数字段可能包含非数字字符,需要提取纯数字部分:

python 复制代码
def deal_people(p_str):
    """
    处理人数字段,提取数字部分
    
    参数:
        p_str: 原始人数字符串
        
    返回:
        提取的数字字符串或NaN
    """
    if pd.isna(p_str):
        return np.nan
    
    # 提取所有数字
    res = re.findall(r'(\d+)', str(p_str))
    
    if res:
        return res[0]  # 返回第一个匹配的数字
    else:
        return np.nan

# 应用处理函数
df['pople'] = df['pople'].apply(deal_people)
print("人数处理示例:")
print(df['pople'].head())

6.6 数据类型转换与特征提取

python 复制代码
# 数据类型转换
df['pople'] = df['pople'].astype(int)  # 转换为整数
df['invest_price'] = df['invest_price'].astype(float)  # 转换为浮点数

# 转换发布时间为datetime格式
# format='mixed'允许Pandas自动推断格式,errors='coerce'将错误值转为NaT
df['publish_time'] = pd.to_datetime(df['publish_time'], format='mixed', errors='coerce')

# 提取时间特征
df['year'] = df['publish_time'].dt.year  # 年份
df['month'] = df['publish_time'].dt.month  # 月份

print("时间特征提取示例:")
print(df[['publish_time', 'year', 'month']].head())

6.7 保存清洗后的数据

python 复制代码
# 保存清洗后的数据,不保存索引列
df.to_excel('./project/cleaned_page_all.xlsx', index=False)
print("清洗后的数据已保存到: ./project/cleaned_page_all.xlsx")
print(f"最终数据形状: {df.shape}")

七.数据可视化

7.1 环境准备

python 复制代码
import re
from collections import Counter
import jieba
import pandas as pd
from pyecharts.charts import *
from pyecharts import options as opts
import warnings

# 忽略警告信息
warnings.filterwarnings('ignore')

# 设置Pandas显示选项
pd.set_option('display.max_columns', None)
pd.set_option('display.width', 1000)

# 加载清洗后的数据
df = pd.read_excel('./project/cleaned_page_all.xlsx')
print(f"可视化数据形状: {df.shape}")

7.2 行业类别分布分析

分析项目在不同行业类别中的分布情况,识别热门行业:

python 复制代码
# 统计各行业类别的项目数量
cate_level = df['category'].value_counts()
print("行业类别分布Top10:")
print(cate_level.head(10))

# 创建柱状图
bar = (
    Bar()
    .add_xaxis(cate_level.index.tolist())  # X轴:行业类别
    .add_yaxis('Category', cate_level.values.tolist(),  # Y轴:项目数量
               label_opts=opts.LabelOpts(is_show=False),  # 不显示柱顶标签
               category_gap='30%')  # 柱间距30%
    .set_global_opts(
        legend_opts=opts.LegendOpts(is_show=True),  # 显示图例
        visualmap_opts=opts.VisualMapOpts(is_show=True),  # 显示视觉映射
        datazoom_opts=opts.DataZoomOpts(is_show=True, type_='inside'),  # 内置缩放
        toolbox_opts=opts.ToolboxOpts(is_show=True),  # 显示工具箱
        xaxis_opts=opts.AxisOpts(type_='category', boundary_gap=True),  # X轴配置
        tooltip_opts=opts.TooltipOpts(is_show=True, trigger='axis', axis_pointer_type='shadow'),  # 提示框
        yaxis_opts=opts.AxisOpts(type_='value', boundary_gap=True),  # Y轴配置
        title_opts=opts.TitleOpts(
            title='行业类别分布柱状图', 
            subtitle='项目数量分布情况',
            title_textstyle_opts=opts.TextStyleOpts(font_weight='bold', font_size=20)
        )
    )
    .set_series_opts(
        # 标记最大值和最小值
        markpoint_opts=opts.MarkPointOpts(
            data=[opts.MarkPointItem(type_='max'), opts.MarkPointItem(type_='min')]
        ),
        # 标记平均值线
        markline_opts=opts.MarkLineOpts(
            data=[opts.MarkLineItem(type_='average')]
        )
    )
)

7.3 行业类别饼图分析

python 复制代码
# 准备饼图数据(取前10个类别)
data = [(i, j) for i, j in zip(cate_level.index.tolist(), cate_level.values.tolist())][:10]

pie = (
    Pie(init_opts=opts.InitOpts(
        width='1200px', 
        height='800px', 
        page_title='饼图', 
        renderer='svg', 
        theme='white'
    ))
    .add('Category', 
         data_pair=data,  # 数据对
         center=['50%', '55%'],  # 饼图中心位置
         radius=['30%', '70%'],  # 内外半径
         rosetype='radius',  # 玫瑰图类型
         label_opts=opts.LabelOpts(
             is_show=True, 
             font_weight='bold', 
             font_size=14, 
             formatter='{b}:{c}({d}%)'  # 显示格式:类别:数量(百分比)
         )
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title='行业类别分布饼图', 
            subtitle='Top10类别占比',
            title_textstyle_opts=opts.TextStyleOpts(
                font_weight='bold', 
                font_family='楷体',
                font_size=25
            ),
            pos_top='top',
            pos_left='center'
        ),
        legend_opts=opts.LegendOpts(is_show=True, pos_top='10%'),  # 图例
        toolbox_opts=opts.ToolboxOpts(is_show=True),  # 工具箱
    )
)

7.4 投资价格分析

分析投资价格的分布情况,了解项目投资规模:

python 复制代码
# 创建投资价格区间
# 使用pd.cut将连续的投资价格离散化为区间
df['invest_level'] = pd.cut(
    df['invest_price'],
    bins=[0, 5, 10, 20, 50, 100, 500, 1000],  # 区间边界
    labels=['0-5万', '5-10万', '10-20万', '20-50万', '50-100万', '100-500万', '500万以上']  # 区间标签
)

# 统计各价格区间的项目数量
invest_Level = df['invest_level'].value_counts()
print("投资价格区间分布:")
print(invest_Level)

# 创建投资价格区间分布柱状图
bar_1 = (
    Bar()
    .add_xaxis(invest_Level.index.tolist())  # X轴:价格区间
    .add_yaxis('invest_price', invest_Level.values.tolist(),  # Y轴:项目数量
               label_opts=opts.LabelOpts(is_show=True, position='top'))  # 显示顶部标签
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title='投资价格区间分布', 
            subtitle='不同投资规模的项目数量',
            title_textstyle_opts=opts.TextStyleOpts(
                font_weight='bold', 
                font_family='楷体',
                font_size=23
            )
        ),
        toolbox_opts=opts.ToolboxOpts(
            is_show=True, 
            orient='vertical',  # 垂直排列
            pos_left='860px'  # 位置
        ),
        xaxis_opts=opts.AxisOpts(name='价格区间'),  # X轴名称
        yaxis_opts=opts.AxisOpts(name='项目数量'),  # Y轴名称
        visualmap_opts=opts.VisualMapOpts(is_show=True),  # 视觉映射
        datazoom_opts=opts.DataZoomOpts(is_show=True, type_='inside')  # 内置缩放
    )
)

# 创建投资价格箱线图(显示分布特征)
box = (
    Boxplot()
    .add_xaxis(['投资价格'])  # X轴:单一类别
    .add_yaxis('价格(万)', 
               Boxplot.prepare_data([df['invest_price'].tolist()])  # 准备箱线图数据
    )
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title='投资价格箱线图', 
            subtitle='价格分布特征(最小值、下四分位、中位数、上四分位、最大值)',
            title_textstyle_opts=opts.TextStyleOpts(
                font_weight='bold', 
                font_family='楷体',
                font_size=23
            )
        ),
        toolbox_opts=opts.ToolboxOpts(
            is_show=True, 
            orient='vertical', 
            pos_left='860px'
        )
    )
)

7.5 项目发布趋势分析

分析项目发布的时间趋势,识别市场活跃度变化:

python 复制代码
# 按年份和月份统计项目发布数量
project_number = df.groupby(['year', 'month'])['name'].count().reset_index(name='number')
# 等同于:project_number = df.groupby(['year', 'month'], as_index=False)['name'].count()

# 创建年月格式的日期列
project_number['date'] = project_number.apply(
    lambda x: f"{x['year']}--{x['month']:02d}",  # 格式:2023--01
    axis=1  # 按行操作
)

print("项目发布数量时间序列(部分):")
print(project_number.head())

# 创建时间趋势折线图
line = (
    Line()
    .add_xaxis(project_number['date'].tolist())  # X轴:年月
    .add_yaxis('项目数量', 
               project_number['number'].tolist(),  # Y轴:项目数量
               is_smooth=True)  # 平滑曲线
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title='项目发布数量时间趋势', 
            subtitle='按月统计',
            title_textstyle_opts=opts.TextStyleOpts(
                font_size=20, 
                font_family='楷体',
                font_weight='bold'
            )
        ),
        xaxis_opts=opts.AxisOpts(
            name='时间(年月)', 
            axislabel_opts=opts.LabelOpts(rotate=45)  # 标签旋转45度
        ),
        yaxis_opts=opts.AxisOpts(name='项目数量'),  # Y轴名称
        tooltip_opts=opts.TooltipOpts(
            is_show=True, 
            trigger='axis',  # 触发方式
            axis_pointer_type='cross'  # 十字准星指示器
        )
    )
    .set_series_opts(
        # 标记平均值线
        markline_opts=opts.MarkLineOpts(
            data=[opts.MarkLineItem(type_='average', name='平均值')]
        ),
        # 标记最大值和最小值点
        markpoint_opts=opts.MarkPointOpts(
            data=[
                opts.MarkPointItem(type_='max', name='最大值'), 
                opts.MarkPointItem(type_='min', name='最小值')
            ]
        )
    )
)

7.6 加入数量与人数关系分析

分析项目参与度指标的相关性:

python 复制代码
# 重命名列以增加可读性
df.rename(columns={'pople': 'people'}, inplace=True)

# 计算相关系数
cor = df['join_number'].corr(df['people'])
print(f"加入数量与人数的相关系数: {cor:.2f}")

# 创建散点图
scatter = (
    Scatter()
    .add_xaxis(df['join_number'].tolist())  # X轴:加入数量
    .add_yaxis('人数', df['people'].tolist())  # Y轴:人数
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title=f'加入数量与人数关系(相关系数:{cor:.2f})', 
            subtitle='相关性分析',
            title_textstyle_opts=opts.TextStyleOpts(
                font_weight='bold', 
                font_size=20,
                font_family='楷体'
            )
        ),
        xaxis_opts=opts.AxisOpts(name='加入数量'),  # X轴名称
        yaxis_opts=opts.AxisOpts(name='人数', max_=30000),  # Y轴名称和最大值
        visualmap_opts=opts.VisualMapOpts(is_show=True),  # 视觉映射
        toolbox_opts=opts.ToolboxOpts(is_show=True),  # 工具箱
    )
)

# 创建双Y轴折线图(时间序列比较)
sorted_data = df.sort_values(['year', 'month'])  # 按时间排序

line_2 = (
    Line(init_opts=opts.InitOpts(
        width='2000px', 
        height='800px', 
        renderer='canvas', 
        theme='white'
    ))
    .add_xaxis(sorted_data.apply(
        lambda x: f"{x['year']}-{x['month']:02d}", 
        axis=1
    ).tolist())  # X轴:年月
    .add_yaxis('加入数量', 
               sorted_data['join_number'].tolist(),  # 第一个Y轴:加入数量
               is_smooth=True,
               label_opts=opts.LabelOpts(is_show=False))  # 不显示标签
    .add_yaxis('人数', 
               sorted_data['people'].tolist(),  # 第二个Y轴:人数
               is_smooth=True, 
               label_opts=opts.LabelOpts(is_show=False))  # 不显示标签
    .set_global_opts(
        title_opts=opts.TitleOpts(title='加入数量与人数趋势比较'),  # 标题
        xaxis_opts=opts.AxisOpts(
            name='时间(年月)', 
            axislabel_opts=opts.LabelOpts(rotate=45)  # 标签旋转
        ),
        yaxis_opts=opts.AxisOpts(name='加入数量', position='left'),  # 左侧Y轴
        tooltip_opts=opts.TooltipOpts(is_show=True),  # 提示框
        datazoom_opts=opts.DataZoomOpts(is_show=True, type_='inside'),  # 内置缩放
        visualmap_opts=opts.VisualMapOpts(is_show=True)  # 视觉映射
    )
)

7.7 产品信息词云分析

分析产品描述中的关键词,了解技术热点:

python 复制代码
def clean_product(product):
    """
    清洗产品信息,去除特殊字符和多余空格
    
    参数:
        product: 原始产品信息字符串
        
    返回:
        清洗后的字符串
    """
    # 去除非中文、英文、数字、空格的字符
    product = re.sub(r'[^\u4e00-\u9fa5a-zA-Z0-9\s]', '', str(product))
    
    # 去除所有空白字符(包括空格、制表符、换行符等)
    product = re.sub(r'\s+', '', product).strip()
    
    return product

# 应用清洗函数
df['product'] = df['product'].apply(clean_product)

# 合并所有产品信息
all_product = ''.join(df['product'].tolist())

# 使用jieba进行中文分词
words = jieba.lcut(all_product)

# 定义停用词列表(过滤无意义词汇)
stopwords = ['的', '了', '和', '是', '在', '我', '有', '就', '不', '人', '都', '一', 
             '一个', '上', '也', '很', '到', '说', '要', '去', '你', '会', '着', 
             '没有', '看', '好', '自己', '这', '系统', '系列', '产品']

# 过滤停用词和单字词
words = [word for word in words if word not in stopwords and len(word) > 1]

# 统计词频
word_counts = Counter(words)
top_100_words = word_counts.most_common(100)

print("产品信息高频词汇Top10:")
for word, count in top_100_words[:10]:
    print(f"{word}: {count}次")

# 创建词云图
word_cloud = (
    WordCloud()
    .add('', 
         top_100_words,  # 词频数据
         word_size_range=[10, 80],  # 词大小范围
         shape='star',  # 词云形状
         textstyle_opts=opts.TextStyleOpts(font_family='楷体'),  # 字体
         pos_left='left',  # 位置
         pos_top='middle')
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title='产品信息词云图', 
            subtitle='高频关键词可视化',
            title_textstyle_opts=opts.TextStyleOpts(
                font_family='楷体', 
                font_size=20,
                font_weight='bold'
            )
        ),
    )
)

# 按行业类别分析产品关键词
category_product = df.groupby('category')['product'].apply(lambda x: ''.join(x)).reset_index()

print("\n各行业类别产品关键词Top10:")
for idx, row in category_product.iterrows():
    print(f"\n{row['category']}:")
    
    # 提取该分类的高频词汇
    category_words = jieba.lcut(row['product'])
    category_words = [word for word in category_words if word not in stopwords and len(word) > 1]
    
    category_word_counts = Counter(category_words)
    top_10 = category_word_counts.most_common(10)
    
    for word, count in top_10:
        print(f"  {word}: {count}")

7.8 创建综合仪表盘

将所有可视化图表整合到一个交互式仪表盘中:

python 复制代码
# 创建可拖拽布局的仪表盘
page = Page(
    layout=Page.DraggablePageLayout,  # 可拖拽布局
    page_title='项目数据分析Dashboard'  # 页面标题
)

# 添加所有图表到仪表盘
page.add(
    bar,      # 行业类别柱状图
    pie,      # 行业类别饼图
    bar_1,    # 投资价格区间分布
    box,      # 投资价格箱线图
    line,     # 项目发布趋势
    scatter,  # 加入数量与人数散点图
    line_2,   # 双Y轴趋势比较
    word_cloud,  # 产品信息词云
)

# 渲染为HTML文件
page.render('project_analysis_dashboard.html')
print("综合仪表盘已保存为: project_analysis_dashboard.html")
print("请用浏览器打开查看交互式图表")


你的点赞和关注是我更新的最大动力!

如果本教程对你有帮助:

  1. 👍 点赞支持一下

  2. 💬 评论告诉我你还想学什么爬虫项目

  3. 🔔 关注我获取更多爬虫和数据挖掘教程

  4. 收藏本文,方便以后查阅

我们一起进步! 🚀

相关推荐
流㶡3 小时前
网络爬虫之requests.get() 之爬取网页内容
python·数据爬虫
yuankoudaodaokou3 小时前
高校科研新利器:思看科技三维扫描仪助力精密研究
人工智能·python·科技
言無咎4 小时前
从规则引擎到任务规划:AI Agent 重构跨境财税复杂账务处理体系
大数据·人工智能·python·重构
张小凡vip4 小时前
数据挖掘(十)---python操作Spark常用命令
python·数据挖掘·spark
U盘失踪了4 小时前
Reqable 导出响应数据
python
2301_790300964 小时前
数据分析与科学计算
jvm·数据库·python
程序员小远4 小时前
使用Postman进行一次完整的接口测试
自动化测试·软件测试·python·测试工具·测试用例·接口测试·postman
Yvonne爱编码4 小时前
JAVA数据结构 DAY1-集合和时空复杂度
java·数据结构·python
DN20204 小时前
AI销售机器人:节日祝福转化率提升30倍
人工智能·python·深度学习·机器学习·机器人·节日