Javaベースの分散クローラーシステムの構築方法を段階的に説明します

Javaベースの分散クローラーシステムの構築方法を段階的に説明します

[51CTO.com からのオリジナル記事] クローラー フレームワークを使用せずに、さまざまなソースから学び、MySQL、HBase などのさまざまな場所にデータを保存できる分散クローラー システムを実装しようとしました。

このシステムはインターフェース指向のコーディング思想に基づいて開発されているため、一定のスケーラビリティを備えています。興味のある友人は、コードを見るだけでその設計のアイデアを理解できます。

コードはまだ多くの場所で密接に結合されていますが、ある程度の時間と労力を費やせば、その大部分を抽出して構成することができます。

時間の制約により、JD.com と Suning.com の 2 つの Web サイトのクローラーのみを作成しました。ただし、さまざまな Web サイトに対してクローラーのランダム スケジュールを実装することは完全に可能です。コード構造から判断すると、Gome、Tmall などの商品クローラーの作成は難しくありませんが、ある程度の時間と労力がかかると予想されます。

なぜなら、Web ページ上のデータを解析するとき、たとえば Suning.com 製品の価格をクロールしていたとき、価格は非同期的に取得され、その API はデジタルの組み合わせの長い文字列だったからです。パターンを発見するのに数時間かかりました。もちろん、私には経験不足だったことを認めなければなりません。

基本的なデータ クロールに加えて、このシステムの設計では次の問題に重点を置いています。

  • 配布を実現するにはどうすればいいですか?同じプログラムをパッケージ化して異なるノードに配布して実行しても、全体的なデータ クロールには影響しません。
  • URL ランダム ループ スケジューリングを実装するにはどうすればよいでしょうか?核となるのは、異なる *** ドメイン名をランダム化することです。
  • シード URL を URL リポジトリに定期的に追加するにはどうすればよいですか?クローラーシステムを停止させない目的を達成するため。
  • クローラー ノード プログラムを監視し、電子メール アラートを送信する方法は?
  • ランダム IP プロキシ ライブラリを実装するにはどうすればよいでしょうか?目的はポイント 2 と多少似ており、どちらもクローラー対策です。

以下ではこのシステムの基本について紹介します。コードには非常に詳細なコメントがあります。興味のある友人はコードを参照できます。最後に、クロール時のデータ分析をいくつか紹介します。

また、このクローラー システムは Java ベースで実装されていますが、言語自体が最も重要なわけではないことにも注意してください。興味のある方は、Python で実装してみてください。

分散クローラーシステムアーキテクチャ

システム全体のアーキテクチャは次のとおりです。

上記のアーキテクチャからわかるように、システム全体は主に 3 つの部分に分かれています。

  • クローラーシステム
  • URLディスパッチシステム
  • 監視警報システム

クローラー システムは、データをクロールするために使用されます。システムは分散設計されているため、クローラー プログラム自体は異なるサーバー ノードで実行できます。

URL ディスパッチ システムの中核は URL ウェアハウスにあります。いわゆる URL ウェアハウスは、実際には Redis を使用してクロールする必要がある URL のリストを保存し、URL ディスパッチャーの特定の戦略に従ってその中の URL を消費します。この観点から見ると、URL リポジトリは実際には URL キューです。

監視アラームシステムは主にクローラーノードを監視します。並行して実行されるクローラー ノードの 1 つに障害が発生しても、全体的なデータ クロール自体には影響しません (クローラーの速度が低下するだけです)。ただし、ノード障害を受動的に検出するのではなく、能動的に通知を受信することを期待しています。

以下では、上記の 3 つの側面に焦点を当て、いくつかのコード スニペットを組み合わせて、システム全体の設計アイデアの基本的な紹介を行います。

クローラーシステム

クローラー システムは独立して実行されるプロセスです。クローラー システムを jar パッケージにパッケージ化し、実行のためにさまざまなノードに配布します。このように、データの並列クロールにより、クローラーの効率が向上します。 (注: ZooKeeper モニタリングはモニタリング アラーム システムに属し、URL ディスパッチャは URL ディスパッチ システムに属します)

ランダム IP プロキシ

ランダム IP プロキシを追加する主な目的は、アンチクローラーに対抗することです。したがって、IP プロキシ ライブラリがあり、http クライアントの構築時にさまざまなプロキシをランダムに使用できる場合、アンチクローラーとの戦いに非常に役立ちます。

システムで IP プロキシ ライブラリを使用するには、まず使用可能なプロキシ アドレス情報をテキスト ファイルに追加する必要があります。

  1. #IPプロキシリポジトリ.txt
  2. 58.60.255.104:8118
  3. 219.135.164.245:3128
  4. 27.44.171.27:9999
  5. 219.135.164.245:3128
  6. 58.60.255.104:8118
  7. 58.252.6.165:9000
  8. ......

上記のプロキシ IP は Xici Proxy から取得したプロキシ IP であり、利用できない可能性があることに注意してください。プロキシ IP をまとめて購入するためにお金を費やすことをお勧めします。これにより、プロキシ IP を見つけるための時間と労力を大幅に節約できます。

次に、http クライアントを構築するツール クラスで、ツール クラスが初めて使用されるときに、これらのプロキシ IP がメモリにロードされ、Java HashMap にロードされます。

  1. //IP アドレス プロキシ ライブラリ マップ
  2. プライベート静的Map<String, Integer > IPProxyRepository = new HashMap<>();
  3. プライベート静的String[] keysArray = null ; // keysArray はランダムなプロキシオブジェクトを生成するために使用されます
  4.  
  5. /**
  6. * 初めて使用するときに、静的コードブロックを使用して IP プロキシライブラリをセットロードします。
  7. */
  8. 静的{
  9. 入力ストリーム= HttpUtil.class.getClassLoader().getResourceAsStream( "IPProxyRepository.txt" ); // プロキシIPを含むテキストをロードする
  10. //バッファストリームオブジェクトを構築する
  11. InputStreamReader isr = 新しい InputStreamReader( in );
  12. BufferedReader bfr = 新しい BufferedReader(isr);
  13. 文字列行 = null ;
  14. 試す {
  15. // 各行をループしてマップに追加します
  16. ((line = bfr.readLine()) != null ) の間 {
  17. 文字列[]を分割 = line.split( ":" ); // 区切り文字として : を使用します。つまり、テキスト内のデータ形式は 192.168.1.1:4893 になります。
  18. 文字列ホスト = split[0];
  19. intポート = Integer .valueOf(split[1]);
  20. IPProxyRepository.put(ホスト、ポート);
  21. }
  22. <String>を設定します。keys = IPProxyRepository.keySet();
  23. keysArray = keys.toArray(新しい文字列[ keys.size ()]); // keysArray はランダムなプロキシオブジェクトを生成するために使用されます
  24. } キャッチ (IOException e) {
  25. e.printStackTrace();
  26. }
  27.  
  28. }

