C++ウェブスクレイピング:パフォーマンスが重要なプロジェクトのための実践ガイド
Expert Network Defense Engineer
TL;DR:
- C++は素晴らしいスクレイピングのパーサーだが、やや不便なスクレイピングのクライアントである。 libxml2はネイティブ速度でHTMLを解析するが、C++でTLSフィンガープリンティング、プロキシのローテーション、JavaScriptのレンダリングを書くのは独立したプロジェクトである。
- 仕事を分ける: レンダリングAPIに取得を任せ、C++に解析をさせる。 libcurlはターゲットURLをScrapeless Universal Scraping APIにPOSTし、レンダリングされたHTMLを返す。libxml2 + XPathはローカルで迅速に抽出を行う。
js_render: trueが空白のページをデータに変える。 多くのサイトはJavaScriptでコンテンツを構築する; APIはサーバーサイドでそれを実行するため、あなたのC++コードは完成したDOMを受け取り、空のシェルではない。- クライアント全体は、あなたがすでに知っている2つのライブラリから成る。 HTTP POSTにはlibcurl、HTML + XPathにはlibxml2 — 埋め込むヘッドレスブラウザも必要なく、C++で維持するアンチボットスタックも必要ない。
- 実証済みの小ささ。 以下の完全なプログラムは1行の
g++でコンパイルされ、ライブで実行された: HTTP 200、51 KBのレンダリングされたHTML、取得された20の製品タイトル。 - 無料で始められる。 新しいScrapelessアカウントには無料のランタイムが含まれている — app.scrapeless.comでサインアップ。
はじめに:C++がスクレイピングでの役割
C++がスクレイピングに現れる理由は1つだけ: スピード。数百万のページを解析する際、libxml2がネイティブ速度でHTMLを処理するのは、インタープリタ型のパーサーに対し明らかに優れている。しかし、問題は解析の前のすべてである。モダンなターゲットは、TLSフィンガープリンティングとアンチボット防御、IPの評判、実際にコンテンツ存在前に実行する必要があるJavaScriptといった形で、アクセスを制限している。これをC++で再現するには — ステルスHTTPスタック、プロキシプール、埋め込まれたブラウザエンジン — は、スクレイピング自体より遙かに多くの作業が必要である。
実用的な分割方法は、C++が不得意な部分をやらせるのを止めることである。取得を扱うためにレンダリングAPIを使用し — TLS、プロキシ、JavaScript、アンチボット — 完成したHTMLをC++に渡して解析させる。あなたのプログラムは、2つのよく理解されたパーツに分かれる: libcurlが1つのHTTPS POSTを行い、libxml2がその結果に対してXPathを実行する。
このガイドは、まさにScrapeless Universal Scraping APIを使用してそれを構築する。最後の完全なプログラムはコンパイルされ、ライブで実行された; その中の数値は実際のキャプチャである。
これを使ってできること
- ネイティブ速度で解析 — レンダリングされたHTMLに対してlibxml2 + XPath、インタープリタのオーバーヘッドなし。
- JavaScript重視のサイトをスクレイピング — あなたのC++バイナリにブラウザを埋め込むことなく。
- アンチボットスタックをスキップ — TLSフィンガープリンティング、プロキシのローテーション、CAPTCHAの処理はAPI側で行われ、あなたのコードには含まれない。
- 既存のC++システムにスクレイピングを落とし込む — トレーディングパイプライン、データツール、ネイティブサービス — libcurlとlibxml2だけで。
- 水平方向にスケール — レンダリングはサーバーサイドで行われるため、バイナリは軽量なまま。
Scrapeless Universal Scraping APIを選ぶ理由
Scrapeless Universal Scraping APIはターゲットURLを受け取り、レンダリングされたブロックされていないHTMLを返す。特にC++クライアントに対して、次のメリットをもたらす:
- サーバーサイドでのJavaScriptレンダリング —
js_render: trueは完成したDOMを返すため、libxml2は実際のコンテンツを見ることができる。 - 195か国以上の住宅用プロキシ — 取得は信頼されたIPから出る; プールを管理する必要はない。
- アンチボット処理 — TLSフィンガープリンティングとチャレンジの解決はAPI側で行われ、あなたのバイナリからは外れる。
- シンプルなHTTPS POST — リンクするSDKは不要; libcurlが必要なC++の全てである。
- シンプルなエンベロープ —
{"code":200,"data":"<html>…"}、重いJSON依存なしに簡単に処理できる。
app.scrapeless.comで無料プランのAPIキーを取得してください。
前提条件
- C++17コンパイラ(g++またはclang)
- libcurlおよびlibxml2の開発ヘッダー
- ScrapelessアカウントとAPIキー — app.scrapeless.comでサインアップ
Debian/Ubuntuの場合:
bash
sudo apt-get install -y libcurl4-openssl-dev libxml2-dev
bash
export SCRAPELESS_API_KEY="your_api_token_here"
ステップ1 — libcurlでURLをPOST
APIは、アクター名(unlocker.webunlocker)および入力 — ターゲットURL、メソッド、JavaScriptをレンダリングするかどうか — を含むJSONボディを受け取る。認証は x-api-tokenヘッダーによって行われる。libcurlはレスポンスをstd::stringに収集する:
cpp
#include <curl/curl.h>
#include <string>
static size_t writeCb(char* ptr, size_t size, size_t nmemb, void* userdata) {
static_cast<std::string*>(userdata)->append(ptr, size * nmemb);
return size * nmemb;
}
std::string fetchRendered(const std::string& url, const char* apiKey) {
std::string payload =
"{\"actor\":\"unlocker.webunlocker\",\"input\":{"
"\"url\":\"" + url + "\",\"method\":\"GET\","
"\"redirect\":true,\"js_render\":true}}";
CURL* curl = curl_easy_init();
std::string resp;
ja
curl_slist* hdrs = nullptr;
hdrs = curl_slist_append(hdrs, "Content-Type: application/json");
hdrs = curl_slist_append(hdrs, (std::string("x-api-token: ") + apiKey).c_str());
curl_easy_setopt(curl, CURLOPT_URL, "https://api.scrapeless.com/api/v1/unlocker/request");
curl_easy_setopt(curl, CURLOPT_POST, 1L);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, payload.c_str());
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hdrs);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &resp);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 120L);
curl_easy_perform(curl);
curl_slist_free_all(hdrs);
curl_easy_cleanup(curl);
return resp;
}
js_render: true は荷重を支えるフラグで、APIにページのJavaScriptを実行して完了したDOMを返すよう指示する。クライアントサイドでコンテンツを生成するサイトが実際のマークアップとして戻ってくる。
無料プランでAPIキーを取得するには: app.scrapeless.com
ステップ2 — HTMLを封筒から取り出す
APIは {"code":200,"data":"<html>…} を返し、 data にJSONエスケープされたHTMLが含まれている。小さなエクストラクタがそのフィールドをデコードする — この形には重いJSONライブラリは必要ない:
cpp
std::string jsonGetData(const std::string& body) {
const std::string key = "\"data\":\"";
size_t i = body.find(key);
if (i == std::string::npos) return "";
i += key.size();
std::string out;
for (; i < body.size(); ++i) {
char c = body[i];
if (c == '\\') { // エスケープ解除 \" \\ \n \r \t \uXXXX
char n = body[++i];
if (n == 'n') out += '\n';
else if (n == 'r') out += '\r';
else if (n == 't') out += '\t';
else if (n == 'u') { out += (char)std::stoi(body.substr(i + 1, 4), nullptr, 16); i += 4; }
else out += n;
} else if (c == '"') break;
else out += c;
}
return out;
}
(多くのフィールドタイプを扱う生産環境では、実際のJSONライブラリをリンクすること — しかし、この封筒に対しては上記のエクストラクタで十分。)
ステップ3 — libxml2とXPathで解析
さあ、速い部分だ。libxml2がHTMLを解析し、XPathが欲しいノードを正確に選び出す。ここではカタログページから製品タイトルを抽出する:
cpp
#include <libxml/HTMLparser.h>
#include <libxml/xpath.h>
#include <iostream>
void extractTitles(const std::string& html) {
htmlDocPtr doc = htmlReadMemory(html.c_str(), html.size(), nullptr, nullptr,
HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING);
xmlXPathContextPtr ctx = xmlXPathNewContext(doc);
xmlXPathObjectPtr r = xmlXPathEvalExpression(
(const xmlChar*)"//article[@class='product_pod']//h3/a/@title", ctx);
int n = r->nodesetval ? r->nodesetval->nodeNr : 0;
std::cout << "books=" << n << "\n";
for (int i = 0; i < n; ++i) {
xmlChar* t = xmlNodeGetContent(r->nodesetval->nodeTab[i]);
std::cout << t << "\n";
xmlFree(t);
}
xmlXPathFreeObject(r);
xmlXPathFreeContext(ctx);
xmlFreeDoc(doc);
}
ステップ4 — コンパイルして実行
両方のライブラリをリンクし、コンパイラをlibxml2のヘッダーに向ける:
bash
g++ scraper.cpp -o scraper -I/usr/include/libxml2 -lcurl -lxml2
./scraper
JavaScriptでレンダリングされたカタログページへのライブ実行から返された結果は:
text
http_status=200 html_bytes=51295 books=20
first_book=A Light in the Attic
これが全体のループだ:1回のHTTPS POSTが出て、51 KBのレンダリングされたHTMLが帰ってきて、libxml2で解析された20のタイトル — ブラウザを埋め込むことも、プロキシプールも、バイナリにボット対策コードを入れることもない。
戻ってくるもの
APIの封筒は小さく、予測可能だ:
json
{
"code": 200,
"data": "<html>...レンダリングされたDOM...</html>"
}
// ライブコールの実際の形。dataにはJSONエスケープされたレンダリングHTMLが保持されている(確認された実行で51,295バイト)。
いくつかの率直な観察:
js_render: trueは遅延をもたらすがコンテンツを得る。 静的ページではオフにして早く進め、ページが空白のときはオンにする。- 複雑なレスポンスには実際のJSONライブラリを使用する。 手動で作成したエクストラクタは単一の
dataフィールドを扱うが、より豊かな内容には適切なパーサーが必要。 - XPathは文字列一致より優れている。 libxml2のXPathは手書きのHTMLスキャンよりも速くて堅牢だ。
- libxml2のオブジェクトを解放する。
xmlFreeDoc、xmlXPathFreeContext、xmlXPathFreeObject— C++はあなたのためにそれを行ってはくれない。
結論:解析にはC++、取得にはAPIを使う
パフォーマンスが重要なC++スクレイピングは、C++が得意とする部分—高速パース—を利用し、ネイティブに構築するのが難しい部分、つまりJavaScriptの実行、プロキシ、そしてボット対策をレンダリングAPIが担うときに最も効果的です。1つのHTTPS POSTにはlibcurl、XPath抽出にはlibxml2を使用すると、全体のクライアントは、2つの馴染み深いライブラリと1行の`g++`で構成されています。他の言語で同じフェッチ後のパースの分割については、[Scrapling + Scrapelessガイド](https://www.scrapeless.com/ja/blog/scrapling-scrapeless-web-scraping-2026)を参照してください。[Universal Scraping API製品ページ](https://www.scrapeless.com/ja/product/universal-scraping-api?utm_source=website&utm_medium=blog&utm_campaign=universalscrapingapi&utm_term=cpp-web-scraping)と[ドキュメント](https://docs.scrapeless.com)がAPIの全貌をカバーしています。ページが必要なときにはサーバーサイドでレンダリングし、XPathでパースし、libxml2オブジェクトを解放してください。
---
## AI駆動のデータパイプライン構築の準備はできましたか?
ネイティブスクレイパーを構築している開発者とつながるために、無料プランを請求し、私たちのコミュニティに参加してください:[Discord](https://discord.gg/VU2vtbq7Q2) · [Telegram](https://t.me/scrapeless)。
[app.scrapeless.com](https://app.scrapeless.com/passport/login/?utm_source=website&utm_medium=blog&utm_campaign=universalscrapingapi&utm_term=cpp-web-scraping)にサインアップして、無料のランタイムを利用し、上記のプログラムをC++パイプラインが必要とするサイトとセレクタに適応させてください。スケールについては[料金表](https://www.scrapeless.com/ja/pricing?utm_source=website&utm_medium=blog&utm_campaign=universalscrapingapi&utm_term=cpp-web-scraping)を参照してください。
---
## FAQ
**Q: なぜlibcurlを直接使用して、純粋なC++で全体のスクレイパーを書くのではないのですか?**
単純で保護されていないページには可能です。ただし、ターゲットがJavaScriptレンダリング、住宅IP、またはボット対策を必要とする瞬間には、それをC++で再現するのは大規模なプロジェクトになります。フェッチをレンダリングAPIにオフロードすることで、バイナリを小さく保つことができます。
**Q: C++プログラムにヘッドレスブラウザを埋め込む必要がありますか?**
いいえ。`js_render: true`はサーバーサイドでJavaScriptを実行し、完成したDOMを返すため、C++コードはHTMLをパースするだけで済みます。
**Q: libxml2は適切なパーサーですか?**
スケールでのHTMLに対しては、はい—高速で成熟しており、完全なXPathサポートがあります。HTTPサイドに関しては、CPRまたはBoost.Beastを使用してlibcurlを置き換えることもできますが、パースにはlibxml2が標準の選択肢です。
**Q: 大きな依存性なしでJSONレスポンスを処理するにはどうすればよいですか?**
ここで示されている単一の`data`フィールドには、小さな抽出器で問題ありません。よりリッチなレスポンスには、手動でパースするのではなく、軽量のJSONライブラリをリンクしてください。
**Q: プロキシを管理する必要がありますか?**
いいえ。APIは住宅プロキシ経由で出力されるため、URLを渡すだけでHTMLが返ってきます。
**Q: libxml2でメモリリークを避けるにはどうすればよいですか?**
常に割り当てたものを解放してください:`xmlXPathFreeObject`、`xmlXPathFreeContext`、および`xmlFreeDoc`。libxml2は手動のメモリ管理を使用しています。
Scrapelessでは、適用される法律、規制、およびWebサイトのプライバシーポリシーを厳密に遵守しながら、公開されているデータのみにアクセスします。 このブログのコンテンツは、デモンストレーションのみを目的としており、違法または侵害の活動は含まれません。 このブログまたはサードパーティのリンクからの情報の使用に対するすべての責任を保証せず、放棄します。 スクレイピング活動に従事する前に、法律顧問に相談し、ターゲットウェブサイトの利用規約を確認するか、必要な許可を取得してください。



