本文目录导读:

我来提供几种PHP网页正文提取的方案,从简单到复杂:
方案1:使用第三方库(推荐)
使用 html2text 库
composer require html2text/html2text
<?php
require 'vendor/autoload.php';
use Html2Text\Html2Text;
$html = file_get_contents('https://example.com/article');
$converter = new Html2Text($html);
echo $converter->getText();
使用 goutte (基于Symfony DomCrawler)
composer require fabpot/goutte
<?php
require 'vendor/autoload.php';
use Goutte\Client;
$client = new Client();
$crawler = $client->request('GET', 'https://example.com/article');
内容
$content = $crawler->filter('article')->first()->text();
echo $content;
方案2:使用正则表达式提取
<?php
class ContentExtractor {
private $html;
public function __construct($html) {
$this->html = $html;
}
/**
* 简单正文提取
*/
public function extractSimple() {
// 去除script和style标签
$html = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $this->html);
$html = preg_replace('/<style\b[^>]*>(.*?)<\/style>/is', '', $html);
// 去除注释
$html = preg_replace('/<!--(.*?)-->/is', '', $html);
// 去除HTML标签
$text = strip_tags($html);
// 处理空白字符
$text = preg_replace('/\s+/', ' ', $text);
$text = trim($text);
return $text;
}
/**
* 高级正文提取(基于常见正文标签)
*/
public function extractAdvanced() {
$patterns = [
'/<article[^>]*>(.*?)<\/article>/is',
'/<div[^>]*class=["\']article["\'][^>]*>(.*?)<\/div>/is',
'/<div[^>]*id=["\']content["\'][^>]*>(.*?)<\/div>/is',
'/<div[^>]*class=["\']content["\'][^>]*>(.*?)<\/div>/is',
'/<div[^>]*class=["\']post["\'][^>]*>(.*?)<\/div>/is',
];
foreach ($patterns as $pattern) {
if (preg_match($pattern, $this->html, $matches)) {
$content = $this->cleanHtml($matches[1]);
return $content;
}
}
return $this->extractSimple();
}
private function cleanHtml($html) {
$html = preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $html);
$html = preg_replace('/<style\b[^>]*>(.*?)<\/style>/is', '', $html);
$html = preg_replace('/<!--(.*?)-->/is', '', $html);
$text = strip_tags($html);
$text = preg_replace('/\s+/', ' ', $text);
return trim($text);
}
}
// 使用示例
$html = file_get_contents('your_html_file.html');
$extractor = new ContentExtractor($html);
$content = $extractor->extractAdvanced();
echo $content;
方案3:使用DOMDocument和XPath
<?php
class DOMContentExtractor {
private $dom;
private $xpath;
public function __construct($html) {
$this->dom = new DOMDocument();
// 使用libxml来避免HTML5标签警告
libxml_use_internal_errors(true);
$this->dom->loadHTML('<?xml encoding="UTF-8">' . $html, LIBXML_NOERROR);
libxml_clear_errors();
$this->xpath = new DOMXPath($this->dom);
}
/**
* 根据常用选择器提取正文
*/
public function extractBySelectors() {
$selectors = [
'//article',
'//*[contains(@class, "article")]',
'//*[contains(@class, "content")]',
'//*[contains(@class, "post")]',
'//div[@id="content"]',
];
foreach ($selectors as $selector) {
$nodes = $this->xpath->query($selector);
if ($nodes->length > 0) {
return $this->getTextContent($nodes->item(0));
}
}
return null;
}
/**
* 智能提取:根据文本密度
*/
public function extractSmart() {
$blocks = $this->xpath->query('//div | //p | //article | //section');
$bestBlock = null;
$maxScore = 0;
foreach ($blocks as $block) {
$text = $block->textContent;
$score = $this->calculateTextDensity($text);
if ($score > $maxScore) {
$maxScore = $score;
$bestBlock = $block;
}
}
if ($bestBlock) {
return $this->getTextContent($bestBlock);
}
return null;
}
private function calculateTextDensity($text) {
// 文本密度计算
$text = trim(preg_replace('/\s+/', ' ', $text));
$chars = mb_strlen($text);
$links = substr_count($text, '<a');
// 简单的文本密度评分
return $chars - ($links * 10);
}
private function getTextContent(DOMNode $node) {
$text = $node->textContent;
return trim(preg_replace('/\s+/', ' ', $text));
}
}
// 使用示例
$html = file_get_contents('your_html_file.html');
$extractor = new DOMContentExtractor($html);
$content = $extractor->extractSmart();
echo $content;
方案4:综合使用的完整示例
<?php
require 'vendor/autoload.php';
use DiDom\Document;
class ArticleExtractor {
private $url;
private $content;
public function __construct($url) {
$this->url = $url;
}
public function fetch() {
// 获取HTML
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $this->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');
$html = curl_exec($ch);
curl_close($ch);
// 使用DiDom解析
$document = new Document($html);
return $this->extractContent($document);
}
private function extractContent(Document $document) {
$selectors = [
'article',
'.article',
'.content',
'.post',
'#content',
'.main-content'
];
foreach ($selectors as $selector) {
$elements = $document->find($selector);
if (count($elements) > 0) {
$this->content = $elements[0]->text();
return $this->content;
}
}
return null;
}
public function getTitle(Document $document) {
$titleElement = $document->find('title');
if (count($titleElement) > 0) {
return $titleElement[0]->text();
}
return null;
}
}
// 使用
try {
$extractor = new ArticleExtractor('https://example.com/article');
$contents = $extractor->fetch();
echo $contents;
} catch (Exception $e) {
echo 'Error: ' . $e->getMessage();
}
注意事项
- 编码问题:确保处理UTF-8编码
- 链接检测:可以过滤掉包含大量链接的区块
- 图片处理:可以选择保留或移除图片
- 性能优化:对大HTML文件使用流式处理
- 反爬虫:注意网站的使用条款
推荐库列表
- Html2Text:简单转换
- DOMDocument + XPath:PHP内置,灵活
- Goutte:Web爬虫和测试
- DiDom:简单易用的DOM操作
- Readability.php:类似Mozilla的Readability算法
选择哪个方案取决于你的具体需求,最简单的场景可以用正则表达式方案,复杂的抽取建议使用DOMDocument或第三方库。