news 2026/10/1 12:20:27

Python爬虫实战:市场监管局公开公示数据采集与清洗全流程

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python爬虫实战:市场监管局公开公示数据采集与清洗全流程

有回朋友让我帮忙整理某区市场监管局公示的一批食品经营许可名单,说是拿来做公司的合规调研。我一开始觉得这事儿复制粘贴就行,结果对方发来一个链接,上千条记录分布在几十个列表页里,其中还有一部分要到详情页才能看到完整信息。手动复制不但费时间,还容易看漏。我说别整了,写个爬虫吧,半小时的事。这篇文章就是把当时的完整过程复盘一遍,核心关键词就四个:爬虫、市场监管局、公开公示数据、requests+BeautifulSoup。这套东西非常适合刚入门 Python 爬虫的读者,因为它不涉及登录、不涉及复杂加密,数据结构规整,能让你用最小成本把整个爬虫流程走通。

1. 项目背景:先看页面,再定方案,最后才动手写代码

1.1 这种需求到底长什么样

很多人一听到“爬虫案例”,脑子里浮现的全是电商、短视频、社交平台那些高对抗场景。但真实世界里,需求量最大的一类爬虫恰恰是这种“公开信息整理”:政府网站的公告公示、行业协会的名录、公共事业单位的办事指南。这类目标数据没有复杂的反爬,没有登录墙,数据本身也是面向公众公开的,唯一的难点是量大、零散、人工整理效率低。

我当时拿到的页面长这样:一个公告列表页,每页二十条左右,一条记录包含店铺名称、许可证编号、法定代表人、发证日期、状态等字段;上方有“共 X 条记录,共 Y 页”的提示;底部是上一页、下一页的翻页控件。点进每一条,还有一个详情页,里面会多出经营范围、地址等字段。这种结构在市场监管局以及各类政府站点的公告栏目里非常常见。

动手之前要先做判断:这个页面是静态 HTML,还是 JS 动态渲染?当时我用浏览器打开了页面源码,列表数据直接在 HTML 里。也就是说,不需要 selenium,不需要等待脚本执行,直接用 requests 就能把整页 HTML 拿回来。这一步判断非常重要,它决定了后面所有技术选型。

1.2 列表页和详情页的分工

这个案例里,页面分两层:

  • 列表页:承载摘要信息,一般用<table>或<ul>结构。列表页的价值在于线索,告诉你有哪几条记录、在哪个链接里。
  • 详情页:承载完整信息,一条记录一个页面。详情页的价值在于补全,把列表页上没有的字段补回来。

我当时的策略是:列表页先抓全量索引,之后只对确实需要详细字段的少数记录进入详情页抓取。这样既省流量,又减少对目标站点的请求次数。如果每条列表记录都要进详情页,请求量会放大二十倍,被限流的概率也大得多。

1.3 动手前的合规确认清单

随手能打开的内容不等于没有边界。我给自己定了一套检查清单,也建议你照着过一遍:

  1. 目标页面是否无需登录即可访问。需要登录才能看的内容,属于站点的非公开区域,原则上不应该碰。
  2. 页面是否有明显的版权声明或使用限制。有些站点写明“禁止采集”,这种直接放弃。
  3. 确认抓取的字段里没有身份证号、手机号、家庭住址等个人敏感信息。如果列表里有,就只抓别的必要字段,敏感字段一律不碰。
  4. 检查robots.txt。用网页打开站点根域名下的/robots.txt路径,看看有没有明确Disallow爬虫访问的目录。如果目标栏目在 Disallow 名单里,就收手。

当时检查完,数据是公开公示信息,字段里也只需要企业名称、许可证号、日期这类内容,整体风险可控。所以我继续往下做。这里多说一句:合规这块不是走过场,而是保护自己。爬虫本身是工具,但工具怎么用,边界必须清晰。

2. 技术选型:requests 能干的活,没必要上 selenium

2.1 静态页面是 requests 的舒适区

Python 爬虫的入门组合拳,大多数情况就是requests负责拿数据,BeautifulSoup负责解析 HTML。这对组合的优势在于轻量、稳定、好调试。对于市场监管局这种公开公告页面,页面背后的 HTML 是服务端直接渲染好的,你请求一次,返回的就是完整页面,不需要执行任何 JavaScript。