その後、http クライアントを構築するたびに、まずマップをチェックしてプロキシ IP があるかどうかを確認します。はい、そうであればそれを使用してください。そうでない場合は、プロキシを使用しないでください。

  1. CloseableHttpClient httpClient = null ;
  2. HttpHost プロキシ = null ;
  3. if (IPProxyRepository.size () > 0) { // IPプロキシアドレスライブラリが空でない場合は、プロキシを設定します
  4. プロキシ = getRandomProxy();
  5. プロキシサーバに HTTP クライアントをインストールします。 // httpclient オブジェクトを作成する
  6. }それ以外{
  7. カスタムビルド// httpclient オブジェクトを作成する
  8. }
  9. HttpGet リクエスト = new HttpGet(url); // http getリクエストを構築
  10. ......

ランダム プロキシ オブジェクトは、次のメソッドを使用して生成されます。

  1. /**
  2. * プロキシオブジェクトをランダムに返す
  3. *
  4. * @戻る 
  5. */
  6. 公共 静的HttpHost getRandomProxy() {
  7. // ホスト:ポートをランダムに取得し、プロキシオブジェクトを構築します
  8. ランダム random = new Random();
  9. 文字列ホスト = keysArray[random.nextInt(keysArray.length)];
  10. intポート = IPProxyRepository.get(ホスト);
  11. HttpHost プロキシ = 新しい HttpHost(ホスト、ポート); // httpプロキシを設定する
  12. プロキシを返します
  13. }

このように、上記の設計を通じて、ランダム IP プロキシの機能が基本的に実現されます。もちろん、改善できる点はまだたくさんあります。

たとえば、この IP プロキシを使用しているときにリクエストが失敗した場合、この状況を記録できますか?一定回数を超えるとプロキシライブラリから削除され、開発者や運用保守担当者が参照できるようにログが生成されます。これは完全に実行可能ですが、この手順は実行しません。

ウェブダウンローダー

Web ページ ダウンローダーは、Web ページからデータをダウンロードするために使用され、主に次のインターフェイスに基づいて開発されています。

  1. /**
  2. * Webデータのダウンロード
  3. */
  4. パブリックインターフェースIDownload {
  5. /**
  6. * 指定されたURLのWebページデータをダウンロードします
  7. * @param URL
  8. * @戻る 
  9. */
  10. パブリックページダウンロード(文字列URL);
  11. }

これに基づいて、システムには 1 つの http get ダウンローダーのみが実装されていますが、必要な機能も実行できます。

  1. /**
  2. * データダウンロード実装クラス
  3. */
  4. パブリッククラスHttpGetDownloadImplはIDownloadを実装します{
  5.  
  6. @オーバーライド
  7. パブリックページダウンロード(文字列URL) {
  8. ページ page = new Page();
  9. 文字列コンテンツ = HttpUtil.getHttpContent(url); // ウェブページのデータを取得する
  10. ページURLを設定します。
  11. ページのコンテンツを設定します。
  12. ページに戻る;
  13. }
  14. }

ウェブページパーサー

Web ページ パーサーは、ダウンロードされた Web ページから目的のデータを解析し、それをオブジェクトに保存します。その後、データ ストレージ デバイスによってさらに処理され、さまざまな永続リポジトリに保存されます。以下のインターフェースに基づいて開発されています。

  1. /**
  2. * Webページデータ分析
  3. */
  4. パブリックインターフェース IParser {
  5. public void parser(ページ page);
  6. }

Web ページ パーサーは、システム全体の開発において非常に重要なコンポーネントです。機能は複雑ではありませんが、多くのコードが含まれています。ショッピングモールや製品が異なれば、対応するパーサーも異なる場合があります。

したがって、JD.com が使用する Web ページ テンプレートは Suning.com が使用するものとは明らかに異なり、Tmall が使用する Web ページ テンプレートは JD.com が使用するものとは明らかに異なるため、特別なショッピング モール向けの製品を開発する必要があります。

したがって、これは完全にあなた自身の開発ニーズに基づいています。ただ、パーサー開発の過程で、重複するコードがいくつか見つかることがあります。この時点で、これらのコードを抽象化し、ツール クラスを開発できます。

現在、システムは JD.com と Suning.com の携帯電話製品データをクロールするため、次の 2 つの実装クラスが記述されています。

  1. /**
  2. * JD製品の実装クラスを分析する
  3. */
  4. パブリッククラス JDHtmlParserImpl は IParser を実装します {
  5. ......
  6. }
  7.  
  8. /**
  9. * Suning.com ウェブサイト分析
  10. */
  11. パブリッククラスSNHtmlParserImplはIParserを実装します{
  12. ......
  13. }

データストレージ

データ ストレージ デバイスは主に、Web ページ パーサーによって解析されたデータ オブジェクトをさまざまなテーブルに保存します。今回クロールした携帯電話製品のデータ オブジェクトは次の Page オブジェクトです。

  1. /**
  2. * ウェブページオブジェクト(主にウェブページのコンテンツと製品データを含む)
  3. */
  4. パブリッククラス Page {
  5. プライベート文字列コンテンツ; //ウェブページコンテンツ
  6.  
  7. プライベート文字列ID; // 製品ID
  8. プライベート文字列ソース; // 製品ソース
  9. プライベートStringブランド。 // 製品ブランド
  10. プライベート文字列タイトル; // 製品タイトル
  11. 非公開浮動価格; // 製品価格
  12. プライベートintコメント数; // 製品コメントの数
  13. プライベート文字列 URL; // 製品アドレス
  14. プライベート文字列imgUrl; // 商品画像のURL
  15. プライベート文字列パラメータ; // 製品仕様パラメータ
  16.  
  17. プライベートList<String> urls = new ArrayList<>(); // リストページを解析するときに解析された製品 URL を格納するコンテナ
  18. }

同様に、MySQL では、テーブル データ構造は次のようになります。

  1. ----------------------------  
  2. -- 電話のテーブル構造 
  3. ----------------------------  
  4. 落とす テーブルが存在する場合、`phone`;
  5. 作成する テーブル`電話` (
  6. `id` varchar (30)文字  SETアームスシー8 NOT   NULLコメント'製品ID'
  7. `source` varchar (30) ではない  NULL COMMENT '製品ソース、例えば jd suning gome など。'
  8. `brand` varchar (30)デフォルト  NULLコメント「携帯電話ブランド」
  9. `title` varchar (255)デフォルト  NULL COMMENT '製品ページのモバイルタイトル'
  10. `price` float (10,2)デフォルト  NULL COMMENT '携帯電話の価格'
  11. `comment_count` varchar (30)デフォルト  NULL COMMENT '携帯電話のコメント'
  12. `url` varchar (500)デフォルト  NULL COMMENT '携帯電話の詳細住所'
  13. `img_url` varchar (500)デフォルト  NULL COMMENT '画像アドレス'
  14. `params` テキスト COMMENT '携帯電話パラメータ、json 形式で保存'
  15. 主要な キー(`id`,`source`)
  16. ) エンジン=InnoDBデフォルト文字セット=utf8;

