网页正文提取(Webpage Content Extraction)是一种常见的自然语言处理技术,可以从网页中提取出主要内容并去除无关内容,常被应用于网络爬虫、搜索引擎、信息抽取等领域。

下面介绍一种基于 C 语言的简单网页正文提取方法:

获取网页源代码

可以使用 C 语言的网络编程库(如 curl、libcurl)获取网页源代码。例如:

#include <stdio.h>
#include <curl/curl.h>

int main(void) {
CURL *curl;
CURLcode res;
char url[] = "http://www.example.com";
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url);
res = curl_easy_perform(curl);
if (res != CURLE_OK)
fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
curl_easy_cleanup(curl);
}
return 0;
}

去除 HTML 标签

使用正则表达式或者 HTML 解析器(如 libxml2)去除 HTML 标签,只留下文本内容。例如:

#include <stdio.h>
#include <curl/curl.h>
#include <libxml/HTMLparser.h>

void startElement(void *ctx, const xmlChar *name, const xmlChar **atts) {}
void endElement(void *ctx, const xmlChar *name) {}
void characters(void *ctx, const xmlChar *ch, int len) {
fwrite(ch, 1, len, stdout);
}

int main(void) {
CURL *curl;
CURLcode res;
char url[] = "http://www.example.com";
curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
htmlParserCtxtPtr ctxt = htmlCreatePushParserCtxt(NULL, NULL, "", 0, "", 0);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctxt);
curl_easy_perform(curl);
htmlParseChunk(ctxt, "", 0, 1);
htmlFreeParserCtxt(ctxt);
curl_easy_cleanup(curl);
}
return 0;
}

提取正文

根据网页的结构和排版,使用一些规则或算法提取出正文。例如:

基于标签密度:计算每个标签内文本的字符数,选取字符数最多的标签作为正文。

基于行块分布函数(Text Density):将网页按照一定的规则(如行宽、字体大小等)分成若干行块,选取行块分布函数曲线最陡峭的区域作为正文。

基于机器学习:使用训练好的分类模型对网页的每个段落进行分类,选取分类为正文的段落作为正文。

一个简单的实现可以基于标签密度,计算每个标签内文本的字符数,选取字符数最多的标签作为正文。例如:

#include <stdio.h>
#include <string.h>
#include <libxml/HTMLparser.h>

#define MAX_TEXT_LEN 10000

void extract_text(xmlNode *node, int *max_density, char *max_text) {
// 计算当前节点的标签密度
int node_density = 0;
xmlChar *node_text = xmlNodeGetContent(node);
if (node_text != NULL) {
node_density = strlen((char *)node_text);
xmlFree(node_text);
}

// 遍历子节点
for (xmlNode *cur_node = node->children; cur_node != NULL; cur_node = cur_node->next) {
// 递归处理子节点
extract_text(cur_node, max_density, max_text);
}

// 如果当前节点的标签密度更高,则更新 max_density 和 max_text
if (node_density > *max_density) {
*max_density = node_density;
node_text = xmlNodeGetContent(node);
if (node_text != NULL) {
strncpy(max_text, (char *)node_text, MAX_TEXT_LEN);
xmlFree(node_text);
}
}
}

int main() {
// 解析 HTML 文件
xmlDoc *doc = htmlReadFile("test.html", NULL, HTML_PARSE_NOBLANKS | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING);
if (doc == NULL) {
fprintf(stderr, "Failed to parse HTML file.\n");
return 1;
}

// 提取正文
int max_density = 0;
char max_text[MAX_TEXT_LEN] = "";
extract_text(xmlDocGetRootElement(doc), &max_density, max_text);

// 输出正文
printf("Extracted text:\n%s\n", max_text);

// 释放资源
xmlFreeDoc(doc);
xmlCleanupParser();
return 0;
}

这个实现使用递归遍历 HTML 树,计算每个标签内文本的字符数,并选取字符数最多的标签作为正文。注意这个实现是一个简单的示例,可能无法处理复杂的 HTML 结构或者有些特殊情况。如果要用于实际项目中,需要根据具体需求进行改进和优化。