[M1 Mac, MacOS Ventura 13.3.1, clang 14.0.3]
libxmlを使ったスクレイピングに成功しました。
ただしPythonのBeautifulSoup & パーサー(html.parserなど)によるものとは違い、文字列比較で絞り込んでいく原始的な手法です。
今回の方法ではUTF-8への変換は不要でした。
IDやXPathを指定してパースできるのか、今後調査します。
#include <iostream>
#include <string>
#include <vector>
#include <curl/curl.h>
#include <libxml/HTMLparser.h>
std::vector<std::pair<std::string, std::string>> idTitlePairs;
std::string convertToString(const xmlChar* xmlString) {
if (xmlString == nullptr) {
return "";
}
return std::string(reinterpret_cast<const char*>(xmlString));
}
void parseAnchorTags(xmlNode* node) {
for (xmlNode* cur = node; cur; cur = cur->next) {
if (cur->type == XML_ELEMENT_NODE && xmlStrcmp(cur->name, (const xmlChar*)"small") == 0) {
xmlChar* id = xmlGetProp(cur, (const xmlChar*)"id");
if (xmlStrcmp(id, (const xmlChar*)"trad") == 0) {
for (xmlNode* child = cur->children; child; child = child->next) {
if (child->type == XML_ELEMENT_NODE && xmlStrcmp(child->name, (const xmlChar*)"a") == 0) {
xmlChar* id0 = xmlGetProp(child, (const xmlChar*)"href");
xmlChar* title0 = xmlNodeListGetString(child->doc, child->children, 1);
// std::cout << "id: " << id0 << std::endl;
// std::cout << "title: " << title0 << std::endl;
string id = convertToString(id0);
id.erase(id.size() - 4);
string title = convertToString(title0);
idTitlePairs.push_back(std::make_pair(id, title));
xmlFree(id0);
xmlFree(title0);
}
}
}
xmlFree(id);
}
parseAnchorTags(cur->children);
}
}
// コールバック関数
size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* buffer) {
size_t totalSize = size * nmemb;
buffer->append((char*)contents, totalSize);
return totalSize;
}
int main() {
// URLからHTMLファイルを取り込む
std::string url = "HTMLファイルのURL";
std::string htmlBuffer;
CURL* curl = curl_easy_init();
if (curl) {
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &htmlBuffer);
CURLcode res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
if (res != CURLE_OK) {
std::cerr << "Error: Failed to download HTML" << std::endl;
return 1;
}
} else {
std::cerr << "Error: Failed to initialize CURL" << std::endl;
return 1;
}
// htmlBufferの確認
// cout << "htmlBuffer: \n" << htmlBuffer << endl;
xmlDoc* doc = htmlReadMemory(htmlBuffer.c_str(), htmlBuffer.size(), nullptr, nullptr, HTML_PARSE_RECOVER);
if (doc) {
xmlNode* root = xmlDocGetRootElement(doc);
parseAnchorTags(root);
xmlFreeDoc(doc);
}
for (const auto& pair : idTitlePairs) {
std::cout << "id: " << pair.first << std::endl;
std::cout << "title: " << pair.second << std::endl;
}
return 0;
}