Python爬虫实战:抓取柳州企业工商数据并做竞品分析

发布时间:2026-08-07 阅读:1 作者:500 元建网站

项目背景与法律边界

作为一名服务于柳州企业的技术人员经常需要做市场调研和竞品分析。一个典型的需求是:"帮我整理一下柳州所有做网站建设的公司名单看看他们的注册资本、成立年限、经营范围等信息。"手动去国家企业信用信息公示系统逐条查看显然太低效了这时候 Python 爬虫就能派上用场。

在开始写代码之前必须先明确法律和道德边界。合法的数据来源(只抓取公开可访问的信息不绕过登录验证不抓取需要授权才能看到的内容);遵守 robots.txt(检查目标网站的 robots.txt 文件遵守其爬取规则);合理的请求频率(设置足够的延时避免对目标服务器造成过大压力一般建议每次请求间隔 2-5 秒);数据用途正当(采集的数据仅用于合法的商业分析和研究不用于倒卖或侵犯他人隐私);优先使用 API(如果目标网站提供了官方 API 接口优先使用 API 而非爬虫本文演示的场景中如果企查查/天眼查等有合适的 API 也可以考虑使用)。牢记:技术能力越大责任越大始终做一个有操守的开发者。

数据采集:Requests + BeautifulSoup

对于静态页面(服务器直接返回完整 HTML 内容的页面)使用 requests + beautifulsoup4 是最轻量高效的方案。首先安装依赖(pip install requests beautifulsoup4 pandas lxml matplotlib);编写基础爬虫框架:import requests from bs4 import BeautifulSoup import time import random import pandas as pd from urllib.parse import urljoin import logging logging.basicConfig(level=logging.INFO format="%(asctime)s - %(levelname)s - %(message)s") headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"} SESSION = requests.Session() SESSION.headers.update(headers) def fetch_page(url, retries=3): for i in range(retries): try: resp = SESSION.get(url, timeout=15) resp.raise_for_status() return resp.text except requests.RequestException as e: logging.warning(f"请求失败 (第{i+1}次): {e}") if i < retries - 1: time.sleep(2 ** i) return None

解析 HTML 提取数据:def parse_company_list(html): soup = BeautifulSoup(html, "lxml") companies = [] items = soup.select(".company-list-item") for item in items: try: name = item.select_one(".company-name").get_text(strip=True) capital = item.select_one(".capital").get_text(strip=True) date = item.select_one(".establish-date").get_text(strip=True) scope = item.select_one(".business-scope").get_text(strip=True) status = item.select_one(".status").get_text(strip=True) companies.append({"公司名称": name, "注册资本": capital, "成立日期": date, "经营范围": scope, "经营状态": status}) except AttributeError as e: logging.warning(f"解析条目失败: {e}") continue return companies

处理动态渲染页面:Selenium方案

越来越多的现代网站使用 Vue/React 等前端框架内容通过 JavaScript 动态渲染这种情况下 requests 拿到的 HTML 只是空壳需要使用 Selenium 模拟真实浏览器行为。安装 Selenium(pip install selenium 同时需要下载对应版本的 ChromeDriver 或使用 webdriver-manager 自动管理);Selenium 爬虫示例:from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.chrome.options import Options import time def create_driver(): options = Options() options.add_argument("--headless") options.add_argument("--disable-gpu") options.add_argument("--no-sandbox") options.add_argument(f"--user-agent={headers["User-Agent"]}") driver = webdriver.Chrome(options=options) return driver def scrape_dynamic_page(url): driver = create_driver() try: driver.get(url) WebDriverWait(driver, 15).until( EC.presence_of_element_located((By.CSS_SELECTOR, ".company-list-item")) ) for scroll in range(3): driver.execute_script("window.scrollTo(0, document.body.scrollHeight);") time.sleep(2) html = driver.page_source finally: driver.quit() return html