HBase のテーブル構造は次のとおりです。

  1. ## cf1 ストア ID ソース 価格 コメント ブランド URL
  2. ## cf2はタイトルパラメータimgUrlを保存します
  3. 作成する  「電話」 「cf1」 「cf2」  
  4.  
  5. ## HBaseシェルで作成されたテーブルを表示する
  6. hbase(main):135:0>説明  '電話'  
  7. テーブルフォン有効です
  8. 電話
  9. ファミリーの説明
  10. { NAME => 'cf1' 、BLOOMFILTER => 'ROW' 、VERSIONS => '1' 、IN_MEMORY => 'false' 、KEEP_DELETED_CELLS => 'FALSE' 、DATA_BLOCK
  11. _ENCODING => 'NONE' 、TTL => 'FOREVER' 、COMPRESSION => 'NONE' 、MIN_VERSIONS => '0' 、BLOCKCACHE => 'true' 、BLOCKSIZE =>
  12. '65536' 、レプリケーションスコープ => '0' }
  13. { NAME => 'cf2' 、BLOOMFILTER => 'ROW' 、VERSIONS => '1' 、IN_MEMORY => 'false' 、KEEP_DELETED_CELLS => 'FALSE' 、DATA_BLOCK
  14. _ENCODING => 'NONE' 、TTL => 'FOREVER' 、COMPRESSION => 'NONE' 、MIN_VERSIONS => '0' 、BLOCKCACHE => 'true' 、BLOCKSIZE =>
  15. '65536' 、レプリケーションスコープ => '0' }
  16. 0.0350秒2行

つまり、HBase に cf1 と cf2 という 2 つの列ファミリが作成されます。 cf1 は、ID、ソース、価格、コメント、ブランド、URL フィールド情報を保存するために使用されます。 cf2 は、タイトル、パラメータ、imgUrl フィールド情報を保存するために使用されます。

異なるデータ ストアでは異なる実装クラスが使用されますが、それらはすべて同じインターフェイスに基づいて開発されます。

  1. /**
  2. * 製品データの保存
  3. */
  4. パブリックインターフェース IStore {
  5. パブリックvoid ストア (ページ page);
  6. }

そして、これを基にMySQLストレージ実装クラス、HBaseストレージ実装クラス、コンソール出力実装クラスを開発しました。たとえば、MySQL ストレージ実装クラスは、実際には単純なデータ挿入ステートメントです。

  1. /**
  2. * dbcデータベース接続プールを使用してmysqlテーブルにデータを書き込みます
  3. */
  4. パブリッククラスMySQLStoreImplはIStoreを実装します{
  5. プライベート QueryRunner queryRunner = new QueryRunner(DBCPUtil.getDataSource());
  6.  
  7. @オーバーライド
  8. パブリックvoid store(ページ ページ) {
  9. 文字列 sql = "insert into phone(id, source, brand, title, price, comment_count, url, img_url, params) values(?, ?, ?, ?, ?, ?, ?, ?, ?)" ;
  10. 試す {
  11. queryRunner.update (sql、page.getId()、
  12. ページ.getSource()、
  13. page.getBrand(),
  14. ページ.getTitle(),
  15. page.getPrice(),
  16. page.getCommentCount(),
  17. ページ.getUrl()、
  18. ページ.getImgUrl(),
  19. ページ.getParams());
  20. } キャッチ (SQLException e) {
  21. e.printStackTrace();
  22. }
  23. }
  24. }

HBase ストレージ実装クラスは、HBase Java API のよく使用される挿入ステートメント コードです。

  1. ......
  2. // cf1:価格
  3. Put 価格Put = 新しい Put(rowKey);
  4. // nullかどうかを確認する必要があります。そうでない場合は、null ポインタ例外が発生します。
  5. pricePut.addColumn(cf1, "price" .getBytes(), page.getPrice() != null ? String.valueOf(page.getPrice()).getBytes() : "" .getBytes());
  6. puts.add (pricePut);
  7. // cf1:コメント
  8. コメントを入力します。Put = new Put(rowKey);
  9. commentPut.addColumn(cf1, "コメント" .getBytes(), page.getCommentCount() != null ? String.valueOf(page.getCommentCount()).getBytes() : "" .getBytes());
  10. puts.add (コメントPut);
  11. // cf1:ブランド
  12. ブランドPut = new Put(rowKey);
  13. brandPut.addColumn(cf1, "brand" .getBytes(), page.getBrand() != null ? page.getBrand().getBytes() : "" .getBytes());
  14. puts.add (brandPut);
  15. ......

もちろん、データを保存する場所については、クローラーを初期化するときに手動で選択できます。

  1. // 3. メモリを挿入する
  2. iSpider.setStore(新しい HBaseStoreImpl());

コードは、同時に複数の場所に保存できるようにはまだ書かれていません。現在のコード アーキテクチャによれば、これを実現するのは比較的簡単です。対応するコードを変更するだけです。

実際には、まずデータを MySQL に保存し、その後 Sqoop を介して HBase にインポートすることができます。詳しい操作については私が書いたSqoopの記事を参照してください。

データを HBase に保存する必要があることが確実な場合は、使用可能なクラスター環境があることを確認して、次の構成ドキュメントをクラスパスに追加することが重要です。

  1. コアサイト.xml
  2. hbase サイト.xml
  3. hdfs-サイト.xml

ビッグデータに興味のある学生はぜひ試してみてください。これまで使用したことがない場合は、MySQL ストレージを使用してください。クローラー プログラムを初期化するときにのみ、MySQL ストレージを挿入する必要があります。

  1. // 3. メモリを挿入する
  2. iSpider.setStore(新しい MySQLStoreImpl());

URLディスパッチシステム

URL スケジューリング システムは、クローラー システム全体の分散を実現するための架け橋であり、鍵となります。 URL スケジューリング システムを使用することで、クローラー システム全体がより効率的に (ストレージとして Redis を使用) URL をランダムに取得し、システム全体の分散を実現できます。

URL リポジトリ

アーキテクチャ図から、いわゆる URL ウェアハウスは Redis ウェアハウスに過ぎないことがわかります。つまり、システムでは URL アドレス リストを保存するために Redis が使用されています。

このようにして、プログラムが配布されることを保証できます。保存された URL が一意である限り、クローラー プログラムがいくつあっても、最終的に保存されるデータは一意であり、重複することはありません。

同時に、URL ウェアハウス内の URL アドレスを取得するための戦略はキューを通じて実装されます。これは、後で URL スケジューラの実装を通じて学習されます。

さらに、当社の URL リポジトリには、主に次のデータが保存されます。

シードURLリスト、Redisのデータ型はリストです

シード URL は永続的に保存されます。一定時間が経過すると、URL タイマーはシード URL を通じて URL を取得し、クローラーが使用する必要がある優先度の高い URL キューに挿入します。

