Java クローラー戦闘: Jsoup+HtmlUnit+マルチスレッド+非同期スレッド+Qiniuyun oss+SpringBoot+MyBatis-Plus+MyBatis-Plus+MySQL8

PS: このオープンソース クローラー プロジェクトは、学習とコミュニケーションのみを目的としています。違法行為にクローラーを使用しないでください。クロールされたデータが販売されたり、流通したりすることはありません。関連する国内法と規制は断固として遵守してください。

1. プロジェクト紹介

このクローラーは Web マスターが 1 人で作成しました。Java クローラー関連のドキュメントが不足していたため、クローラーを作成する際に多くの落とし穴に遭遇しました。この記事では、それらをビデオで 1 つずつ説明します。クローラーは、独自の e コマース プロジェクトを組み合わせたものであるため、同様のクローラーよりも詳細です。プロジェクトには合計 6 つのテーブルが含まれます。テーブル構造とクロールされたデータは次のとおりです。

このクローラーは、検索ページのページ全体 (30 件) の SPU と詳細ページの 1 つの SPU のクロールをサポートしており、SPU をクロールする際に、対応するサブ製品をクロールします。クロールされた写真はローカルまたは Qiniu Cloud に保存することも、まったく保存することもできませんが、サードパーティのリンクを直接使用してアプリケーションで設定することもできます。クロール結果には、テーブル tb_crawler_log に対応するログ レコードが含まれ、テーブルにはクロール タイプを表すタイプ フィールドがあります。0- スケジュールされたタスクのクロール、1- 製品リスト (30 spu) のクロール、2- 詳細ページ spu のクロール、3- クロールされたデータの更新。このうち、 type=3は、大手 EC 企業のアンチクライミングが比較的厳しく、クロールされたデータの一部が異常である必要があるため、非常に実用的です。異常な商品データが見つかった場合は、再度クロールして商品データを更新できます。結果。当サイトの商品詳細ページには、異常のある商品データを再クローリングして更新する機能がございます。

時限タスク クローラーの構成は、テーブル tb_crawler_config に対応します。クローラーは複数のサーバー上で実行できます。各サーバーは、独自の IP アドレスに基づいてテーブル内で独自の構成情報を検索し、製品をクロールします。

 製品リストのクロールの通常の範囲は、Spu: 20 ~ 30、Sku: 30 ~ 500 です。そうでない場合は、クロール防止および電流制限が行われているか、製品がクロールされており、データベースに保存されない可能性があります。

 2. ソースコード分析

2.1 依存関係のインポート

IDEA の Spring Initializr を使用して、SpringBoot 環境を迅速に構築し、クローラーのコア依存関係をインポートします。

<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.10.3</version>
</dependency>
<!--模拟网页,实现动态获取-->
<dependency>
    <groupId>net.sourceforge.htmlunit</groupId>
    <artifactId>htmlunit</artifactId>
    <version>2.60.0</version>
</dependency>

2.2 HtmlUnit を使用してページを解析する 

/**
 * @param cookie
 * @param url 爬取链接
 * @return
 * @throws Exception
 */
public  String parseByUrl(String cookie, String url) throws Exception{
    // 得到浏览器对象,直接New一个就能得到,现在就好比说你得到了一个浏览器了
    WebClient webClient = new WebClient(BrowserVersion.CHROME);
    webClient.setJavaScriptErrorListener(new HUnitJSErrorListener());
    webClient.setCssErrorHandler(new HUnitCssErrorListener());
    webClient.setJavaScriptTimeout(30000);

    // 这里是配置一下不加载css和javaScript,因为httpunit对javascript兼容性不太好
    webClient.getOptions().setCssEnabled(true);
    webClient.getOptions().setJavaScriptEnabled(true);
    webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
    webClient.getOptions().setThrowExceptionOnScriptError(false);
    webClient.getOptions().setPrintContentOnFailingStatusCode(false);

    Cookie ck = new Cookie(".jd.com","Cookie",cookie);
    webClient.getCookieManager().addCookie(ck);
    // header设置
    webClient.addRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36");
    // 做的第一件事,去拿到这个网页,只需要调用getPage这个方法即可
    HtmlPage htmlpage = webClient.getPage(url);
    return htmlpage.asXml();
}

