[C++] 322 BBS閲覧アプリの製作 その8 ボードタイトルの取得 libxml, XPath

[M1 Mac, MacOS Ventura 13.3.1, clang 14.0.3]

BBSのボードタイトルをカテゴリーと共に2次元vectorとして取得しました。

全てのaタグをnodeのリストとして取得し、aタグのhref、aタグの内容、すぐ上にあるbタグの内容(カテゴリー)の3要素をvectorにプッシュバックしています。bタグがない場合は”XXX”をカテゴリーとします。

2要素の場合はmake_pair関数かmake_tuple関数、3要素以上の場合はmake_tuple関数を使います。make_pairでは変数名.first、変数名.secondでそれぞれ呼び出すことができます。make_tupleの要素はstd::get<番号>(変数名)で取得します。

aタグから遡ってbタグを探す、などややこしめの課題を与えるとChatGPTからまともな答えがほぼ返ってこないのですが、今回は一発正解でした。

int main() {
    std::vector<std::tuple<std::string, std::string, std::string>> idTitleCat;
    std::string cat;

<中略>
    xmlDoc* doc = htmlReadMemory(htmlBuffer.c_str(), htmlBuffer.size(), nullptr, nullptr, HTML_PARSE_RECOVER);
    if (doc) {
        xmlNodeSetPtr boardObj = executeXpath(doc, (xmlChar *)"//*[name()='a']");

        if (boardObj) {
            for (int i = 0; i < boardObj->nodeNr; i++) {
                xmlNodePtr node = boardObj->nodeTab[i];
                xmlChar* id0 = xmlGetProp(node, (xmlChar*)"href");
                xmlChar* title0 = xmlNodeGetContent(node);

                string id = convertToString(id0);
                id.erase(id.size() - 4);
                string title = convertToString(title0);

                xmlNodePtr catNode = node->prev;
                while (catNode != nullptr && xmlStrcmp(catNode->name, (const xmlChar*)"b") != 0) {
                    catNode = catNode->prev;
                }

                if (catNode != nullptr) {
                    xmlChar* cat0 = xmlNodeGetContent(catNode);
                    cat = convertToString(cat0);
                    xmlFree(cat0);
                } else {
                    cat = "XXX";
                }

                idTitleCat.push_back(std::make_tuple(id, title, cat));
                
                xmlFree(id0);
                xmlFree(title0);
            }
        }
        xmlFreeDoc(doc);
    }


    for (const auto& element : idTitleCat) {
        std::cout << "id: " << std::get<0>(element) << std::endl;
        std::cout << "title: " << std::get<1>(element) << std::endl;
        std::cout << "cat: " << std::get<2>(element) << std::endl;
    }