これにより、クローラー プログラムの実行を終了せずに、継続的にデータをクロールできるようになります。

優先度の高いURLキュー、Redisデータ型が設定されている

高優先度 URL キューとは何ですか?実際には、リストの URL を保存するために使用されます。では、リスト URL とは何でしょうか?

簡単に言えば、リストには複数の製品が含まれます。 JD.com を例に、携帯電話リストを開きます。

このアドレスには、特定の製品の URL ではなく、クロールする必要がある複数のデータ (携帯電話製品) のリストが含まれています。

各高レベル URL を解析することで、多数の特定の製品 URL を取得できます。特定の製品 URL は低優先度 URL であり、低優先度 URL キューに保存されます。

このシステムを例にとると、保存されるデータは次のようになります。

  1. jd.com.higher
  2. --https://list.jd.com/list.html?cat=9987,653,655&page=1  
  3. ...
  4. suning.com.higher
  5. --https://list.suning.com/0-20006-0.html  
  6. ...

低優先度URLキュー、Redisデータ型が設定されている

優先度の低い URL は、実際には次の携帯電話製品のような特定の製品の URL です。

URL のデータをダウンロードして解析することで、必要なデータを取得できます。

このシステムを例にとると、保存されるデータは次のようになります。

  1. jd.com.lower  
  2. --https://item.jd.com/23545806622.html  
  3. ...
  4. suning.com.lower  
  5. --https://product.suning.com/0000000000/690128156.html  
  6. ...

URL ディスパッチャー

いわゆる URL ディスパッチャーは、URL ウェアハウス Java コードのディスパッチ戦略です。ただし、その核心はディスパッチにあるため、説明のために URL ディスパッチャ内に置かれます。現在、ディスパッチは次のインターフェースに基づいて開発されています。

  1. /**
  2. * URLリポジトリ
  3. * 主な機能:
  4. * 倉庫に URL を追加します (優先度の高いリスト、優先度の低い製品 URL)
  5. * 倉庫から URL を取得します (最初に優先度の高い URL を取得し、そうでない場合は優先度の低い URL を取得します)
  6. *
  7. */
  8. パブリックインターフェースIRepository {
  9.  
  10. /**
  11. * URLの取得方法
  12. * 倉庫から URL を取得します (最初に優先度の高い URL を取得し、そうでない場合は優先度の低い URL を取得します)
  13. * @戻る 
  14. */
  15. パブリック文字列 poll();
  16.  
  17. /**
  18. * 製品リストの URL を高優先度リストに追加します
  19. * @param 高URL
  20. */
  21. パブリックvoid offerHigher(String highUrl);
  22.  
  23. /**
  24. * 低優先度リストに商品URLを追加する
  25. * @param 低URL
  26. */
  27. パブリックvoid offerLower(String lowUrl);
  28.  
  29. }

URL リポジトリとして Redis をベースにした実装は次のとおりです。

  1. /**
  2. * Redis をベースにしたフルネットワーク クローラー。クローラー URL をランダムに取得します。
  3. *
  4. * Redis に URL を保存するために使用されるデータ構造は次のとおりです。
  5. * 1. クロールする必要があるドメイン名セット(ストレージデータ型が設定されており、最初にRedisに追加する必要があります)
  6. * 
  7. * スパイダー.ウェブサイト.ドメイン
  8. * 値(設定)
  9. * jd.com suning.com gome.com
  10. *キーは定数オブジェクト SpiderConstants.SPIDER_WEBSITE_DOMAINS_KEY から取得されます
  11. * 2. 各ドメイン名に対応する高優先度および低優先度の URL キュー (保存データ タイプはリストであり、クローラー プログラムがシード URL を解析した後に動的に追加されます)
  12. * 
  13. * jd.com.higher
  14. * jd.com.lower  
  15. * suning.com.higher
  16. * suning.com.lower  
  17. * gome.com.higher
  18. *下へ 
  19. * 値(リスト)
  20. * 解析する必要がある対応するURLリスト
  21. *キーは、定数 SpiderConstants.SPIDER_DOMAIN_HIGHER_SUFFIX または SpiderConstants.SPIDER_DOMAIN_LOWER_SUFFIX にランダムなドメイン名を追加することによって取得されます。
  22. * 3. シードURLのリスト
  23. * 
  24. * スパイダーシードのURL
  25. * 値(リスト)
  26. * クロールするデータのシードURL
  27. *キーは定数SpiderConstants.SPIDER_SEED_URLS_KEYから取得されます
  28. *
  29. * シード URL リスト内の URL は、URL スケジューラによって高優先度 URL キューと低優先度 URL キューに定期的に追加されます。
  30. */
  31. パブリッククラス RandomRedisRepositoryImpl は IRepository を実装します {
  32.  
  33. /**
  34. * 施工方法
  35. */
  36. パブリックRandomRedisRepositoryImpl() {
  37. 初期化();
  38. }
  39.  
  40. /**
  41. * 初期化方法、初期化時に、まずRedis内の高優先度と低優先度のURLキューをすべて削除します
  42. * そうでない場合、最後の URL キュー内の URL が消費されず、停止して次の実行を開始すると、URL ウェアハウスに重複した URL が存在することになります。
  43. */
  44. パブリックvoid init() {
  45. ジェディス jedis = JedisUtil.getJedis();
  46. <String> domains = jedis.smembers(SpiderConstants.SPIDER_WEBSITE_DOMAINS_KEY);を設定します
  47. 文字列higherUrlKey;
  48. 文字列lowerUrlKey;
  49. for (文字列ドメイン:ドメイン) {
  50. より高いUrlKey = ドメイン + SpiderConstants.SPIDER_DOMAIN_HIGHER_SUFFIX;
  51. lowerUrlKey = ドメイン + SpiderConstants.SPIDER_DOMAIN_LOWER_SUFFIX;
  52. jedis.del(higherUrlKey, lowerUrlKey);
  53. }
  54. JedisUtil.returnJedis(jedis);
  55. }
  56.  
  57. /**
  58. * キューから URL を取得します。現在の戦略は次のとおりです。
  59. * 1. 優先度の高いURLキューから最初に取得する
  60. * 2. 優先度の低いURLキューから取得する
  61. * 実際のシナリオでは、まずリストのURLを解析し、次に製品のURLを解析する必要があります。
  62. * ただし、分散マルチスレッド環境では、優先度の高いURLキューのどこかの時点で、これが完全に保証されるわけではないことに注意してください。
  63. * URL は消費されていますが、プログラムはまだ次の優先度の高い URL を解析中です。このとき、他のスレッドは高優先度キューの URL を確実に取得できなくなります。
  64. ※このとき、優先度の低いキューにあるURLが取得されます。これは分析を検討するときに特に重要です。
  65. * @戻る 
  66. */
  67. @オーバーライド
  68. パブリック文字列ポーリング(){
  69. //セットからランダムに***ドメイン名を取得します
  70. ジェディス jedis = JedisUtil.getJedis();
  71. 文字列 randomDomain = jedis.srandmember(SpiderConstants.SPIDER_WEBSITE_DOMAINS_KEY); // jd.com
  72. 文字列キー= randomDomain + SpiderConstants.SPIDER_DOMAIN_HIGHER_SUFFIX; // jd.com.higher
  73. 文字列 url = jedis.lpop(キー);
  74. if(url == null ) { // nullの場合は低優先度から取得する
  75. キー= randomDomain + SpiderConstants.SPIDER_DOMAIN_LOWER_SUFFIX; // jd.com より。より低い 
  76. url = jedis.lpop(キー);
  77. }
  78. JedisUtil.returnJedis(jedis);
  79. URLを返します
  80. }
  81.  
  82. /**
  83. * URLを高優先度URLキューに追加します
  84. * @param 高URL
  85. */
  86. @オーバーライド
  87. パブリックvoid offerHigher(String highUrl) {
  88. オファーUrl(highUrl、SpiderConstants.SPIDER_DOMAIN_HIGHER_SUFFIX);
  89. }
  90.  
  91. /**
  92. * 低優先度のURLキューにURLを追加する
  93. * @param 低URL
  94. */
  95. @オーバーライド
  96. パブリックvoid offerLower(String lowUrl) {
  97. 低いUrlを提供します。
  98. }
  99.  
  100. /**
  101. * offerHigher と offerLower から抽象化された URL を追加するための汎用メソッド
  102. * @param url 追加するURL
  103. * @param urlTypeSuffix URL タイプのサフィックス .higher または.lower  
  104. */
  105. パブリックvoid offerUrl(文字列 url、文字列 urlTypeSuffix) {
  106. ジェディス jedis = JedisUtil.getJedis();
  107. 文字列ドメイン = SpiderUtil.getTopDomain(url); // URL に対応するトップドメイン名を取得します (例: jd.com)
  108. 文字列キー= ドメイン + urlTypeSuffix; // jd.com.higher などのURL キューのキーを連結します。
  109. jedis.lpush(キー、 URL ); // URL を URL キューに追加します
  110. JedisUtil.returnJedis(jedis);
  111. }
  112. }