解析プロセス中に大量のログが出力されます。WebClient の組み込み設定では js および css ログが出力されますが、ログは引き続き出力されます。この問題を解決するには、DefaultJavaScriptErrorListener と DefaultCssErrorHandler の 2 つのクラスを書き直す必要があります。例外がキャッチされた場合、何も処理されず、エラーおよび警告のログ情報は出力されません。

 2.3 application.yml の設定

  • 新しいクローラ データベースを作成し、プロジェクトの SQL ファイルをインポートし、データベース接続プールを構成します。
  • 構成ファイル qiniu の下の storage と ossStorage の値はブール値で、それぞれ写真をローカルに保存するか Qiniu Cloud に保存するかを示します。最初は ossStorage=true を設定し、写真を Qiniu Cloud に保存しようとしましたが、写真の容量が多すぎて、データがあまりないのにデータ容量が使用されてしまいました。次に、2 つの属性を false に設定します。これは、ローカルにも Qiniu Cloud oss にも保存されず、サードパーティのリンクを直接使用することを意味します (次の構成情報は単なるテンプレートであり、実際のデータではありません)。
spring:
  application:
    name: crawler
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://lqjai.com:3306/mall_goods?serverTimezone=Asia/Shanghai
    username: zhangsan
    password: 6666666

qiniu:
  storage: false #是否开启本地存储
  ossStorage: false #是否开启七牛云存储
  accessKey: Jm9_djiofs16bXsFtWOSxMUvBJa3Rxp3wA0N0poH
  secretKey: 4hRBlT6DM_dBdUoLw8eMSu55kYiacl0Fzb2ZKn0J
  bucket: kili  #空间名称
  file:
    url: http://oss.lqjai.cn/
    path: qjmall/img/goods/

2.4 タイミングタスクの設定

適切な時間間隔を自分で設定し、30秒間隔で次のタスクを実行します。同時に、データベースの tb_crawler_config 情報を構成します。主に、クロールするキーワードとクロールするページ数を設定します。クローラーが複数のサーバーで実行される場合は、複数のレコードを構成する必要があります。クローラーがデータを読み取るときに、独自の IP 構成情報。

2.5 マルチスレッドと非同期スレッド

ここで設定されているスレッド プールのサイズは 30 ですが、お使いのコンピュータの構成に応じて変更できます。ここでは、複数のスレッドによる共有変数への相互排他的アクセスの問題に注意する必要があります。ここでは単純に行レベルのスレッド プールを使用します。相互排他的アクセスを実現するために mysql をロックします。

// 初始化线程池
@PostConstruct
private void initThreadPool() {
    if (executorService == null) {
        executorService = new ThreadPoolExecutor(30, 30, 0L, TimeUnit.MILLISECONDS,
                                                 new LinkedBlockingQueue<Runnable>(), new ThreadPoolExecutor.DiscardPolicy());
    }
}

ここでは、非同期スレッドはデフォルトで閉じられています。非同期スレッドを開きたい場合は、コメント @Async を解放するだけです。 

非同期スレッドは単なるコメントではなく、多くの注意事項があります。そうしないと、非同期スレッドがまったく有効にならない場合があり、依然として同期スレッドであり、見つけるのが簡単ではない場合があります。非同期スレッドが機能しない一般的な状況は次のとおりです。

  • 非同期効果を生成するには、@Async を別のクラスで使用する必要があります。メソッドは別のクラス、つまりクラスの外部から呼び出す必要があります。クラスの内部呼び出しは無効です。
  • Spring プロキシ クラスはありません。@Transactional および @Async アノテーションの実装は Spring の AOP に基づいており、AOP の実装は動的プロキシ モードに基づいているためです。では、アノテーションが失敗する理由は明らかで、Spring コンテナによって管理されていないため、プロキシ オブジェクトの代わりにオブジェクト自体が呼び出されていることが原因である可能性があります。
  • @EnableAsync アノテーションは @SpringBootApplication スタートアップ クラスに追加されません
  • アノテーション @Async を使用した非同期メソッドの戻り値は void または Future のみです
  • 注釈付きメソッドはパブリック メソッドである必要があります。
  • クラス内から呼び出す必要がある場合は、最初にそのプロキシ クラスを取得する必要があります

