また、Nutchを使用して指定された種子をcrawっている、クロールされたデータに基づいて検索したり、いくつかのソースコードを見たりするなど、以前にクローラーを使用しました。もちろん、Nutchはクローラーを非常に包括的かつ綿密に考えています。画面上でrawいされているWebページ情報と処理情報を見るたびに、これは非常に暗いテクノロジーであると常に感じています。今回は、春のMVCを整理する機会を利用して、自分で小さなクローラーを作りたかったのです。小さなバグを簡単に取得できるかどうかは関係ありません。必要なのは、私が望む情報をクロールできるシードウェブサイトだけです。例外がある場合、一部のAPIが不適切に使用されているか、異常なHTTP要求ステータスに遭遇する可能性があるため、またはデータベースの読み取りと書き込みに問題があるためです。例外を報告し、例外を解決する過程で、JewelCrawler(息子のニックネーム)はすでに独立してデータをcrawl落することができ、Word2Vecアルゴリズムに基づいた感情分析のための小さなスキルもあります。
後で解決するのを待っている未知の例外があるかもしれませんし、データベースとのやり取り、読み取りや執筆など、いくつかのパフォーマンスを最適化する必要があります。ただし、この年にこれを置くためのエネルギーはあまりありません。最初の2つの記事は、主に機能と結果に焦点を当てています。この記事では、ジュエルクローラーがどのように生まれたかについて説明し、コードをgithubに置いています(ソースコードアドレスは記事の最後にあります)。興味がある場合は、注意を払うことができます(コミュニケーションと学習のためだけに、Doubanをお願いします。ドゥバンをお願いします。もっと誠実で害が少なくなります)
環境の紹介
開発ツール:Intellij Idea 14
データベース:MySQL 5.5 +データベース管理ツールNAVICAT(クエリデータベースに接続するために使用できます)
言語:Java
JARパッケージ管理:Maven
バージョン管理:git
ディレクトリ構造
で
com.ansj.vecは、word2vecアルゴリズムのJavaバージョンの実装です
com.jackie.crawler.doubanmovieは、Crawler実装モジュールです
これらのモジュールはまだ使用されていないため、一部のパッケージは空です。
リソースモジュールは、構成ファイルとリソースファイルを保存します。
テストモジュールは、UTの書き込みに使用されるテストモジュールです。
データベース構成
1.依存関係パッケージを追加します
JewelcrawlerはMaven Managementを使用しているため、POM.xmlに対応する依存関係を追加するだけです。
<Dependency> groupId> org.springframework </groupid> <artifactid> spring-jdbc </artifactid> <version> 4.1.1.release </version> </dependency> <sependency> <shiplency> <groupid> <groupId> commons-dbcp </groupid> <artifactid> commons-dbcp </artifactid> <artifactid> commons-dbcp </artifactid> <artifactid> <artifactid> <artifactid> <バージョン> 1.4 </バージョン> </依存関係> < <Artifactid> mysql-connector-java </artifactid> <bersion> 5.1.38 </version> </dependency> <dependency> <groupid> mysql </groupid> <artifactid> mysql-connector-java </artifactid> <バージョン>
2。データソースBeanを宣言します
beans.xmlのデータソースの豆を宣言する必要があります
<コンテキスト:プロパティプレイスホルダーlocation = "classpath*:*。プロパティ"/> <bean id = "datasource" destroy-method = "close"> <property name = "driverclassname" value = "$ {jdbc.driver}"/>> <プロパティ名= "url" value = "$ {jdbc.url} value = "$ {jdbc.username}"/> <プロパティ名= "password" value = "$ {jdbc.password}"/>注:以下は、外部構成ファイルjdbc.propertiesバウンドです。特定のデータソースのパラメーターがこのファイルから読み取られます。
問題に遭遇した場合、「sql [Inserting Intering user(id)values(?)]; field 'name'にはデフォルト値がありません。」解決策は、テーブルの対応するフィールドを自己成長フィールドに設定することです。
ページを解析するときに遭遇する問題
クロールしたWebページデータの場合、DOM構造を解析し、必要なデータを取得する必要があります。この期間中、次のエラーが発生します
org.htmlparser.nodeは認識されません
解決策:JARパッケージの依存関係を追加します
<Dependency> GroupId> org.htmlparser </groupId> <artifactid> htmlparser </artifactid> <バージョン> 1.6 </version> </dependency>
org.apache.http.httpentityは認識されていません
解決策:JARパッケージの依存関係を追加します
<Dependency> groupId> org.apache.httpcomponents </groupId> <artifactid> httpclient </artifactid> <バージョン> 4.5.2 </version> </dependency>
もちろん、これは期間中に発生する問題であり、JSOUPによって行われたページ分析が最終的に使用されます。
Maven Warehouseのダウンロード速度は遅いです
以前にデフォルトのMaven Centralリポジトリを使用しましたが、JARパッケージのダウンロード速度は非常に遅かったです。それが私のネットワークの問題なのか、他の理由なのかはわかりません。その後、Alibaba CloudのMavenリポジトリをオンラインで見つけました。更新後、以前と比較して血を吐くことをお勧めします。
<Mirrors> <mirror> <id> alimaven </id> <name> aliyun maven </name> <url> http://maven.aliyun.com/nexus/content/groups/public/ </url> <mirrorof> central </mirrorof> </mirrors> </mirrors>
Mavenのsettings.xmlファイルを見つけて、この画像を追加します。
リソースモジュールの下でファイルを読み取る方法
たとえば、Seed.Propertiesファイルをお読みください
@test public void testfile(){file seedfile = new file(this.getClass()。getResource( "/seed.properties")。getPath()); System.out.print( "==========" + SeedFile.Length() + "=========================="); }正規表現について
Regrexの正規表現を使用する場合、定義されたパターンが一致している場合は、グループ方法を使用してサブストリングを見つける前に、MatcherのFindメソッドを呼び出す必要があります。グループメソッドを直接呼び出すことで、必要な結果を見つける方法はありません。
上記のマッチャークラスのソースコードを見ました
パッケージjava.util.regex; Import java.util.objects; public final class matcherはmatchResult { /***このマッチャーを作成したパターンオブジェクトを実装します。 */ pattern parentpattern; /***グループが使用するストレージ。マッチング中にグループがスキップされた場合、無効な値が含まれる場合があります。 */ int []グループ; /***一致するシーケンス内の範囲。アンカー *は、これらの「硬い」境界で一致します。領域の変更 *これらの値は変更されます。 */ int from、to; /** * Lookbehindはこの値を使用して、subexpression *の一致がLookbehindに遭遇したポイントで終了することを確認します。 */ int lookbehindto; /***一致する元の文字列。 */ charquenceテキスト; /***最後のノードで使用されるマッチャー状態。 NOANCHORは、すべての入力を消費する必要がない場合に使用されます。 Endanchorは *すべての入力を一致させるために使用されるモードです。 */ static final int endanchor = 1;静的final int noanchor = 0; int acceptmode = noanchor; /***パターンに最後に一致した文字列の範囲。最後の *一致が失敗した場合、最初は-1です。最初は0を保持します。その後、最後のマッチの終了のインデックスを保持します(ここから *次の検索が始まります)。 */ int first = -1、last = 0; /***最後のマッチ操作で一致したものの終了インデックス。 */ int oldlast = -1; /***置換に適用される最後の位置のインデックス。 */ int lastAppendPosition = 0; /** *ノードが使用するストレージは、パターンでどの繰り返しがあるか、グループがどこから始まるかを伝えるために使用されます。ノード自体は無国籍です *したがって、彼らは試合中にこのフィールドに依存して状態を保持します。 */ int []地元の人。 /** *入力の多くが変更される可能性があるかどうかを示すBoolean *最後の試合の結果。 * * HitendがTrueで、試合が見つかった場合、より多くの入力 *が別の一致が見つかる可能性があります。 * Hitendが真であり、試合が見つからなかった場合、より多くの入力が一致を見つける可能性があります。 * Hitendが虚偽で一致が見つからなかった場合、 *入力は一致を見つけることができません。 */ boolean hitend; /** *ブールンは、入力を増やす可能性があるかどうかを示します *正の一致を負の一致に変えます。 * * requiredがtrueで、試合が見つかった場合、より多くの入力が一致を失う可能性があります。 * requiredがfalseで、試合が見つかった場合、より多くの入力は試合を変更する可能性がありますが、試合は失われません。 *一致が見つからなかった場合、要求には意味がありません。 */ boolean required; /** * TransparentBoundsが真である場合、この * Matcherの領域の境界は、それらを超えて見ようとする境界マッチングコンストラクトに透明性があります。 */ boolean transparentbounds = false; /** * AnchoringBoundsがTrueの場合、この * Matcherの領域の境界は ^や$などのアンカーを一致させます。 */ boolean anchoringbounds = true; /***デフォルトのコンストラクターなし。 * / matcher(){} / ***すべてのマッチャーは、マッチ中にパターンで使用される状態を持っています。 */matcher(pattern parent、charquence text){this.parentpattern = parent; this.text = text; // State Storage int parentGroupCount = math.max(parent.capturinggroupCount、10);グループ= new int [parentgroupCount * 2]; locals = new int [parent.localcount]; //フィールドを初期状態に入れますreset();} ..../***は、前の一致で一致する入力サブシーケンスを返します。 * * * * <p>入力シーケンス<i> s </i>、 *式<i> m。</i> <tt> group()</tt> </i> <tt> s。 <i> m。</i> <tt> end())</tt> *は同等です。 </p> * * <p>いくつかのパターン、たとえば<tt> a * </tt>は、空の *文字列に一致することに注意してください。このメソッドは、パターン *が入力の空の文字列と正常に一致するときに空の文字列を返します。 </p> * * @ @@return前のマッチに一致する(おそらく空の)シーケンス、 *文字列形式 * * @Throws IllegalStateException *マッチがまだ試みられていない場合 *または前のマッチ操作が失敗した場合 *またはpublic String group(){return Group(0);}/** *は、以前のマッチ操作中に与えられたグループによってキャプチャされた入力サブセンフェンスを返します。 * * * * <p>マッチャー<i> m </i>、入力シーケンス<i> s </i>、およびグループインデックス * <i> g </i>、式<i> m。</i> <tt>グループ(</tt> <i> g </i> <tt>)</tt>および * S。 </p> * * <p> <a href = "pattern.html#cg">キャプチャグループ</a>は、左から右にインデックス付けされ、1つから始まります。グループゼロはパターン全体を示します。 *式<tt> m.group(0)</tt>は<tt> m.group()</tt>に相当します。 * </p> * * <p>一致が成功したが、指定されたグループが入力シーケンスの任意の部分を一致させなかった場合、<tt> null </tt>が返されます。注 *いくつかのグループ、たとえば<tt>(a *)</tt>は、空の文字列に一致します。 *このメソッドは、そのようなグループが入力の空の文字列に正常に一致するときに空の文字列を返します。 </p> * @param Group *このマッチャーのパターンのキャプチャグループのインデックス * * @ @return(おそらく空の)以前の試合中にグループがキャプチャした後、<tt> null </tt> </tt> </tt> </tt> </tt> </tt> </tt> * @throws Illegalstateexception * @throws indectの場合、指定されたインデックス */public String Group(int Group){if(first <0)を使用して、パターンのキャプチャグループはありません。 if(group <0 || group> groupCount())は、新しいindexOutOfOUTOUPBOUNDSEXCEPTION( "No Group" + Group)をスローします。 if((グループ[グループ*2] == -1)||(グループ[グループ*2+1] == -1))nullを返します。 return getSub sequence(グループ[グループ * 2]、グループ[グループ * 2 + 1]) * * <p>このメソッドは、このマッチャーの領域の開始時に始まります。または、 *メソッドの以前の呼び出しが成功し、マッチャーがリセットされていない場合、最初の文字で前の *一致で一致しない場合。 * * <p>一致が成功した場合、 * <tt> start </tt>、<tt> end </tt>、および<tt> group </tt>メソッドを介してさらに多くの情報を取得できます。 </p> * * @return <tt> true </tt>の場合、入力 *シーケンスのシーケンスがこのマッチャーのパターンと一致する場合のみ */public boolean find(){int nextsearchindex = last; if(nextsearchindex == first)nextsearchindex ++; //次の検索が領域の前に始まる場合、領域でそれを開始します(nextsearchindex <from)nextsearchindex = from; //次の検索が領域を超えて始まる場合、(nextsearchindex> to){for(int i = 0; i <groups.length; i ++)group [i] = -1; falseを返します。 } return search(nextsearchindex);} /***検索を開始して、指定された境界内でパターンを見つけます。 *グループにはデフォルト値が満たされており、状態マシンのルートの一致 *が呼び出されます。ステートマシンは、このマッチャーで進行するため、試合の状態 *を保持します。 * * matcher.fromは、アンカーが設定する検索の開始の「ハード」境界 *であるため、ここには設定されていません。 from param *は、検索の開始の「ソフト」境界です。つまり、 * regexはそのインデックスで一致しようとしますが、 ^はそこで一致しません。後続の *検索方法への呼び出しは、前の試合の終わりである新しい「ソフト」境界から始まります。 */boolean search(int from){this.hitend = false; this.requireend = false; from = from <0? 0:from; this.first = from; this.oldlast = oldlast <0? From:Oldlast; for(int i = 0; i <groups.length; i ++)group [i] = -1; AcceptMode = noanchor; boolean result = parentpattern.root.match(this、from、text); if(!result)this.first = -1; this.oldlast = this.last; return result;} ...}その理由は次のとおりです。最初にFINDメソッドを呼び出してグループに直接呼び出さない場合、グループメソッドがグループ(INTグループ)を呼び出すことがわかります。メソッドのメソッド本体に最初の<0がある場合。明らかに、この条件はここで真実です。なぜなら、最初の初期値は-1であるため、ここに例外がスローされるからです。ただし、Findメソッドを呼び出すと、検索(NextSearchIndex)が最終的に呼び出されることがわかります。ここのNextSearchIndexはLastによって割り当てられ、Lastの値は0であり、検索方法にジャンプすることに注意してください
boolean search(int from){this.hitend = false; this.requireend = false; from = from <0? 0:from; this.first = from; this.oldlast = oldlast <0? From:Oldlast; for(int i = 0; i <groups.length; i ++)group [i] = -1; AcceptMode = noanchor; boolean result = parentpattern.root.match(this、from、text); if(!result)this.first = -1; this.oldlast = this.last; return result;}このNextSearchIndexは、fromに渡され、からはメソッド本体の最初に割り当てられます。したがって、Findメソッドを呼び出した後、これの最初は-1ではなく、例外を投げかけていません。
ソースコードはBaidu Netdiskにアップロードされました:http://pan.baidu.com/s/1dfwtvnz
上記の問題は比較的粉砕されており、問題に遭遇して解決するときの要約です。特定の操作中に他の問題があります。ご質問や提案がある場合は、お気軽にお問い合わせください^^。
最後に、これまでにクロールされたデータをいくつか入れてください
録音テーブル
その中で、79,032が保存され、48,471のクロールされたWebページは
映画のテーブル
現在、2964の映画とテレビの作品がrawいされています
コメントテーブル
29,711の記録がrawいされました
上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。