コード分​​析を通じて、核心は URL ウェアハウス (Redis) で URL をスケジュールする方法にあることもわかります。

URL タイマー

一定時間が経過すると、優先度の高い URL キューと優先度の低い URL キューの両方の URL が消費されます。

人間の介入を減らしながらプログラムがデータのクロールを継続できるようにするには、事前に Redis にシード URL を挿入し、URL タイマーがシード URL から URL を取り出して、定期的に高優先度 URL キューに格納するようにします。これにより、プログラムが一定間隔で中断することなくデータをクロールするという目的を達成できます。

URL が使用された後、データを継続的にクロールする必要があるかどうかは、個人のビジネス ニーズによって異なります。したがって、この手順は必須ではありませんが、このような操作も提供されます。

実際のところ、クロールする必要があるデータは定期的に更新されるからです。クロールするデータを定期的に更新したい場合、タイマーが非常に重要な役割を果たします。

ただし、ループ内でデータを繰り返しクロールする必要があると判断された場合は、メモリ実装の設計時に重複データの問題を考慮する必要があり、つまり重複データを更新する必要があることに注意してください。

私が設計したストレージデバイスには、現時点ではこの機能は含まれていません。興味のある友人は自分でそれを実装することができます。データを挿入する前に、データベースにデータが存在するかどうかを確認するだけで済みます。

注意すべきもう 1 つの点は、URL タイマーは独立したプロセスであり、個別に開始する必要があることです。

タイマーはQuartzに基づいて実装されています。そのジョブのコードは次のとおりです。

  1. /**
  2. * シードURLをURLウェアハウスから毎日定期的に取得し、優先度の高いリストに追加します
  3. */
  4. パブリッククラスUrlJobはJobを実装します{
  5.  
  6. // log4j ログ
  7. プライベート Logger ロガー = LoggerFactory.getLogger(UrlJob.class);
  8.  
  9. @オーバーライド
  10. パブリックvoid実行(JobExecutionContext コンテキスト) は JobExecutionException をスローします {
  11. /**
  12. * 1. 指定されたURLシードリポジトリからシードURLを取得します
  13. * 2. シードURLを高優先度リストに追加する
  14. */
  15. ジェディス jedis = JedisUtil.getJedis();
  16. <String> seedUrls = jedis.smembers(SpiderConstants.SPIDER_SEED_URLS_KEY);を設定します// spider.seed.urls Redis データ型は、シード URL の繰り返し追加を防ぐために設定されています
  17. (文字列 seedUrl : seedUrls) {
  18. 文字列ドメイン = SpiderUtil.getTopDomain(seedUrl); // *** シード URL のドメイン名
  19. jedis.sadd(ドメイン + SpiderConstants.SPIDER_DOMAIN_HIGHER_SUFFIX、seedUrl);
  20. logger.info( "シードを取得:{}" , seedUrl);
  21. }
  22. JedisUtil.returnJedis(jedis);
  23. // システム。 out .println( "スケジューラジョブテスト..." );
  24. }
  25.  
  26. }

スケジューラの実装は次のとおりです。

  1. /**
  2. * URLタイミングスケジューラ、シードURLをURL対応ウェアハウスに定期的に保存
  3. *
  4. * 業務規定:毎日午前1時10分にシードURLを倉庫に保存する
  5. */
  6. パブリッククラス UrlJobScheduler {
  7.  
  8. パブリックUrlJobScheduler() {
  9. 初期化();
  10. }
  11.  
  12. /**
  13. * スケジューラを初期化する
  14. */
  15. パブリックvoid init() {
  16. 試す {
  17. スケジューラ scheduler = StdSchedulerFactory.getDefaultScheduler();
  18.  
  19. // 次の開始メソッドが実行されない場合、タスクのスケジュールは開始されません
  20. スケジューラを開始します。
  21.  
  22. 文字列= "URL_SCHEDULER_JOB" ;
  23. 文字列グループ= "URL_SCHEDULER_JOB_GROUP" ;
  24. JobDetail jobDetail = new JobDetail(名前グループ、 UrlJob.class);
  25. 文字列 cronExpression = "0 10 1 * * ?" ;
  26. トリガー トリガー= 新しい CronTrigger(名前グループ、cronExpression);
  27.  
  28. //タスクをスケジュールする
  29. スケジューラ.scheduleJob(ジョブの詳細、トリガー);
  30.  
  31. } キャッチ (SchedulerException e) {
  32. e.printStackTrace();
  33. } キャッチ (ParseException e) {
  34. e.printStackTrace();
  35. }
  36. }
  37.  
  38. 公共 静的void main(String[] args) {
  39. URLJobScheduler urlJobScheduler = new UrlJobScheduler();
  40. urlJobScheduler.start();
  41. }
  42.  
  43. /**
  44. * スケジュールされたタスク
  45. * 指定された倉庫からシードURLを毎日定期的に取得し、優先度の高いURLリストに保存する必要があるため
  46. * これは中断されないプロセスなので、止めることはできません
  47. */
  48. プライベートvoid start() {
  49. )の間{
  50.  
  51. }
  52. }
  53. }

