核心思路
一个采集工具的核心工作流程可以概括为:

(图片来源网络,侵删)
- 目标分析:分析你要采集的网站页面结构,找到文章列表页和文章详情页的规律。
- 模拟请求:使用 PHP 的 cURL 或 file_get_contents 获取目标网页的 HTML 内容。
- 内容提取:使用 PHP 的 DOMDocument 或正则表达式,从 HTML 中精准地提取出标题、内容、作者、来源、图片等信息。
- 内容处理:对提取的内容进行清洗、格式化(例如处理图片路径、过滤广告等)。
- 入库发布:将处理好的内容按照 DedeCMS 的数据结构,插入到数据库的
dede_archives(文章主表)、dede_addonarticle(文章附加表)等表中,并自动生成文章HTML页面。
第一步:准备工作
- 本地环境:确保你有一个可以运行 PHP 和 MySQL 的本地环境(如 XAMPP, WAMP, MAMP)。
- DedeCMS 安装:安装一个干净的 DedeCMS 程序到你本地环境。
- 目标网站选择:选择一个结构清晰、没有复杂反爬虫机制的网站作为练习目标。请务必注意:采集时请遵守目标网站的
robots.txt协议,尊重版权,仅供学习使用。
第二步:创建采集工具的后台管理页面
为了方便在 DedeCMS 后台管理我们的采集工具,我们可以创建一个类似官方插件的管理界面。
- 创建目录:在 DedeCMS 的
/dede/目录下创建一个新文件夹,my_crawler/。 - 创建主文件:在
/dede/my_crawler/目录下创建一个index.php文件,这个文件将是我们采集工具的入口和主界面。
/dede/my_crawler/index.php (基础框架)
<?php
require_once(dirname(__FILE__)."/config.php");
CheckPurview('sys_Collection');
// 引入公共函数库
require_once(DEDEINC.'/dedecollection.class.php');
// 如果是提交表单,则执行采集逻辑
if (isset($_POST['submit'])) {
$list_url = trim($_POST['list_url']);
$title_rule = trim($_POST['title_rule']);
$content_rule = trim($_POST['content_rule']);
// 这里将调用我们的核心采集函数
// crawlSite($list_url, $title_rule, $content_rule);
echo "<script>alert('采集任务已提交!'); window.location='index.php';</script>";
exit;
}
// 显示采集表单
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">我的自定义采集工具</title>
<link href="../static/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container-fluid mt-3">
<h2>我的自定义采集工具</h2>
<form action="index.php" method="post" class="form-horizontal">
<div class="form-group">
<label class="col-sm-2 control-label">列表页URL:</label>
<div class="col-sm-10">
<input type="text" name="list_url" class="form-control" placeholder="http://www.example.com/list_{page}.html" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">标题提取规则:</label>
<div class="col-sm-10">
<input type="text" name="title_rule" class="form-control" placeholder="h1.title" required>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">内容提取规则:</label>
<div class="col-sm-10">
<input type="text" name="content_rule" class="form-control" placeholder="div.post-content" required>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" name="submit" class="btn btn-primary">开始采集</button>
</div>
</div>
</form>
</div>
</body>
</html>
第三步:编写核心采集逻辑
这是整个工具的灵魂,我们将创建一个类来封装所有采集功能。
/dede/my_crawler/Crawler.php