Selenium 的注意事项:headless 模式下也要设置合理的 User-Agent 否则有些网站会检测到 headless 浏览器特征并拒绝服务;等待机制一定要用 WebDriverWait + 显式条件等待而不是固定的 time.sleep() 这样既能保证稳定性又能避免不必要的等待时间;注意内存泄漏长时间运行的 Selenium 爬虫应该定期重启 WebDriver 或者改用 Playwright(更现代的替代方案内存占用更低 API 更友好)。

数据清洗与存储

原始数据往往存在各种问题需要清洗后才能用于分析。常见问题和处理方法:缺失值(df.dropna(subset=["公司名称", "注册资本"]) 删除关键字段为空的行或 df["注册资本"].fillna("未公示") 用默认值填充);重复值(df.drop_duplicates(subset=["公司名称"]) 根据公司名称去重);格式统一(注册资本字段可能有"100万人民币"、"100万元"等多种写法需要统一转换为数值类型);文本清理(经营范围字段可能有多余空格换行符需要 .strip() 和正则清理)。完整的清洗函数示例:import re def clean_capital(s): if pd.isna(s) or s == "未公示": return 0 match = re.search(r"([\d.]+)", str(s)) if match: num = float(match.group(1)) if "万" in s: num *= 10000 elif "亿" in s: num *= 100000000 return int(num) return 0 df["注册资本_数值"] = df["注册资本"].apply(clean_capital)

数据存储方案:CSV 格式(df.to_csv("liuzhou_companies.csv", index=False, encoding="utf-8-sig" 最简单的格式 Excel 可直接打开);SQLite 数据库(import sqlite3 conn = sqlite3.connect("companies.db") df.to_sql("companies", conn, if_exists="replace", index=False) 适合后续程序化查询);MySQL 数据库(from sqlalchemy import create_engine engine = create_engine("mysql+pymysql://user:pass@localhost/dbname") df.to_sql("companies", engine, if_exists="replace", index=False) 适合生产环境和 Web 应用对接)。推荐在采集过程中就做好增量去重——用公司名称做唯一键已存在的跳过只插入新数据这样可以支持多次运行而不产生重复记录。

Pandas分析与可视化

有了干净的数据就可以开始分析了。以下是一些常见的竞品分析维度和对应的 Pandas/Matplotlib 代码。基本统计概览:print(f"企业总数: {len(df)}") print(f"注册资本均值: {df['注册资本_数值'].mean():.0f} 元") print(f"存续企业占比: {(df['经营状态'] == '存续').mean()*100:.1f}%");注册资本分布直方图:import matplotlib.pyplot as plt plt.rcParams["font.sans-serif"] = ["SimHei"] plt.rcParams["axes.unicode_minus"] = False fig, ax = plt.subplots(figsize=(10, 6)) df[df["注册资本_数值"] > 0]["注册资本_数值"].hist(bins=50, ax=ax, edgecolor="black") ax.set_title("柳州目标行业企业注册资本分布", fontsize=14) ax.set_xlabel("注册资本(元)") ax.set_ylabel("企业数量") plt.tight_layout() plt.savefig("capital_distribution.png", dpi=150);Top 10 企业排名:top10 = df.nlargest(10, "注册资本_数值")[["公司名称", "注册资本_数值", "成立日期", "经营范围"]] print(top10.to_string(index=False))。

更高级的分析可以包括:文本分析(对经营范围字段做 jieba 分词和词频统计了解行业热门业务方向);地理分布(如果有地址信息可以用高德地图 API 做地理编码后在地图上展示企业分布热力图);关联分析(通过股东信息和高管信息挖掘企业之间的关联关系网络)。这些分析结果可以输出为 HTML 报告(使用 Jupyter Notebook 或 Streamlit 生成交互式 Dashboard)供管理层决策参考。记住数据分析的目的不是为了画漂亮的图表而是为了发现有价值的商业洞察帮助做出更好的决策。以上就是从数据采集到分析可视化的完整流水线希望对柳州的技术同行们有所帮助。

上一篇:技术实战:柳州企业网站从零搭建Nginx+PHP+MySQL高性能架构