C ++およびGumboのParsim HTML



GumboはCのHTML5パーサーです。 これまでのところ、Gumboはツリーのみを提供しますが、それを操作するための便利な機能は提供していません。 したがって、ヘルパークラスをいくつか作成しました。



katの下には、habrahabr.ru / all /ページの分析例があります。

ファイルから読み取り、HTMLを解析します

std::string readAll(const std::string &fileName); //... using namespace EasyGumbo; auto page = readAll(argv[1]); Gumbo parser(&page[0]);
      
      





Gumboクラスのコンストラクターは、「\ 0」で終わるメモリバッファーへのポインターを受け取ります。



std :: find_if、コンパレータ、および要素



記事とそのURLをリストします。 これを行うには、値post_titleを持つクラス属性を含むタグa (アンカー)を見つけます。

  Gumbo::iterator iter = parser.begin(); while (iter != parser.end()) { iter = std::find_if(iter, parser.end(), And(Tag(GUMBO_TAG_A), HasAttribute("class", "post_title"))); if (iter == parser.end()) { break; } Element titleA(*iter); auto text = titleA.children()[0]; std::cout << "***\n"; std::cout << std::setw(8) << "Title" << " : " << text->v.text.text << std::endl; std::cout << std::setw(8) << "Url" << " : " << titleA.attribute("href")->value << std::endl; ++iter; }
      
      





TagおよびHasAttributeコンパレータで標準std :: find_ifアルゴリズムを使用します。 Andテンプレート関数は、 LogicalAndコンパレーターのインスタンスを作成します。 ElementGumboNodeのファサードです

要素インターフェース
 struct Element { typedef Vector<GumboNode*> ChildrenList; Element(GumboNode &element) noexcept : m_node(element) { assert(GUMBO_NODE_ELEMENT == m_node.type); } ChildrenList children() const noexcept { return ChildrenList(m_node.v.element.children); } const GumboSourcePosition& start() const noexcept { return m_node.v.element.start_pos; } const GumboSourcePosition& end() const noexcept { return m_node.v.element.end_pos; } const GumboAttribute* attribute(const char* name ) const noexcept { return gumbo_get_attribute(&m_node.v.element.attributes, name); } GumboNode &m_node; };
      
      







Gumboでは、テキストはGUMBO_NODE_TEXTタイプのノードに保存されるため、タグA



はアクセスせず、その子孫titleA.children()[0]



アクセスします。



findAllおよびイテレーター



段階的に進むのは不便な場合もありますが、必要なノードのリストをすぐに取得したいです。

  iter = std::find_if(iter, parser.end(), And(Tag(GUMBO_TAG_DIV), HasAttribute("class", "hubs"))); std::cout << std::setw(8) << "Hubs" << " : "; auto hubs = findAll(iter.fromCurrent(), parser.end(), Tag(GUMBO_TAG_A)); for (auto hub: hubs) { Element hubA(*hub); if (hub != hubs[0]) { std::cout << ", "; } std::cout << hubA.children()[0]->v.text.text; } std::cout << std::endl;
      
      





ここで、ハブを持つノードを見つけてから、findAllを使用して、 fromCurrentメソッドを使用して新しいイテレーターを作成し、すべてのAタグを引き出します。

イテレータは、通過を開始したツリーの最上部を記憶するように設計されています。 クロール中にこのノードでつまずいた場合、クロールは完了します。 この動作は便利です。サブツリーを離れる心配はありません。 これにより、コンストラクトを記述できます。

  auto posts = findAll(parser.begin(), parser.end(), And(Tag(GUMBO_TAG_A), HasAttribute("class", "post shortcuts_item"))); for(auto post : posts) { Gumbo::iterator iter(post); /* *     */ ... }
      
      





これは便利ですが、サブツリーを2回通過することがわかります。 またgotoAdjメソッドが公開されています 。これにより、次の要素に移動して、サブツリーをスキップできます。 残りのコードは簡単です。



分析全体は次のようになります。

 #include <fstream> #include <iomanip> #include <iostream> #include <algorithm> #include <gumbo.h> #include "Gumbo.hpp" using namespace std; std::string readAll(const std::string &fileName) { std::ifstream ifs; ifs.open(fileName); ifs.seekg(0, std::ios::end); size_t length = ifs.tellg(); ifs.seekg(0, std::ios::beg); std::string buff(length, 0); ifs.read(&buff[0], length); ifs.close(); return buff; } int main(int argc, char *argv[]) { if (argc != 2) { return 0; } using namespace EasyGumbo; auto page = readAll(argv[1]); Gumbo parser(&page[0]); Gumbo::iterator iter = parser.begin(); while (iter != parser.end()) { iter = std::find_if(iter, parser.end(), And(Tag(GUMBO_TAG_A), HasAttribute("class", "post_title"))); if (iter == parser.end()) { break; } Element titleA(*iter); auto text = titleA.children()[0]; std::cout << "***\n"; std::cout << std::setw(8) << "Title" << " : " << text->v.text.text << std::endl; std::cout << std::setw(8) << "Url" << " : " << titleA.attribute("href")->value << std::endl; iter = std::find_if(iter, parser.end(), And(Tag(GUMBO_TAG_DIV), HasAttribute("class", "hubs"))); std::cout << std::setw(8) << "Hubs" << " : "; auto hubs = findAll(iter.fromCurrent(), parser.end(), Tag(GUMBO_TAG_A)); for (auto hub: hubs) { Element hubA(*hub); if (hub != hubs[0]) { std::cout << ", "; } std::cout << hubA.children()[0]->v.text.text; } std::cout << std::endl; iter = std::find_if(iter, parser.end(), And(Tag(GUMBO_TAG_DIV), HasAttribute("class", "views-count_post"))); ++iter; std::cout << std::setw(8) << "Views" << " : " << iter->v.text.text << std::endl; iter = std::find_if(iter, parser.end(), And(Tag(GUMBO_TAG_SPAN), HasAttribute("class", "favorite-wjt__counter js-favs_count"))); ++iter; std::cout << std::setw(8) << "Stars" << " : " << iter->v.text.text << std::endl; iter = std::find_if(iter, parser.end(), And(Tag(GUMBO_TAG_A), HasAttribute("class", "post-author__link"))); Element authorA(*iter); std::cout << std::setw(8) << "Author" << " : " << authorA.children()[2]->v.text.text << std::endl; iter = std::find_if(iter, parser.end(), And(Tag(GUMBO_TAG_DIV), HasAttribute("class", "post-comments"))); auto comments = findAll(iter.fromCurrent(), parser.end(), Tag(GUMBO_TAG_A)); if (comments.size() == 1) { Element commentsA(*comments[0]); std::cout << std::setw(8) << "Comments" << " : " << commentsA.children()[0]->v.text.text << std::endl; } } return 0; }
      
      





コードは常にGitHubで利用可能です




All Articles