لقد استخدمت أيضًا زحفات من قبل ، مثل استخدام Nutch لزحف بذرة محددة ، والبحث استنادًا إلى البيانات المزروعة ، والنظر تقريبًا في بعض التعليمات البرمجية المصدر. بالطبع ، يعتبر نوتش الزحف بشكل شامل ودقيق للغاية. كلما رأيت معلومات صفحة الويب ومعالجة المعلومات التي تم الزحف عليها على الشاشة ، أشعر دائمًا أن هذه تقنية مظلمة للغاية. هذه المرة ، استفدت من الفرصة لفرز Spring MVC وأردت صنع زاحف صغير بنفسي. لا يهم إذا كان بإمكاني الحصول على بعض الأخطاء الصغيرة بسهولة. كل ما أحتاجه هو موقع ويب للبذور يمكنه زحف المعلومات التي أريدها. إذا كان هناك استثناء ، فقد يكون ذلك لأن بعض واجهات برمجة التطبيقات تستخدم بشكل غير صحيح ، أو قد تواجه حالة طلب HTTP غير طبيعي ، أو هناك مشكلة في قراءة قاعدة البيانات والكتابة. في عملية الإبلاغ عن الاستثناءات وحل الاستثناءات ، يمكن لـ Jewelcrawler (لقب SON) أن يزحف بالفعل البيانات بشكل مستقل ، وهناك أيضًا مهارة صغيرة للتحليل العاطفي تعتمد على خوارزمية Word2VEC.
قد تكون هناك استثناءات غير معروفة في انتظار حلها لاحقًا ، ويجب أن يتم تحسين بعض الأداء ، مثل التفاعل مع قاعدة البيانات وقراءة البيانات والكتابة ، وما إلى ذلك. ومع ذلك ، ليس لدي الكثير من الطاقة لوضع هذا في العام ، لذلك سأقدم ملخصًا بسيطًا اليوم. تركز المادتين الأولين بشكل رئيسي على الوظائف والنتائج. تتحدث هذه المقالة عن كيفية ولادتها JewelCrawler وتضع الكود على Github (عنوان الكود المصدر هو في نهاية المقالة). إذا كنت مهتمًا ، فيمكنك الانتباه (للتواصل والتعلم فقط ، من فضلك Douban. من فضلك Douban. كن أكثر صدقًا وأقل ضررًا)
مقدمة البيئة
أدوات التنمية: intellij Idea 14
قاعدة البيانات: أداة إدارة قواعد البيانات MySQL 5.5 + (يمكن استخدامها للاتصال بقواعد بيانات الاستعلام)
اللغة: جافا
إدارة حزمة الجرة: Maven
إدارة الإصدار: GIT
هيكل الدليل
في
com.ansj.vec هو تنفيذ إصدار Java لخوارزمية Word2Vec
com.jackie.crawler.doubanmovie هي وحدة تنفيذ الزاحف ، والتي تشمل أيضًا
بعض الحزم فارغة لأن هذه الوحدات لم تستخدم بعد ، من بينها
تقوم وحدة الموارد بتخزين ملفات التكوين وملفات الموارد ، مثل
وحدة الاختبار هي وحدة اختبار تستخدم لكتابة UT.
تكوين قاعدة البيانات
1. إضافة حزم التبعية
يستخدم JewelCrawler إدارة Maven ، لذلك تحتاج فقط إلى إضافة التبعيات المقابلة في pom.xml.
<Rependency> <roupEd> org.springframework </rougiD> <StifactId> spring-jdbc </stifactid> <splection> 4.1.1.Release </version> </rependency> <redenced> <groupid> pool-pool </groupid> <Gropled> commons-dbcp </rougiD> <StifactId> commons-dbcp </stifactid> <StifactId> commons-dbcp </shintifactid> <terfactid> commons-dbcp </stiftid> <sored> 1.4 </version> </empency> <StifactId> mysql-connector-java </shintifactid> <الإصدار> 5.1.38 </version> </repreadency> <reperency> <roupled> mysql </rougeid> </sontifactid> mysql-connect
2. إعلان حبة مصدر البيانات
نحتاج إلى إعلان حبة مصدر البيانات في Beans.xml
<context: property-placeholder location = "classpath*:*. properties"/> <bean id = "dataSource" destroy-method = "close"> <property name = "driverClassName" value = "$ {jdbc.driver}"/> <property name = "$ {jdbc.url} value = "$ {jdbc.username}"/> <property name = "password" value = "$ {jdbc.password}"/>ملاحظة: هنا ملف التكوين الخارجي JDBC.Properties ملزمة ، ويتم قراءة معلمات مصدر البيانات المحدد من هذا الملف.
إذا واجهت المشكلة ، "SQL [insert في قيم المستخدم (معرف) (؟)] ؛ لا يحتوي الحقل على قيمة افتراضية ؛" الحل هو تعيين الحقل المقابل للجدول على حقل النمو الذاتي.
تواجه المشاكل عند تحليل الصفحات
بالنسبة لبيانات صفحة الويب التي قمت بزحفها ، تحتاج إلى تحليل بنية DOM والحصول على البيانات التي تريدها. خلال هذه الفترة ، تواجه الخطأ التالي
لم يتم التعرف على org.htmlparser.node
الحل: أضف تبعية حزمة الجرة
<Rependency> <roupiD> org.htmlparser </groupId> <StifactId> htmlparser </artifactId> <sored> 1.6 </version> </sependency>
لم يتم التعرف على org.apache.http.httpentity
الحل: أضف تبعية حزمة الجرة
<Rependency> <roupeD> org.apache.httpcomponents </groupId> <StifactId> httpclient </shintifactid> <الإصدار> 4.5.2 </version> </sependency>
بالطبع ، هذه مشكلة تواجهها خلال هذه الفترة ، ويتم استخدام تحليل الصفحة الذي تم إجراؤه بواسطة JSoup.
سرعة تنزيل مستودع Maven بطيئة
لقد استخدمت مستودع Maven المركزي الافتراضي من قبل ، وكانت سرعة تنزيل حزم الجرة بطيئة للغاية. لا أعرف ما إذا كانت مشكلة شبكتي أو أسباب أخرى. في وقت لاحق ، وجدت مستودع Maven من Alibaba Cloud Online. بعد التحديث ، تم التوصية بتقيؤ الدم مقارنة قبل.
<ribors> <lirror> <id> alimaven </d> <name> aliyun maven </mame> <Url> http://maven.aliyun.com/nexus/content/groups/public/ </url> <lirerorof> المركزية </mirrorof> </mirrors>
ابحث عن ملف settings.xml من Maven وأضف هذه الصورة.
طريقة لقراءة الملفات ضمن وحدة الموارد
على سبيل المثال ، اقرأ ملف seed.properties
test public void testfile () {file seedfile = new file (this.getClass (). getResource ("/seed.properties"). getPath ()) ؛ System.out.print ("============" + Seedfile.length () + "===========") ؛ }حول التعبيرات العادية
عند استخدام تعبيرات Regrex العادية ، إذا تم مطابقة النمط المحدد ، فأنت بحاجة إلى استدعاء طريقة Find Matcher قبل أن تتمكن من استخدام طريقة المجموعة للعثور على السلسلة الفرعية. لا توجد طريقة للعثور على النتيجة التي تريدها من خلال استدعاء طريقة المجموعة مباشرة.
نظرت إلى الكود المصدري لفئة المطابقة أعلاه
حزمة java.util.regex ؛ استيراد java.util.Objects ؛ ينفذ مطابقة الفئة النهائية العامة matchresult { /*** كائن النمط الذي أنشأ هذا المطابقة. */ pattern parentpattern ؛ /*** التخزين المستخدمة من قبل المجموعات. قد تحتوي على قيم غير صالحة إذا تم تخطي مجموعة خلال المطابقة. */ int [] مجموعات ؛ /*** النطاق داخل التسلسل الذي يجب مطابقة. المراسي * سوف يتطابق مع هذه الحدود "الصلبة". تغيير المنطقة * يغير هذه القيم. */ int من ، إلى ؛ /** * يستخدم Lookbehind هذه القيمة للتأكد من أن المطابقة الفرعية * تنتهي عند النقطة التي تمت فيها مواجهة Lookbehind. */ int lookbehindto ؛ /*** السلسلة الأصلية التي يتم مطابقة. */ نص charsequence ؛ /*** حالة المطابقة المستخدمة من قبل العقدة الأخيرة. يتم استخدام nooanchor عندما لا يضطر مطابقة * إلى استهلاك جميع المدخلات. Endanchor هو * الوضع المستخدم لمطابقة جميع المدخلات. */ ثابت نهائي int endanchor = 1 ؛ ثابت نهائي int nooanchor = 0 ؛ int acceptMode = nooanchor ؛ /*** نطاق السلسلة التي تتوافق مع النمط. إذا فشلت المباراة الأخيرة * أولاً هي -1 ؛ في البداية ، يحمل 0 ثم يحمل فهرس نهاية المباراة الأخيرة (وهو المكان الذي يبدأ فيه البحث التالي). */ int first = -1 ، last = 0 ؛ /*** فهرس النهاية لما هو مطابق في عملية المباراة الأخيرة. */ int oldlast = -1 ؛ /*** فهرس الموقف الأخير المطبق في بديل. */ int lastappendposition = 0 ؛ /** * التخزين المستخدمة من قبل العقد لمعرفة التكرار الذي هم عليه في * نمط ، وأين تبدأ المجموعات. العقد نفسها عديمة الجنسية ، * لذلك يعتمدون على هذا المجال لعقد الحالة أثناء المباراة. */ int [] السكان المحليين ؛ /** * Boolean التي تشير إلى ما إذا كان المزيد من المدخلات يمكن أن تغير * نتائج المباراة الأخيرة أم لا. * * إذا كانت الضربة صحيحة ، وتم العثور على تطابق ، فقد يتسبب المزيد من الإدخال * في العثور على تطابق مختلف. * إذا كانت الضربة صحيحة ولم يتم العثور على تطابق ، فقد يتسبب المزيد من المدخلات في العثور على تطابق. * إذا كانت الضربة خاطئة ولم يتم العثور على تطابق ، فلن يتسبب المزيد من الإدخال في العثور على تطابق. */ الضحك المنطقي ؛ /** * Boolean التي تشير إلى ما إذا كان المزيد من المدخلات يمكن أن يتغير * تطابق إيجابي إلى واحد سلبي. * * إذا كان الطلب صحيحًا ، وتم العثور على تطابق ، فقد يتسبب المزيد من المدخلات في فقدان المباراة. * إذا كان TerqueDend كاذبًا وتم العثور على تطابق ، فقد يغير المزيد من الإدخال المطابقة ولكن لن تضيع المباراة. * إذا لم يتم العثور على مباراة ، فلا يوجد أي معنى. */ مطالب منطقية ؛ /** * إذا كانت الشفافة صحيحة ، فإن حدود منطقة المطابقة هذه شفافة للبحث عن الرؤوس ، و lookbehind ، * والحدود المطابقة التي تحاول رؤيتها وراءها. */ Boolean TransparentBounds = false ؛ /** * إذا كان InstricoringBounds صحيحًا ، فإن حدود مراسي مطابقة منطقة المطابقة هذه * مثل ^ و $. */ boolean anchoringbounds = true ؛ /*** لا مُنشئ افتراضي. * / matcher () {} / *** جميع المطاعم لديها الحالة المستخدمة حسب النمط أثناء المباراة. */matcher (pattern parent ، text charsepress) {this.parentPattern = parent ؛ this.text = text ؛ // تخصيص Story Storage int ParentGroupCount = Math.Max (parent.capturingGroupCount ، 10) ؛ المجموعات = new int [parentGroupCount * 2] ؛ السكان المحليين = new int [parent.localCount] ؛ // ضع الحقول في إعادة تعيين الحالات الأولية () ؛} ..../*** إرجاع الإدخال بعد المباراة السابقة. * * * <p> لمطابقة <i> m </i> مع تسلسل الإدخال <i> s </i> ، * التعبيرات <i> m. </i> <tt> group () </tt> و * <i> s. </i> <tt> substring (</tt> <i> </i> <tt> start () <i> m. </i> <tt> end ()) </tt> * متكافئة. </p> * * <p> لاحظ أن بعض الأنماط ، على سبيل المثال <tt> a * </tt> ، تطابق السلسلة الفارغة *. ستعود هذه الطريقة إلى السلسلة الفارغة عندما يتطابق النمط * بنجاح مع السلسلة الفارغة في الإدخال. </p> * * Return تسلسل (ربما فارغ) الذي يتطابق مع المباراة السابقة ، * في نموذج السلسلة * * throws alfortlestatexception * إذا لم تتم محاولة تطابق بعد ، * أو إذا فشلت عملية المطابقة السابقة */مجموعة السلسلة العامة () {Return Group (0) ؛}/** * إرجاع الإدخال الذي تم التقاطه بواسطة المجموعة المحددة أثناء المطابقة السابقة. * * <p> لمطابقة <i> m </i> ، تسلسل الإدخال <i> s </i> ، وفهرس المجموعة * <i> g </i> ، التعبيرات <i> m. </i> <tt> المجموعة (</tt> <i> g </i> <tt>) </tt> و * و * <i> s. </i> <tt> substring (</tt> <i> m. </i> <tt> ابدأ (</tt> <i> g </i> <tt>) ، </tt> <i> m. </i> <tt> end (</tt> <i> g </i> <tt>) </tt> * </p> * * <p> <a href = "pattern.html#cg"> يتم فهرسة مجموعات التقاط </a> من اليسار * إلى اليمين ، بدءًا من واحد. تشير المجموعة Zero إلى النمط بأكمله ، لذلك * التعبير <TT> M.Group (0) </tt> يعادل <tt> M.Group () </tt>. * </p> * * <p> إذا كانت المباراة ناجحة ولكن المجموعة المحددة فشلت في مطابقة * أي جزء من تسلسل الإدخال ، ثم يتم إرجاع <tt> null </tt>. لاحظ * أن بعض المجموعات ، على سبيل المثال <tt> (a *) </tt> ، تطابق السلسلة الفارغة. * ستُرجع هذه الطريقة السلسلة الفارغة عندما تطابق مثل هذه المجموعة بنجاح * السلسلة الفارغة في الإدخال. </p> * param group * فهرس مجموعة التقاط في نمط هذا المطابقة * * return (ربما فارغ) تم التقاطه لاحقًا من قبل المجموعة * أثناء المباراة السابقة ، أو <tt> null </tt> إذا فشل المجموعة * في حالة فهرس internes * التقاط مجموعة في النمط * مع فهرس معين */مجموعة السلسلة العامة (مجموعة int) {if (first <0) رمي جديد aluctalstateException ("لا توجد مطابقة") ؛ if (group <0 || group> groupCount ()) رمي indexoutofboundsexception جديد ("no group" + group) ؛ if ((المجموعات [المجموعة*2] == -1) || (المجموعات [المجموعة*2+1] == -1)) إرجاع فارغ ؛ return getSubsequence (المجموعات [المجموعة * 2] ، تحاول المجموعات [المجموعة * 2 + 1]). ToString () ؛}/** * محاولة العثور على التسلسل التالي لتسلسل الإدخال الذي يتطابق مع النمط. * * <p> تبدأ هذه الطريقة في بداية منطقة هذا المطابقة ، أو إذا كان * الاحتجاج السابق للطريقة ناجحًا ولم يتم إعادة تعيين المطابقة * في الشخصية الأولى التي لا تتطابق معها المباراة السابقة. * * <p> إذا نجحت المباراة ، فيمكن الحصول على مزيد من المعلومات عبر * <tt> ابدأ </tt> و <tt> end </tt> و <tt> مجموعة </tt>. </p> * * regurn <tt> true </tt> if ، وفقط ، إذا كان تسلسل الإدخال * يتطابق مع نمط هذا المطابقة */public boolean find () {int nextsearchIndex = last ؛ if (nextsearchIndex == first) NextSearchIndex ++ ؛ // في حالة بدء البحث التالي قبل المنطقة ، ابدأ في المنطقة إذا كان (NextSearchIndex <من) NextSearchIndex = من ؛ // إذا بدأ البحث التالي خارج المنطقة ، فإنه يفشل إذا (nextsearchIndex> إلى) {for (int i = 0 ؛ i <groups.length ؛ i ++) مجموعات [i] = -1 ؛ العودة كاذبة } بحث الإرجاع (NextSearchIndex) ؛} /*** يبدأ البحث للعثور على نمط داخل الحدود المحددة. * تمتلئ المجموعات بالقيم الافتراضية ويتم استدعاء مطابقة الجذر * لجهاز الحالة. ستحتفظ آلة الولاية بالدولة * للمباراة حيث تستمر في هذا المطابقة. * * matcher.from لم يتم تعيينه هنا ، لأنه الحدود "الصلبة" * لبداية البحث التي سيتم تعيين المراسات إليها. من Param * هو الحدود "اللينة" لبداية البحث ، مما يعني أن * regex يحاول مطابقة هذا الفهرس ولكن ^ لن يتطابق هناك. تبدأ المكالمات اللاحقة إلى أساليب البحث عند حدود جديدة "ناعمة" وهي * نهاية المباراة السابقة. */boolean search (int from) {this.hitend = false ؛ this.requireend = false ؛ من = من <0؟ 0: من ؛ this.first = from ؛ this.oldlast = oldlast <0؟ من: Oldlast ؛ لـ (int i = 0 ؛ i <groups.length ؛ i ++) مجموعات [i] = -1 ؛ قبول mode = nooanchor ؛ نتيجة منطقية = parentPattern.root.match (هذا ، من النص) ؛ if (! النتيجة) this.first = -1 ؛ this.oldlast = this.last ؛ نتيجة العودة ؛} ...}السبب هو هذا: إذا لم تتصل بالطريقة Find أولاً واتصل بالمجموعة مباشرة ، فيمكنك العثور على مجموعة مكالمات Group Method (مجموعة INT). هناك إذا كان أول <0 في جسم الطريقة للطريقة. من الواضح أن هذا الشرط صحيح هنا ، لأن القيمة الأولية لـ First هي -1 ، لذلك سيتم طرح استثناء هنا. ومع ذلك ، إذا اتصلت بطريقة البحث ، فيمكنك العثور على أن البحث (NextSearchIndex) سيتم استدعاؤه في النهاية. لاحظ أن NextSearchIndex هنا قد تم تعيينه بواسطة Last ، وقيمة Last 0 ، ثم القفز إلى طريقة البحث
البحث المنطقي (int from) {this.hitend = false ؛ this.requireend = false ؛ من = من <0؟ 0: من ؛ this.first = from ؛ this.oldlast = oldlast <0؟ من: Oldlast ؛ لـ (int i = 0 ؛ i <groups.length ؛ i ++) مجموعات [i] = -1 ؛ قبول mode = nooanchor ؛ نتيجة منطقية = parentPattern.root.match (هذا ، من النص) ؛ if (! النتيجة) this.first = -1 ؛ this.oldlast = this.last ؛ نتيجة العودة ؛} يتم تمرير NextSearchIndex هذا من ، ويتم تعيينه إلى أولاً في جسم الطريقة. لذلك ، بعد استدعاء طريقة البحث ، لم يعد أول هذا -1 ، ولا يرمي استثناء.
تم تحميل الرمز المصدر إلى Baidu NetDisk: http://pan.baidu.com/1dfwtvnz
المشكلات المذكورة أعلاه محطمة نسبيًا ، وهي بعض الملخصات عند مواجهة المشكلات وحلها. ستكون هناك مشاكل أخرى أثناء عمليات محددة. إذا كان لديك أي أسئلة أو اقتراحات ، فلا تتردد في ذكر ^ ^.
أخيرًا ، ضع بضع أجزاء من البيانات المزروعة حتى الآن
جدول السجل
من بينها ، يتم تخزين 79،032 ، و 48،471 صفحة ويب زحف
طاولة الفيلم
حاليا ، تم الزحف 2964 أعمال الأفلام والتلفزيون
جدول التعليقات
تم زحف 29،711 السجل
ما سبق هو كل محتوى هذه المقالة. آمل أن يكون ذلك مفيدًا لتعلم الجميع وآمل أن يدعم الجميع wulin.com أكثر.