監視警報システム

監視アラーム システムの追加は、実際にはクローラー プログラムが継続的に実行されている可能性があるため、ユーザーがノード ダウンタイムを受動的に検出するのではなく、積極的に検出できるようにすることが主な目的です。

また、クローラー プログラムを複数のノードに展開するため、ノードを監視し、問題が発生したときにそれを適時に検出して修正する必要があります。監視アラーム システムは独立したプロセスであり、別途開始する必要があることに注意してください。

根拠

まず、ZooKeeper に /ispider ノードを作成する必要があります。

  1. [zk: localhost:2181(接続済み) 1] /ispider を作成しますispider
  2. /ispider を作成しました

監視およびアラーム システムの開発は、主に ZooKeeper の実装に依存しています。監視プログラムは、ZooKeeper の下のノード ディレクトリを監視します。

  1. [zk: localhost:2181(接続済み) 0] ls /ispider  
  2. []

クローラー プログラムが起動すると、このノード ディレクトリの下に一時ノード ディレクトリが登録されます。

  1. [zk: localhost:2181(接続済み) 0] ls /ispider  
  2. [192.168.43.166]

ノードがクラッシュすると、一時ノード ディレクトリは ZooKeeper によって削除されます。

  1. [zk: localhost:2181(接続済み) 0] ls /ispider
  2.  
  3. []

同時に、ノード ディレクトリ /ispider を監視しているため、ZooKeeper がその下のノード ディレクトリを削除すると (またはノード ディレクトリを追加すると)、ZooKeeper は監視プログラムに通知を送信します。

つまり、監視プログラムはコールバックを取得し、コールバック プログラムでアラーム システム アクションを実行して、監視アラーム機能を完了します。

ZooKeeper Java API 使用上の注意

ZooKeeper のネイティブ Java API を使用できます。私が作成した別の RPC フレームワークのネイティブ API を使用しました (基礎レイヤーはリモート通信を実現するために Netty に基づいています)。

ただし、コードは明らかにはるかに複雑になるため、使いやすくするために ZooKeeper についてさらに学ぶ必要があります。

そのため、開発の難易度を軽減するために、ここではサードパーティのカプセル化された API、つまり curator を使用して ZooKeeper クライアント プログラムを開発します。

クローラーシステムZooKeeper登録

クローラー システムを起動すると、プログラムは ZooKeeper クライアントを起動し、独自のノード情報 (主に IP アドレス) を ZooKeeper に登録します。

また、/ispider ノード ディレクトリに、クローラー プログラムが配置されているノードの IP アドレスにちなんで名付けられたノード (/ispider/192.168.43.116 など) を作成します。実装コードは次のとおりです。

  1. /**
  2. * 登録 zk
  3. */
  4. プライベートvoidレジスタZK() {
  5. 文字列 zkStr = "uplooking01:2181、uplooking02:2181、uplooking03:2181" ;
  6. intベーススリープ時間Ms = 1000;
  7. 最大再試行回数 = 3;
  8. 再試行ポリシー retryPolicy = 新しい ExponentialBackoffRetry(baseSleepTimeMs、maxRetries);
  9. CuratorFramework キュレーター = CuratorFrameworkFactory.newClient(zkStr, retryPolicy);
  10. キュレーターを起動します。
  11. 文字列 ip = null ;
  12. 試す {
  13. // zkの特定のディレクトリに登録する ノードを書き込む ノードを作成する
  14. ip = InetAddress.getLocalHost().getHostAddress();
  15. curator.create ().withMode(CreateMode.EPHEMERAL).forPath( "/ispider/" + ip, ip.getBytes());
  16. } キャッチ (UnknownHostException e) {
  17. e.printStackTrace();
  18. } キャッチ (例外 e) {
  19. e.printStackTrace();
  20. }
  21. }

作成したノードは一時的なノードであることに注意してください。監視・警報機能を実現するには、一時ノードにする必要があります。

モニタリングプログラム

まず、ZooKeeper でノード ディレクトリを監視する必要があります。私たちのシステムでは、ノード ディレクトリ /ispider を監視するように設計されています。

  1. パブリックスパイダーモニタータスク() {
  2. 文字列 zkStr = "uplooking01:2181、uplooking02:2181、uplooking03:2181" ;
  3. intベーススリープ時間Ms = 1000;
  4. 最大再試行回数 = 3;
  5. 再試行ポリシー retryPolicy = 新しい ExponentialBackoffRetry(baseSleepTimeMs、maxRetries);
  6. curator = CuratorFrameworkFactory.newClient(zkStr, retryPolicy);
  7. キュレーターを起動します。
  8. 試す {
  9. previousNodes = curator.getChildren().usingWatcher(this).forPath( "/ispider" );
  10. } キャッチ (例外 e) {
  11. e.printStackTrace();
  12. }
  13. }

上記では、通知を受信するためのコールバック プログラムである ZooKeeper のウォッチャーを登録しています。このプログラムでは、アラーム ロジックが実行されます。

  1. /**
  2. * このメソッドは、監視対象のzkに対応するディレクトリが変更されたときに呼び出されます。
  3. * 現在の***ノードステータスを取得し、***ノードステータスを初期または以前のノードステータスと比較すると、ノード変更の原因がわかります。
  4. * @param イベント
  5. */
  6. @オーバーライド
  7. パブリックvoid プロセス(WatchedEvent イベント) {
  8. 試す {
  9. リスト<String> currentNodes = curator.getChildren().usingWatcher(this).forPath( "/ispider" );
  10. // HashSet<String> previousNodesSet = new HashSet<>(previousNodes);
  11. if( currentNodes.size () > previousNodes.size ()) { // ***のノードサービスが以前のノードサービス数を超え、新しいノードが追加されます
  12. (文字列ノード: currentNodes) {
  13. if(!previousNodes. contains (ノード)) {
  14. // 現在のノードは新しく追加されたノードです
  15. logger.info( "----新しいクローラーノード {} が追加されました" , node);
  16. }
  17. }
  18. } else if(currentNodes.size ( ) < previousNodes.size ()) { //ノードがダウンしています。アラートメールまたは SMS を送信します。
  19. (文字列ノード: previousNodes) {
  20. if(!currentNodes. contains (ノード)) {
  21. // 現在のノードがダウンしているため、メールを送信する必要があります
  22. logger.info( "----クローラーノード {} がクラッシュしました" , node);
  23. MailUtil.sendMail( "クローラーノードがダウンしています。クローラーノードのステータスを手動で確認してください。ノード情報は次のとおりです: " , node);
  24. }
  25. }
  26. } // ドロップされたアイテムと新しく追加されたアイテムの数は完全に同じです。この状況は上記に含まれていません。興味のある友達は、この特別な状況を含む監視を直接実装できます
  27. 以前のnodes = currentNodes; //前のノードリストを更新して最新のノードリストになる
  28. } キャッチ (例外 e) {
  29. e.printStackTrace();
  30. }
  31. //ネイティブAPIは再度監視する必要があります。各監視は1回しか有効になるためです。
  32. //しかし、これはキュレーターAPIを使用するときは必要ありません
  33. }