用 requests 做这件事,整个链路非常短:发请求 → 拿到字符串 → 解析 → 提取字段。出了任何问题,打印一下返回内容就能定位。相比之下,selenium 要启动浏览器、等待渲染、处理弹窗,虽然能搞定动态页面,但开销大得多,跑起来也慢,还经常因为浏览器版本问题水土不服。

2.2 selenium 和 Scrapy 什么时候才需要

很多新手容易犯一个毛病:学完 selenium 之后什么网站都想用 selenium。实际上它只适合两种情况:一是数据在页面源码里根本找不到,必须等 JS 跑完才渲染出来;二是需要模拟点击、滚动输入这类真实交互才能翻出更多数据。如果页面右键查看源码就能看见数据,直接用 requests 更省事。

Scrapy 又是另一个层级。它适合处理几十万条以上的数据、需要自动去重、并发调度、分布式扩展的场景。这个市场监管局案例有多少量?撑死几千条,用 Scrapy 属于杀鸡用牛刀。分布式爬虫更是没必要,单机一个循环就够。爬虫选型的第一原则永远是匹配数据量级,而不是把技术栈堆到最复杂。

2.3 环境准备

我用的是 Python 3.9 版本。依赖只有四个:requests、beautifulsoup4、lxml、pandas。前两个是爬虫主力,lxml 是 BeautifulSoup 的解析器,pandas 用于最后的数据落盘和统计。安装命令如下:

pip install requests beautifulsoup4 lxml pandas

建议在动手前用一个虚拟环境,避免污染全局的 Python 环境:

python -m venv venv source venv/bin/activate # Windows 下是 venv\Scripts\activate

lxml 的作用容易被忽略。BeautifulSoup 默认用html.parser,处理简单页面没问题,但遇到不规范、结构嵌套比较深的 HTML,lxml 的容错性和速度都更好。所以实际项目中我基本都指定lxml。

3. 核心爬虫实现:请求、解析、翻页、存储四步走

3.1 请求构造与重试机制

先写一个最基础的请求函数。这个函数要处理两件事:模拟浏览器的请求头,以及失败后的重试。