(图片来源网络,侵删)
<?php
class MyCrawler {
private $baseUrl;
private $host;
// 构造函数,初始化基础URL
public function __construct($url) {
$this->baseUrl = $url;
$this->host = parse_url($url, PHP_URL_HOST);
}
/**
* 获取网页内容
* @param string $url 目标URL
* @return string|false HTML内容
*/
public function fetchContent($url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // 跟随重定向
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36');
curl_setopt($ch, CURLOPT_TIMEOUT, 30); // 30秒超时
curl_setopt($ch, CURLOPT_HEADER, 0);
$content = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($httpCode != 200) {
// 可以在这里记录错误日志
return false;
}
curl_close($ch);
return $content;
}
/**
* 使用DOMDocument解析HTML并提取内容
* @param string $html HTML字符串
* @param string $selector CSS选择器 (e.g., 'div.post-content')
* @return string|false 提取到的内容
*/
public function extractBySelector($html, $selector) {
if (empty($html) || empty($selector)) {
return false;
}
$dom = new DOMDocument();
// @用于抑制因为HTML不规范产生的警告
@$dom->loadHTML('<?xml encoding="UTF-8">' . $html);
$xpath = new DOMXPath($dom);
// 将CSS选择器转换为XPath
// 这是一个简化的转换,实际中可能需要更复杂的逻辑或引入第三方库
$xpathQuery = '//' . str_replace('.', '/', $selector);
$nodeList = $xpath->query($xpathQuery);
if ($nodeList->length > 0) {
// 取第一个匹配的元素
$node = $nodeList->item(0);
// 保留HTML标签
return $dom->saveHTML($node);
}
return false;
}
/**
* 处理图片,将相对路径转为绝对路径
* @param string $content HTML内容
* @return string 处理后的内容
*/
public function processImages($content) {
$dom = new DOMDocument();
@$dom->loadHTML('<?xml encoding="UTF-8">' . $content);
$xpath = new DOMXPath($dom);
$imgNodes = $xpath->query('//img');
foreach ($imgNodes as $img) {
$src = $img->getAttribute('src');
if (!empty($src) && strpos($src, 'http') !== 0) {
// 如果是相对路径,则拼接为绝对路径
$img->setAttribute('src', $this->baseUrl . '/' . ltrim($src, '/'));
}
}
return $dom->saveHTML();
}
/**
* 保存文章到DedeCMS数据库
* @param array $articleData 文章数据数组
*/
public function saveToDede($articleData) {
global $dsql;
$typeid = isset($articleData['typeid']) ? $articleData['typeid'] : 1; // 默认分类ID
$title = $dsql->GetInnerText($articleData['title']);
$body = $articleData['content'];
$source = $articleData['source'] ?? '本站原创';
$author = $articleData['author'] ?? '管理员';
$description = mb_substr(strip_tags($body), 0, 200, 'utf-8'); // 自动生成摘要
$arcQuery = "INSERT INTO `dede_archives` (`typeid`, `typeid2`, `sortrank`, `flag`, `ismake`, `channel`, `arcrank`, `click`, `money`, `title`, `shorttitle`, `color`, `writer`, `source`, `litpic`, `pubdate`, `senddate`, `mid`, `keywords`, `description`, `notpost`, `templet`, `filename`)
VALUES ({$typeid}, 0, 0, 'c', -1, 1, 0, 0, 0, '{$title}', '', '', '{$author}', '{$source}', '', NOW(), NOW(), 1, '', '{$description}', 0, '', '');";
$dsql->ExecuteNoneQuery($arcQuery);
$aid = $dsql->GetLastID(); // 获取新插入文章的ID
// 插入到附加表
$addQuery = "INSERT INTO `dede_addonarticle` (`aid`, `body`, `templet`) VALUES ({$aid}, '{$body}', '');";
$dsql->ExecuteNoneQuery($addQuery);
// 生成HTML
// include_once(DEDEINC."/arc.archives.class.php");
// $arc = new Archives($aid);
// $arc->MakeHtml();
// 为了简化,这里不直接生成HTML,DedeCMS的后台任务会处理
// 或者你可以调用DedeCMS的API来生成
echo "文章《{$title}》采集成功,ID: {$aid}<br>";
}
/**
* 主采集方法
* @param string $listUrl 列表页URL
* @param string $titleSelector 标题选择器
* @param string $contentSelector 内容选择器
*/
public function crawl($listUrl, $titleSelector, $contentSelector) {
$listContent = $this->fetchContent($listUrl);
if (!$listContent) {
echo "无法获取列表页内容!";
return;
}
// 提取文章链接列表 (这里简化处理,实际需要根据列表页结构分析)
// 假设列表页的每个文章链接都在 <a class="article-link"> 标签中
$dom = new DOMDocument();
@$dom->loadHTML('<?xml encoding="UTF-8">' . $listContent);
$xpath = new DOMXPath($dom);
// 这是一个示例,你需要根据实际网站结构调整
$linkNodes = $xpath->query('//a[contains(@class, "article-link")]');
echo "找到 " . $linkNodes->length . " 篇文章,开始采集...<hr>";
foreach ($linkNodes as $linkNode) {
$articleUrl = $linkNode->getAttribute('href');
if (strpos($articleUrl, 'http') !== 0) {
$articleUrl = 'http://' . $this->host . $articleUrl;
}
echo "正在采集: {$articleUrl}<br>";
$articleContent = $this->fetchContent($articleUrl);
if (!$articleContent) {
echo " - 采集失败,跳过。<br>";
continue;
}
$title = $this->extractBySelector($articleContent, $titleSelector);
$content = $this->extractBySelector($articleContent, $contentSelector);
if ($title && $content) {
// 处理图片路径
$content = $this->processImages($content);
// 准备入库数据
$articleData = [
'title' => $title,
'content' => $content,
'source' => '采集自 ' . $this->host,
'typeid' => 1, // 设置一个默认的分类ID
];
$this->saveToDede($articleData);
} else {
echo " - 提取标题或内容失败,规则可能不正确。<br>";
}
// 礼貌性延时,避免请求过于频繁
sleep(1);
}
echo "<hr>采集任务完成!";
}
}
第四步:整合与调用
我们需要修改 index.php,让它调用我们刚刚创建的 MyCrawler 类。
修改 /dede/my_crawler/index.php
<?php
require_once(dirname(__FILE__)."/config.php");
CheckPurview('sys_Collection');
// 引入我们自己的采集器类
require_once(dirname(__FILE__).'/Crawler.php');
// 如果是提交表单,则执行采集逻辑
if (isset($_POST['submit'])) {
$list_url = trim($_POST['list_url']);
$title_rule = trim($_POST['title_rule']);
$content_rule = trim($_POST['content_rule']);
// 实例化采集器
$crawler = new MyCrawler($list_url);
// 开始采集
$crawler->crawl($list_url, $title_rule, $content_rule);
// 执行完后,不再显示表单,直接结束
exit;
}
// ... (以下部分保持不变,显示采集表单) ...
?>
<!DOCTYPE html>
<!-- ... (HTML表单部分与之前相同) ... -->
第五步:测试与优化
- 访问后台:登录你的 DedeCMS 后台,在菜单中应该能看到“我的自定义采集工具”的入口(如果没有,可能需要手动在菜单中添加)。
- 填写规则:
- 列表页URL:填写你准备好的列表页地址。
- 标题提取规则:在浏览器中打开一个文章详情页,按F12打开开发者工具,找到标题元素,复制其CSS选择器。
<h1 class="article-title">,规则就填h1.article-title。 - 内容提取规则:同样在开发者工具中找到文章正文内容的容器,复制其CSS选择器。
<div class="post-body">,规则就填div.post-body。
- 开始采集:点击“开始采集”按钮,观察页面输出,你会看到采集进度和结果。
可能的优化方向
- 分页采集:修改
crawl方法,让它支持{page}这样的占位符,循环采集多页列表。 - 规则存储:将采集规则(URL、标题选择器、内容选择器等)保存到数据库中,方便管理和复用。
- 队列处理:对于大量采集任务,使用队列(如 MySQL 表或 Redis)来存储待采集的URL,避免因网络问题或脚本超时导致任务中断。
- 更强大的选择器:PHP 的 DOMDocument 对 CSS3 选择器的支持有限,可以考虑集成更强大的库,如 PHPQuery(模仿 jQuery 语法)或 Symfony DomCrawler,它们能让你用更直观的方式选择元素。
- 图片本地化:编写一个函数,将文章中的远程图片下载到你的服务器,并替换为本地路径,这是非常实用的功能。
- 反爬虫应对:增加 IP 代理池、随机 User-Agent、请求间隔等策略,以提高采集的成功率。
重要提醒
- 版权与法律前,请务必确认目标网站的版权政策,商业用途的采集可能涉及法律风险。
- 服务器负载:采集会频繁请求目标网站和你的本地数据库,可能会对服务器造成较大压力,建议在服务器负载较低时进行,或使用队列分批处理。
- DedeCMS 版本:本教程基于较新版本的 DedeCMS 数据库结构,如果你使用的是非常古老的版本,
dede_archives和dede_addonarticle的字段可能略有不同,请根据实际情况调整 SQL 语句。
通过以上步骤,你就拥有了一个功能基础但完全可控的 DedeCMS 采集工具,你可以基于这个框架,不断添加新功能,让它变得越来越强大,祝你编码愉快!

(图片来源网络,侵删)