もちろん、ノードがダウンしているかどうかを判断するための上記のロジックにはまだいくつかの問題があります。上記のロジックによれば、新しいノードを追加してノードを削除するイベントが同時に発生する場合、判断することはできません。したがって、より精度が必要な場合は、上記のプログラムコードを変更できます。

メール送信モジュール

テンプレートコードを使用できますが、使用する場合は、独自のメールアドレスを送信者として使用してください。

以下は、クローラーノードがハングアップしたときに受け取ったメールです。

実際、SMSサービスを購入すると、SMS APIを介して携帯電話にテキストメッセージを送信することもできます。

練習:jd.comとSuning.comのモバイル製品データをクロールします

このシステムを導入したときに述べたように、私はJD.comとSuning.comのWebページパーサーのみを書いたので、次のステップはネットワーク全体の携帯電話製品データをクロールすることです。

環境説明

RedisおよびZookeeperサービスが利用できるようにする必要があります。さらに、HBaseを使用してデータを保存する必要がある場合は、Hadoopクラスター内のHBaseが利用可能であり、関連する構成ファイルがCrawlerプログラムのClassPathに追加されていることを確認する必要があります。

注意すべきもう1つのことは、URLタイマーと監視アラームシステムが個別のプロセスとして実行され、オプションでもあることです。

クローラーの結果

データを2回クロールし、それをそれぞれMySQLとHBaseに保存しようとしましたが、次のデータが与えられました。

mysqlに保存します

  1. mysql> select  電話から(*)
  2. + ----------+  
  3. | count (*)|
  4. + ----------+  
  5. | 12052 |
  6. + ----------+  
  7. 1 セット 
  8.  
  9. mysql> select   count (*) source = ' jd.com ' ;
  10. + ----------+  
  11. | count (*)|
  12. + ----------+  
  13. | 9578 |
  14. + ----------+  
  15. 1 セット 
  16.  
  17. mysql> select   count (*) source = 'サンニング
  18. .com ';
  19. + ----------+  
  20. | count (*)|
  21. + ----------+  
  22. | 2474 |
  23. + ----------+  
  24. 1 セット 

視覚化ツールでデータを表示します。

hbaseに保存します

  1. hbase(メイン):225:0* count   '電話'  
  2. 現在 カウント:1000、行:11155386088_jd.com
  3. 現在 カウント:2000、行:136191393_Suning.com
  4. 現在 カウント:3000、行:16893837301_jd.com
  5. 現在 カウント:4000、行:19036619855_jd.com
  6. 現在 カウント:5000、行:1983786945_jd.com
  7. 現在 カウント:6000、行:1997392141_jd.com
  8. 現在 カウント:7000、行:21798495372_jd.com
  9. 現在 カウント:8000、行:24154264902_jd.com
  10. 現在 カウント:9000、行:25687565618_jd.com
  11. 現在 カウント:10000、行:26458674797_jd.com
  12. 現在 カウント:11000、行:617169906_Suning.com
  13. 現在 カウント:12000、行:769705049_Suning.com
  14. 1.5720秒12348行
  15. => 12348

HDFSでデータを表示します。

データ量と実際の状況の分析

JD:JD携帯電話のリストは約160ページで、各リストには60の製品データがあるため、合計金額は約9,600で、データは基本的にそれに沿っています。

後で、ログ分析を通じて、失われたデータは一般に接続タイムアウトによって引き起こされることがわかります。したがって、クローラー環境を選択するときは、優れたネットワーク環境を持つホストでそれを行うことをお勧めします。

同時に、IPプロキシアドレスライブラリがある場合は、より良いでしょう。さらに、接続タイムアウトでは、プログラムでさらに制御できます。

データのクロールに失敗したURLが発生したら、それを再試行のURLキューに追加できます。現在、この機能を行っていません。興味のある学生はそれを試すことができます。

Suning.com:Suningのデータを見てみましょう。約100ページの携帯電話リストがあり、各ページには60個の製品データもあるため、合計金額は約6,000です。

しかし、私たちのデータは3000の順序にすぎないことがわかります(欠落しているものは、頻繁にrawうによって引き起こされる接続障害の問題です)。これはなぜでしょうか?

これは、Suningのリストページを開いた後、最初に30の製品をロードするためです。マウスがスライドすると、他の30の製品データが別のAPIを介してロードされます。これは、各リストページに当てはまります。したがって、実際、製品データの半分が欠落しており、rawいされていません。

この理由を知った後、達成することは難しくありませんが、時間の制約のために、私はもうそれをしませんでした。興味のある友達はそれをすることができます。

ログによるクローラーシステムのパフォーマンスの分析

クローラーシステムでは、Webページのダウンロード、データ分析などのすべての重要な場所が記録されるため、関連する時間パラメーターをログを通して大まかに分析できます。

  1. 2018-04-01 21:26:03 [pool-1-thread-1] [cn.xpleaf.spider.utils.httputil] [情報] - Webページをダウンロード:https://list.jd.com/list.html?cat = 9987,653,6555r: 590 ms: 590 ms proxption  
  2. 2018-04-01 21:26:03 [Pool-1-Thread-1] [cn.xpleaf.spider.core.parser.impl.jdhtmlparserimpl] [情報] - パーサーリストページ:https://list.jd.com/list.html?
  3. 2018-04-01 21:26:03 [Pool-1-Thread-3] [cn.xpleaf.spider.core.parser.impl.snhtmlparserimpl] [情報] - パーサーリストページ:https://list.suning.com/0-20006-0.html
  4. 2018-04-01 21:26:04 [Pool-1-Thread-5] [cn.xpleaf.spider.utils.httputil] [情報] - Webページをダウンロード:https://item.jd.com/6737464.html、Consume:219 MS、Null:Null:Null:Null:Null:Null:Null:Null:Null:Null:Null: Null Null: Null  
  5. 2018-04-01 21:26:04 [Pool-1-Thread-2] [cn.xpleaf.spider.utils.httputil] [情報] - Webページをダウンロード:https://list.jd.com/list.html?cat = 9987,653,655&page=2&sort-sort = sort_asct_asct_asct_asct_asct_asp_asc 276ミリ秒、プロキシ情報: null null  
  6. 2018-04-01 21:26:04 [Pool-1-Thread-4] [cn.xpleaf.spider.utils.httputil] [情報] - Webページをダウンロード:https://list.suning.com/0-20006-99.html、Consume:300 MS、Null:Null :Null:Null:Null :Null Null  
  7. 2018-04-01 21:26:04 [Pool-1-Thread-4] [cn.xpleaf.spider.core.parser.impl.snhtmlparserimpl] [情報] - パーサーリストページ:https://list.suning.com/0-20006-99.html
  8. ......
  9. 2018-04-01 21:27:49 [Pool-1-Thread-3] [cn.xpleaf.spider.utils.httputil] [情報] - Webページをダウンロード:https://club.jd.com/comment/ProductCommentsummaries。 Action ?ReferenceIDS = 23934388891、消費時間:176 ms、プロキシ情報: null null  
  10. 2018-04-01 21:27:49 [pool-1-thread-3] [cn.xpleaf.spider.core.parser.Impl.JDHtmlParserImpl] [INFO] - 解析商品页面:https://item.jd.com/23934388891.html, 消耗时长:413ms
  11. 2018-04-01 21:27:49 [Pool-1-Thread-2] [cn.xpleaf.spider.utils.httputil] [情報] - https://review.suning.com/ajax/review_satisfy/general-000000000000000100179333333333333079092 、プロキシ情報:null:null  
  12. 2018-04-01 21:27:49 [Pool-1-Thread-2] [cn.xpleaf.spider.core.parser.impl.snhtmlparserimpl] [情報] - 分析製品ページ:https://product.suning.com/0070079092/10017779337.htmp
  13. ......