import requests import time BASE_URL = "https://scjg.example.gov.cn/publicity/list" HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 " "(KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36", "Accept-Language": "zh-CN,zh;q=0.9", "Referer": "https://scjg.example.gov.cn/", } def fetch_html(url, params=None, retries=3): for attempt in range(retries): try: resp = requests.get(url, params=params, headers=HEADERS, timeout=10) resp.raise_for_status() resp.encoding = resp.apparent_encoding return resp.text except requests.RequestException as e: print(f"[{attempt + 1}/{retries}] 请求失败:{e}") if attempt < retries - 1: time.sleep(2 ** attempt) # 指数退避:1s、2s、4s return None

解释一下几个细节。User-Agent是服务器识别客户端的第一个依据,现在很多站点会拒绝空 UA 或者非常规 UA 的请求,所以这个字段必须带。Referer表示请求从哪个页面跳过来,模拟真实浏览器访问路径。timeout一定要设,不设的话,碰到服务器无响应,requests 有可能长时间挂住。apparent_encoding是根据页面内容自动猜测编码,后面会细说,这里先让它跑起来。

重试部分我用的是指数退避策略:第一次失败等 1 秒,第二次等 2 秒,第三次等 4 秒。这是爬虫工程里非常基础但好用的手法,能有效缓解临时性的网络抖动,又不至于在服务器繁忙时继续猛打。

3.2 列表页解析与详情页链接

拿到 HTML 字符串之后,下一步就是解析。解析之前有个必要动作:先在浏览器里按 F12,找到列表区域对应的 DOM 结构。我当时看到的页面结构大概是这样的:

<table class="data-list"> <tbody> <tr> <td>某餐饮管理有限公司</td> <td>JY202411230001</td> <td>2024-11-23</td> <td><a href="/detail/20241123/123.html">查看</a></td> </tr> </tbody> </table>

解析代码就能写成这样:

from bs4 import BeautifulSoup def parse_list_page(html): soup = BeautifulSoup(html, "lxml") rows = soup.select("table.data-list tbody tr") records = [] for row in rows: tds = row.find_all("td") if len(tds) < 4: continue link = tds[3].find("a") records.append({ "company_name": tds[0].get_text(strip=True), "license_no": tds[1].get_text(strip=True), "publish_date": tds[2].get_text(strip=True), "detail_url": link["href"] if link else "", }) return records

这里有几个容易忽略的坑。第一,get_text(strip=True)可以把元素里的空白字符、换行清理干净,避免出现“ 某餐饮管理有限公司 ”这种脏数据。第二,len(tds) < 4的检查很重要,有些表格行可能是合并单元格、表头行,直接跳过比硬解析好。第三,详情页链接的href经常是相对路径,后面要用urljoin转成完整 URL,这个我在踩坑部分再展开。

选择器的写法不要照抄我这里的.data-list,一定要以你实际看到的页面为准。有的站点列表用的是<ul><li>,有的用<div class="news-item">,解法都一样:先定位容器,再逐行提取。

3.3 翻页逻辑

列表页通常有页码参数。我遇到的站点翻页规则很单纯:URL 的 query 里加一个page参数,第 1 页是page=1,第 2 页是page=2。翻页代码就是一个循环:

import random def crawl_list(start_page, end_page): all_records = [] for page in range(start_page, end_page + 1): params = {"page": page} html = fetch_html(BASE_URL, params=params) if html is None: print(f"[跳过] 第 {page} 页请求失败") continue page_records = parse_list_page(html) print(f"第 {page} 页抓取到 {len(page_records)} 条") all_records.extend(page_records) time.sleep(random.uniform(2, 4)) # 每次翻页随机停顿 return all_records

有的站点总页数会直接显示在页面上,有的只显示“下一页”按钮。判断总页数有一个笨但有效的办法:从第 1 页开始翻,当某一页返回的记录条数为 0 且连续出现两次时,就默认已经到底了。第一次跑的时候可以把end_page设大一点,完全靠空页判断来终止。跑完以后看看实际翻到了哪一页,再回来修正参数,这样最稳。

翻页之间加random.uniform(2, 4)的睡眠,不是装模作样。它能把请求频率控制在一个比较体面的水平,减少被限流的可能。我后面单独开一章讲频率控制,这里先埋个伏笔。

3.4 落盘为 CSV

抓完以后,数据是内存里的一组字典。保存我用的是 pandas,一行代码就能生成 CSV:

import pandas as pd df = pd.DataFrame(all_records) df.to_csv("scjg_records.csv", index=False, encoding="utf-8-sig")

utf-8-sig这个编码很关键。普通 UTF-8 保存的 CSV,用 Excel 打开时中文会乱码,因为 Excel 默认用 ANSI 编码解读文件。utf-8-sig会在文件开头写入 BOM 头,Excel 识别到之后就会按 UTF-8 处理,中文能正常显示。这是处理中文 CSV 的经典经验。

如果不想为了保存一个 CSV 引入 pandas,用标准库csv也完全没问题:

import csv with open("scjg_records.csv", "w", newline="", encoding="utf-8-sig") as f: writer = csv.DictWriter(f, fieldnames=all_records[0].keys()) writer.writeheader() writer.writerows(all_records)

两种方案殊途同归。我习惯用 pandas,因为后面数据清洗和统计还要用到它,不如从一开始就统一用这套工具链。

4. 反爬与频率控制:小爬虫也要有“礼貌”

4.1 先认清常见的反爬机制

虽然市场监管局这类公示站点相对温和,但并不是完全没有防护。爬虫写得过于粗暴照样会触发限制。常见的反爬机制和对应现象,我整理了一张表:

常见机制表现合理应对方式
UA 检测请求返回 403,或者在日志里看到“浏览器不支持”设置正常的 User-Agent
频率限制请求正常但返回页面为空,或突然变慢拉长间隔,加随机睡眠
IP 封禁连续多次 403 或连接超时立即停止程序,休息一段时间
会话计数单位时间内请求数超出阈值全局限速,每秒不超过 1 次请求
验证码页面出现验证码图片或滑块直接停手,检查是否已经对站点造成压力

你可能会奇怪,为什么表格里“验证码”这一行只写了停手,没写怎么破解。这是因为绕验证码属于典型的突破防护机制,既不合适,也没必要。公开数据少抓几条、抓慢一点,不会影响你拿到核心信息;一旦把验证码绕过写成流程,性质就彻底变了。这件事必须想明白。

4.2 限速、退避与重试

我当时的做法很朴素:全局维护一个最小请求间隔,每次请求之前计算一下距上次请求的时间差,如果太短就补觉。代码可以这样写:

import time class PoliteCrawler: def __init__(self, min_interval=2.0): self.min_interval = min_interval self.last_request_time = 0 def wait_if_needed(self): elapsed = time.time() - self.last_request_time if elapsed < self.min_interval: time.sleep(self.min_interval - elapsed) self.last_request_time = time.time()

这样就算翻页循环里忘了写sleep,限速逻辑也能兜底。随机睡眠和全局限速可以同时用,随机睡眠解决“节奏太规律”的问题,全局限速解决“整体频率太高”的问题。两者侧重不同,叠加起来效果更好。

指数退避讲过了,再补充一点:不要在收到服务器错误响应后立刻原地重试,尤其不要用固定 0.5 秒重试十次。这种重试风暴比爬虫本身更容易触发反爬。正确做法是把它放到一个退避队列里,等待一段时间再重新发起,连续失败多次就直接放弃,记入日志人工介入。

4.3 断点续爬:跑到一半断了,不用从头再来

爬虫跑一半卡住或者被断网,是很常见的事。如果每次重跑都从第 1 页开始,浪费的请求都是额外的服务器压力。断点续爬的做法并不复杂:每抓完一页,把页码写入一个本地进度文件。下次启动的时候读取进度文件,从上次结束的页码继续。

import os def get_checkpoint(progress_file="progress.txt"): if os.path.exists(progress_file): with open(progress_file, "r") as f: return int(f.read().strip() or "1") return 1 def save_checkpoint(page, progress_file="progress.txt"): with open(progress_file, "w") as f: f.write(str(page))

在翻页循环里,每成功处理完一页就调用save_checkpoint(page)。这样哪怕第 50 页断掉,重启之后从 51 页接着跑,前面的数据不需要重新抓。

5. 实战踩坑:编码、空值和页面结构变化的应对

5.1 中文乱码问题

requests 拿到的响应内容是字节流,需要解码成字符串。市场上老旧的政务网站经常用 GBK 或 GB2312 编码,一些新站点用 UTF-8。如果固定写死resp.encoding = "utf-8",碰到 GBK 页面就会解析出一堆乱码。

我在 3.1 里用了resp.apparent_encoding,这个东西会从页面内容里自动推测编码,多数情况下是准的。但它也有失灵的时候,页面编码声明不规范,或内容样本太少,推测就可能出错。保守的做法是手动指定常见编码,再配合apparent_encoding做兜底:

if resp.apparent_encoding: resp.encoding = resp.apparent_encoding else: resp.encoding = "utf-8"

还有一种情况是页面声明是 UTF-8,但因为服务器配置问题,实际内容用 ISO-8859-1 传输。这种问题比较隐蔽,排查方法就是打印前 500 个字符看看有没有乱码,有就换个编码试。爬虫调试里有个特别土但特别好用的手段:把返回的 HTML 保存到本地文件,用浏览器打开直接看。浏览器对编码的容错能力强,人眼扫一眼就能定位是请求问题还是解析问题。

5.2 字段缺失和空值

列表页字段缺失很常见。有的记录没有法定代表人,有的没有发证日期,有的详情链接为空。如果解析代码不做保护,一个None就能让后面整段逻辑崩溃。

我习惯在解析完一页之后做一次统一清洗,把所有字段中多余的空白字符去掉,空值统一转成空字符串:

def clean_record(record): result = {} for key, value in record.items(): if value is None: result[key] = "" else: result[key] = str(value).strip() return result

这个函数看着简单,价值却很大。它把“字段可能为 None”这个不确定性拦截在一个地方,后面所有环节都不用再处理空值问题。写爬虫的时候,第一版可以跑通流程,但正式抓取之前一定要随机抽查几页数据,确认每条字段都干净。

5.3 相对链接和页面改版

列表里的详情链接,经常写成href="/detail/123.html"这种相对路径形式。直接把这个值当作完整 URL 去请求,requests 会报错或者请求到一个不存在的路径。处理方式是用urljoin:

from urllib.parse import urljoin detail_url = urljoin("https://scjg.example.gov.cn/publicity/list", href)

urljoin会自动判断相对路径应该拼接到哪个层级。这个函数可以处理/detail/...、detail/...、../detail/...各种写法,比自己手动拼字符串稳妥得多。

页面改版则是另一个更大的坑。政务类网站改版频率不算低,今天还是table.data-list,明天可能就变成div.news-item。解析代码里所有选择器都会失效。应对办法有两个:一是优先使用页面上比较稳定的属性,比如某个容器的id,而不是容易变化的 CSS 类名;二是在主流程里加一个记录条数自检,某页解析结果远低于预期时,停止抓取并告警,而不是把空数据默默写进最终结果。

6. 数据清洗、去重与增量抓取

6.1 用唯一标识做去重

抓完上千条数据之后,第一步是去重。这个案例里,每条记录都有“许可证编号”,这是天然的唯一标识。用 pandas 一步就能完成:

df = df.drop_duplicates(subset=["license_no"], keep="first")

但如果某些记录恰好没有许可证编号,用单一字段去重就不够了。保险的做法是构造一个复合键,比如“公司名称 + 发证日期”。我的经验是:先打印一下每条记录的唯一键长度分布,如果空值过多,就不要硬依赖唯一标识,改为组合键去重。

df["_dedup_key"] = df["company_name"] + "_" + df["license_no"].fillna("") df = df.drop_duplicates(subset=["_dedup_key"])

去重之后顺手打印一下数据量变化,能帮你判断抓取过程中是否有重复翻页。如果去重前后数据量差异巨大,多半是翻页逻辑出了问题,比如某些页被重复抓取。

6.2 增量更新

网站在不断更新,今天抓完明天又有新记录。增量抓取的思路很简单:把本地已有的唯一键集合加载到内存里,新抓到的记录如果唯一键已经存在就跳过,只保留新增部分。

def load_existing_keys(csv_path="scjg_records.csv"): if os.path.exists(csv_path): old_df = pd.read_csv(csv_path) return set(old_df["license_no"].dropna().astype(str)) return set() existing_keys = load_existing_keys() new_records = [r for r in all_records if r["license_no"] not in existing_keys]

很多公示类站点的列表页是按发布时间倒序排列的,新增记录永远在最前面。这意味着增量更新只需要抓前面几页,就能拿到绝大部分新数据。这比每次都全量抓取省力得多,对目标站点的负担也小。

6.3 顺手做个简单统计

数据抓下来不分析一下,有点浪费。用 pandas 做一个按月统计,看公示数量的时间趋势,几行代码就够:

df["publish_month"] = df["publish_date"].str.slice(0, 7) monthly = df.groupby("publish_month").size().reset_index(name="count") monthly.to_csv("monthly_summary.csv", index=False, encoding="utf-8-sig")

注意slice(0, 7)的前提是日期格式统一。如果同一条数据里有2024-11-23也有2024/11/23,就得先做格式归一化:

df["publish_date"] = df["publish_date"].str.replace("/", "-", regex=False)

这种看似不起眼的清洗,实际场景里经常遇到。数据源不统一,是所有爬虫项目的常态,提前在清洗阶段处理掉,后面统计就不用反复折腾。

7. 合规红线与最后复盘

7.1 三条不能碰的线

整个项目做完,我最想强调的还是合规性。爬虫本身不违法,但边界必须清楚。以我自己的标准,有三条线绝对不能碰:

  • 不绕过访问控制。凡是需要登录、需要验证码才能看的内容,不碰。这些内容属于站点采取了技术保护措施的数据,绕过访问控制去拿,性质完全不同。
  • 不拖垮目标服务。请求频率控制在合理范围,遇到异常先停而不是先冲。做爬虫的人应该有这个自觉:你的程序对服务器造成的压力,不应该超过一个普通用户连续手动浏览的程度。
  • 不重复采集敏感字段。采集字段只取完成任务所必需的。公示平台里如果出现了联系方式、证件号这类信息,哪怕它展示在页面上,也不要写进自己的库。

这三条我每次做爬虫之前都会过一遍脑子。它不能让你赚到什么,但能帮你避开绝大多数麻烦。

7.2 还能往哪些方向扩展

如果你跑通了这个小案例,想继续深入,方向其实很多。数据量大了之后,可以上 Scrapy 做并发调度,同时保持去重和限速。遇到动态渲染的列表,可以学一下 selenium,但要想清楚“是不是 requests 也能解决”。再往后,数据格式判断、异常感知、分布式抓取、持久化到数据库,这些都是自然的技术演进路径。

但我要多嘴一句:别为了练技术而故意去爬那些防护很强的站点。公开公示类网站是练手的好地方,因为数据结构真实,又不涉及敏感内容,可以让你把爬虫的整个闭环完整走一遍。

7.3 我自己的一点体会

做这个市场监管局小案例之前,我也写过不少爬虫,但每次都会踩一些重复的坑。这个项目真正让我觉得有价值的,不是代码本身,而是“先分析页面、再写脚本”这个习惯被再次验证了。拿到一个目标站点的第一件事永远是花几分钟看结构,而不是急着写代码。页面里哪些字段是稳定的、哪些链接需要拼接、分页规则是什么,这些东西确认一遍,后面写代码都是顺水推舟的事。

最后再分享一个小技巧:任何爬虫项目,第一版代码都不要追求完美。先把 1 页跑通,确认解析正确,再去补循环、重试、断点续爬这些东西。小步快跑,比一次性堆一大段逻辑然后排错半天要高效得多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 12:20:19

正余弦算法优化VMD参数:信号分解自动寻优方案

简介&#xff1a;一份面向信号处理与数据分析人员的Python实现包&#xff0c;聚焦正余弦算法(SCA)对变分模态分解(VMD)关键参数的自动优化。压缩包内共9个文件&#xff0c;包含4个xml工程配置、1个py核心算法脚本、1个txt示例数据&#xff0c;以及若干IDE辅助配置文件&#xff…

作者头像 李华
网站建设 2026/10/1 12:19:45

独立开发者要不要写测试?一套轻量级风险控制策略

前阵子有个做独立产品的朋友突然找我&#xff0c;说技术栈终于定完了&#xff0c;CI 也搭了&#xff0c;但有个问题卡了他很久&#xff1a;测试到底写不写&#xff1f;他一个人维护三个项目&#xff0c;白天写业务、晚上被用户追着改 bug&#xff0c;怎么看都觉得写测试是在浪费…

作者头像 李华
网站建设 2026/10/1 12:19:28

航电软件开发全解析:从DO-178C实践到适航认证的避坑指南

1. 开场&#xff1a;这个行业最不缺的&#xff0c;是“教训”我在这行摸爬滚打十几年&#xff0c;见过太多同行在航电软件开发上栽跟头。有人把DO-178C当成文档流水线&#xff0c;有人把“通过测试”等同于“验证充分”&#xff0c;还有人至今分不清“确认”和“验证”的区别。…

作者头像 李华
网站建设 2026/10/1 12:19:12

Web身份认证基石:Session认证原理、实现与常见坑位全解析

Web身份认证是每个做Web开发的人迟早都要面对的一道坎。刚入行那会儿&#xff0c;我总以为登录功能就是把用户名密码查一下库&#xff0c;比对成功就完事。直到第一个带完整账号体系的系统上线&#xff0c;才意识到“记住你是谁”这件事&#xff0c;远比想象中复杂得多。今天想…

作者头像 李华
网站建设 2026/10/1 12:18:45

正则化回归实战:Python中岭回归、Lasso与Elastic Net调参指南

简介&#xff1a;这是一份面向数据科学与统计学学习者的正则化回归Python算法资源&#xff0c;系统实现L1正则化&#xff08;Lasso&#xff09;与L2正则化&#xff08;Ridge&#xff09;两种主流模型&#xff0c;解决高维数据下模型过拟合和特征选择问题。资源基于Scikit-Learn…

作者头像 李华
网站建设 2026/10/1 12:18:33

广州靠谱GEO专业公司怎么选?广东横琴讯灵智能科技挑选全攻略

广东横琴讯灵智能科技有限公司是一家扎根珠海横琴&#xff0c;为大湾区制造、建材机电、工程类B端企业提供垂直数字化营销解决方案的服务商&#xff0c;核心业务为GEO生成式引擎优化&#xff0c;同时配套短视频IP打造、账号运营与数字化定制服务&#xff0c;凭借自研双引擎技术…

作者头像 李华