本文目录导读:

在PHP中采集数据并入库,主要涉及三个核心步骤:采集(抓取)、解析(提取) 和 存储(入库)。
下面给你一个完整的实战流程和代码示例,并附上一些常见的坑和最佳实践。
第一步:环境准备(必装扩展)
在开始之前,请确认你的PHP环境已开启以下扩展:
curl:用于抓取网页(比file_get_contents更稳定,支持模拟浏览器)。pdo或mysqli:用于连接数据库。mbstring或DOMDocument:用于解析HTML。
第二步:数据库表设计
假设我们要采集“商品列表”,MySQL建表语句如下:
CREATE TABLE `products` ( `id` int(11) NOT NULL AUTO_INCREMENT, varchar(255) NOT NULL COMMENT '标题', `price` decimal(10,2) DEFAULT NULL COMMENT '价格', `detail_url` varchar(500) DEFAULT NULL COMMENT '详情页URL', `image` varchar(500) DEFAULT NULL COMMENT '图片链接', `source_hash` varchar(32) DEFAULT NULL COMMENT 'URL去重哈希', `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uniq_hash` (`source_hash`) -- 防止重复采集 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
关键点:
source_hash字段用于URL去重,使用md5($url)存入,并在数据库设置唯一索引,避免重复入库。
第三步:PHP采集与入库完整代码
<?php
/**
* PHP 采集数据入库 Demo
*/
// ---------- 1. 数据库连接 (PDO) ----------
$dsn = 'mysql:host=127.0.0.1;dbname=test;charset=utf8mb4';
$user = 'root';
$pass = 'password';
try {
$pdo = new PDO($dsn, $user, $pass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die('数据库连接失败: ' . $e->getMessage());
}
// ---------- 2. 采集函数 (cURL) ----------
function curl_get($url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 如果网站是https,暂时关闭证书验证
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
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/120.0.0.0 Safari/537.36'); // 模拟浏览器
curl_setopt($ch, CURLOPT_TIMEOUT, 30); // 超时时间
$data = curl_exec($ch);
curl_close($ch);
return $data;
}
// ---------- 3. 解析HTML (DOMDocument + XPath) ----------
function parse_html($html) {
$dom = new DOMDocument();
// 屏蔽解析HTML时的警告
libxml_use_internal_errors(true);
$dom->loadHTML('<?xml encoding="utf-8"?>' . $html); // 避免中文乱码
libxml_clear_errors();
$xpath = new DOMXPath($dom);
$items = [];
// 这里以某网站为例,假设商品标题在 .item-title 类中,价格在 .item-price 类中
$titles = $xpath->query('//h3[@class="item-title"]/a');
$prices = $xpath->query('//span[@class="item-price"]');
$links = $xpath->query('//h3[@class="item-title"]/a');
foreach ($titles as $i => $title_node) {
$items[] = [
'title' => trim($title_node->textContent),
'price' => (float) str_replace(['¥', ','], '', $prices->item($i)->textContent),
'url' => $links->item($i)->getAttribute('href'),
'image' => '' // 可根据实际情况提取
];
}
return $items;
}
// ---------- 4. 入库逻辑(使用预处理+事务) ----------
function insert_db($pdo, $items) {
// 预处理SQL,防止SQL注入
$sql = "INSERT INTO products (title, price, detail_url, image, source_hash)
VALUES (:title, :price, :url, :image, :hash)";
$stmt = $pdo->prepare($sql);
$pdo->beginTransaction(); // 开启事务,加快插入速度
$count = 0;
foreach ($items as $item) {
$hash = md5($item['url']); // 生成URL哈希
try {
$stmt->execute([
':title' => $item['title'],
':price' => $item['price'],
':url' => $item['url'],
':image' => $item['image'],
':hash' => $hash,
]);
$count++;
} catch (PDOException $e) {
// 如果唯一索引冲突(说明已存在),则跳过
if ($e->getCode() == 23000) {
continue;
} else {
// 其他错误,回滚
$pdo->rollBack();
throw $e;
}
}
}
$pdo->commit();
echo "成功入库 {$count} 条数据\n";
}
// ---------- 5. 主流程 ----------
$target_url = 'https://example.com/products'; // 替换为你要采集的目标网址
// 抓取
$html = curl_get($target_url);
if (empty($html)) {
die('抓取失败,页面为空');
}
// 解析
$items = parse_html($html);
// 入库
insert_db($pdo, $items);
echo "采集完成,本次共处理 " . count($items) . " 条数据。\n";
第四步:高级优化与避坑指南
防封IP(代理池)
采集频繁会导致IP被封,建议设置 curl_setopt($ch, CURLOPT_PROXY, 'http://代理IP:端口') 或使用随机延迟。
// 每次请求后随机暂停 0.5~2秒 usleep(rand(500000, 2000000));
处理异步加载的数据(AJAX)
如果目标网页是动态加载的(如API接口返回JSON),直接用cURL拿到的是空壳,你需要:
- 打开浏览器F12 -> Network,找到XHR请求的URL。
- 直接采集那个API地址,拿到JSON数据后,用
json_decode()解析入库。
相对路径转绝对路径
页面里的链接可能都是相对路径(如 /product/123.html),入库前需要拼成绝对URL:
$base_url = 'https://example.com';
if (strpos($link, 'http') !== 0) {
$link = rtrim($base_url, '/') . '/' . ltrim($link, '/');
}
数据清洗
- 用
trim()去除多余空格。 - 价格去符号:
str_replace(['¥', '$', ','], '', $str)。 - 转义HTML实体:
htmlspecialchars_decode($str)。
批量入库(性能优化)
如果每天采集上万条,逐条 execute 太慢,改用 批量插入:
$values = [];
$placeholders = [];
foreach ($items as $item) {
$placeholders[] = "(?, ?, ?, ?, ?)";
$values = array_merge($values, [$item['title'], $item['price'], $item['url'], $item['image'], md5($item['url'])]);
}
$sql = "INSERT INTO products (title, price, detail_url, image, source_hash) VALUES " . implode(",", $placeholders);
$pdo->executeQuery($sql, $values);
第五步:命令行脚本(推荐使用)
将上述代码保存为 spider.php,在命令行运行(避免网页执行超时):
php spider.php
如果你想在浏览器中运行且数据量很大,记得在脚本开头设置:
set_time_limit(0);
总结流程图
[cURL抓取] → [DOM解析/正则提取] → [数据清洗] → [MD5去重] → [PDO预处理入库]
你只需要根据目标网页的HTML结构调整 parse_html() 函数中的 XPath 或正则表达式即可,如果遇到复杂登录的情况,还需要用 curl_setopt 携带Cookie或使用Session。