平均して、製品Webページのデータをダウンロードする時間は200〜500ミリ秒の範囲です。もちろん、これは当時のネットワークの状況に依存します。

さらに、製品をクロールするための時間データを実際に計算する場合は、ログの下のデータで計算できます。

  • 製品ページのデータをダウンロードする時間
  • 価格データを取得する時間
  • コメントデータを取得する時間

私のホスト(CPU:E5 10コア、メモリ:32GB、仮想マシン、3つの仮想マシンがそれぞれ有効になっています)では、状況は次のとおりです。

3つのノードを使用すると、それに応じて時間が1/3に縮小しないことがわかります。これは、この時点でクローラーのパフォーマンスに影響を与える主な問題は、多数のノード、多数のスレッド、多数のネットワーク要求があるネットワークの問題であるためです。

ただし、帯域幅は確実であり、プロキシが使用されない場合、頻繁にリクエストが増加し、接続の障害も増加し、時間に特定の影響を与えます。ランダムプロキシライブラリを使用すると、状況がはるかに良くなります。

しかし、クローラーノードを水平スケールで追加した後、クローラーの時間を大幅に短縮できることは確かです。これは、分散クローラーシステムの利点でもあります。

Crawler Systemsで使用される反anti-Crawler戦略

クローラーシステム全体の設計では、主に反クローラーの目的を達成するために次の戦略が使用されています。

  • プロキシを使用してアクセス - > IPプロキシライブラリ、ランダムIPプロキシ。
  • ランダム***ドメインURLアクセス - > URLスケジューリングシステム。
  • 各スレッドは、1つの製品データの睡眠をクロールする前に短時間睡眠をcrawっています。

要約する

このシステムはJavaに基づいて実装されていることに注意する必要がありますが、個人的には言語自体がまだ問題ではなく、コアはシステム全体の設計と理解にあると思います。

私はこの記事を書き、そのような分散クローラーシステムのアーキテクチャを全員と共有しました。ソースコードに興味がある場合は、GitHubで確認できます。

[[228686]]

Ye Yonghao、ビッグデータエンジニア、Huawei Hcie-RS認定エンジニア。彼はHuaweiやNeteaseなどの企業で働いており、現在、ビッグデータの分野での学習と研究に焦点を当てています。

[51CTO オリジナル記事、パートナーサイトに転載する場合は、元の著者とソースを 51CTO.com として明記してください]

<<:  「二つのクラウド」が論争を巻き起こし、中国のパブリッククラウド競争が激化

>>:  ハイブリッドクラウドを導入することで、企業がデジタルトランスフォーメーションの失敗から身を守る方法

推薦する

Baidu は差別化せずに更新します。キーワードとアンカーテキストに注意してください。

Baiduが大規模なアップデートを行った際、多くのサイトが降格やK-edされた。降格やK-edされた...

かつて年間数百万ドルを稼いだWowa減量ネットワークの全体最適化計画

2018年最もホットなプロジェクト:テレマーケティングロボットがあなたの参加を待っています最近、個人...

生鮮食品電子商取引会社TuoTu Gongsheの「前例のない」成長の物語

16世紀後半の大航海時代に、オランダは西アジア半島からチューリップを持ち込みました。その後、チューリ...

インターネット取引プラットフォームの征服:ウェブサイト取引の静かな台頭を分析

21世紀に入り、世界はインターネット時代に入り、電子商取引が普及しています。2013年には、インター...

写真サイトを1ヶ月占拠し、キーワードランキングが急上昇

私は最近、昨年 8 月に構築した画像サイトを引き継ぎました。問題はトラフィックもランキングもなかった...

ssdvirt-1g メモリ KVM VPS/10g SSD/1T 月間トラフィック/月額 7 ドル

ssdvirt の VPS は比較的高価ですが、この夏休み期間中はプロモーションがあるようです。1G...

nodeblade - $15/年 VPS/256MB RAM/80GB HDD/ダラス

Nodebladeのダラスデータセンターは、1000Mポート、80Gハードドライブの年間支払いが15...

SEO診断:医療ウェブサイトの総合分析K

みなさんこんにちは。私は徐子宇です。先ほど収集した SEO 診断事例に続いて、非常に興味深いことが分...

BandwagonHost: Japan cn2 gia を追加しました。BandwagonHost Japan cn2 gia vps が正式に販売中です!

BandwagonHost は知らないうちに日本の cn2 gia ネットワークへのアクセスを拡大し...

年間3ドルの料金でVirmachの低価格VPSレビューを体験

Virmach の VPS は特別な時期に特別なプロモーションを行っており、価格も非常に安く、たとえ...

エッジコンピューティングとは何ですか?フォグコンピューティングとは何ですか?

モノのインターネットの継続的な進歩に伴い、フォグ コンピューティングやエッジ コンピューティングなど...

#ニュース# sharktech (shark): 新しいハードウェア、無料の 60G 防御を備えた新しいサーバーがいくつか追加され、割引が販売されています

sharktech からの最新ニュース: いくつかの新しいサーバー モデルが追加されました。古いハー...

alphavps - 年間 12 ユーロ / メモリ 1g / CPU 2 個 / SSD 15g / コンピュータ ルーム 5 室 (オプション)

AlphaVPS は 2017 年に設立されました。現在までに、VPS 用のオプション データ セン...

オンラインプロモーションを効果的に行う方法の事例分析

インターネットプロモーションは、敷居の低さ、操作の簡単さ、結果の速さなどの利点から、多くの中小企業に...