3 アンチクライミングで踏まれた穴 

  • 詳細ページにはログインする必要があります。製品詳細ページに入るには、ログインする必要があります。ログインするためのスクリプトを作成しようとしましたが、Jingdong ログインにはスライディング スライダー、オブジェクト認識、その他の検証があり、マシンはシミュレートできません当面の人間行動検証情報。解決策は、自分で手動でログインし、SMS ログイン Cookie の有効期限が 30 日間なので、Cookie をコピーすることです。
  • コメントと価格はクロールできません。Jingdong のクロール対策です。コメントと価格は js スクリプトを通じて動的に取得されます。最初に読み込まれるページにはこれら 2 つのデータが含まれていないため、インターフェースを調整してこれらを個別に取得する必要があります
//http请求查询价格
public Map<String, Object> queryPrice(String ck, String id){
    HttpHeaders headers = new HttpHeaders();//header参数
    List<String> cookies = Arrays.asList(ck.split(";"));
    // header设置
    headers.add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36");
    // cookie设置
    headers.put(HttpHeaders.COOKIE, cookies);
    HttpEntity<String> httpEntity = new HttpEntity(headers);

    String priceUrl = "https://p.3.cn/prices/mgets?callback=jQuery2414702&pduid=15282860256122085625433&pdpin=&skuIds=J_"+id;
    log.info("\n###### priceUrl:{}", priceUrl);
    ResponseEntity<String> priceResult = restTemplate.exchange(priceUrl, HttpMethod.GET, httpEntity, String.class);
    return getPrice(priceResult.getBody());
}
  • あまりクロールしすぎないように注意してください。最初はクローラーが 1 つしか実行されておらず、クロールされたデータには異常なデータはほとんどありませんでした。後で、これでは遅すぎると感じました。100 万件の商品データをクロールしたいので、進捗を加速する必要があります。次に、4 台のマシンを実行し、3 台​​のクラウド サーバーを 1 日 24 時間実行し、1 台のマシンをクローラーを実行してマシンがアイドル状態のときにデータをクロールしました。その後、JD.com が私のアクセスが異常であることを検出したため、アクセスを制限する必要があると推測されます。現在の制限後、商品名、価格、画像アドレスが空の異常なデータに時々クローリングされます。通常の状況では、これら 3 つのデータ項目は空ではありません。現在の制限後も停止せずに4台のマシンで実行し続けましたが、結局、時々異常なデータが発生しますが、正常なデータまでは上昇し、定期的に異常なデータをクリーンアップしていました。その後、JDに直接アカウント制限をかけられ、Web版ではログインできなくなり、ログインしてもすぐに落ちてしまいましたが、アプリでは普通にログインできました。約 1 日後、私のアカウントは再びログインできるようになり、セキュリティ部門がそれを審査し、ブロックから電流制限に格下げされたものと推定されています。現在の制限を超えると、価格フィールドはデータのクロールに失敗することがありますが、クロールされた他のフィールドは正しくなります。

 4. まとめ

今回は、クロールされたデータを私が作成した電子商取引プロジェクトに適用するため、クローラーはより慎重に実行されました。Java クローラーのドキュメントが少なく、手探りしていると落とし穴をたくさん踏んでしまい、かなり時間がかかりました。クローラーは依然として Python で簡単に使用できるため、将来的にはクローラーを作成するのに Python を使用することを好みます。

5. 関連情報

このプラットフォームでは自由にリンクを投稿することができず、審査も通っていないため、ここにはリンクを投稿しません。ソースコードが必要な場合は、私の個人ホームページ「Kili Learning Network」にアクセスするか、Github でプロジェクトを直接検索してください。キーワードはブログのタイトルです

おすすめ

転載: blog.csdn.net/m0_70140421/article/details/124852475