ฉันเคยใช้ชุดรวบรวมข้อมูลมาก่อนเช่นการใช้ nutch เพื่อรวบรวมข้อมูลเมล็ดที่ระบุค้นหาตามข้อมูลที่รวบรวมข้อมูลและดูที่ซอร์สโค้ดบางส่วน แน่นอน Nutch พิจารณาซอฟต์แวร์รวบรวมข้อมูลอย่างละเอียดและพิถีพิถัน เมื่อใดก็ตามที่ฉันเห็นข้อมูลหน้าเว็บและข้อมูลการประมวลผลที่ถูกรวบรวมข้อมูลบนหน้าจอฉันมักจะรู้สึกว่านี่เป็นเทคโนโลยีที่มืดมาก คราวนี้ฉันใช้ประโยชน์จากโอกาสในการแยกแยะ MVC ในฤดูใบไม้ผลิและต้องการสร้างตัวรวบรวมข้อมูลตัวเล็ก ๆ ด้วยตัวเอง ไม่สำคัญว่าฉันจะได้รับข้อบกพร่องเล็ก ๆ น้อย ๆ ได้อย่างง่ายดาย ทั้งหมดที่ฉันต้องการคือเว็บไซต์เมล็ดพันธุ์ที่สามารถรวบรวมข้อมูลที่ฉันต้องการ หากมีข้อยกเว้นอาจเป็นเพราะ API บางตัวถูกใช้อย่างไม่เหมาะสมหรืออาจพบสถานะคำขอ HTTP ที่ผิดปกติหรือมีปัญหากับการอ่านและเขียนฐานข้อมูล ในกระบวนการรายงานข้อยกเว้นและการแก้ไขข้อยกเว้น Jewelcrawler (ชื่อเล่นของลูกชาย) สามารถรวบรวมข้อมูลได้อย่างอิสระและยังมีทักษะเล็ก ๆ สำหรับการวิเคราะห์ทางอารมณ์ตามอัลกอริทึม Word2veC
อาจมีข้อยกเว้นที่ไม่ทราบว่ารอการแก้ไขในภายหลังและประสิทธิภาพบางอย่างจำเป็นต้องได้รับการปรับให้เหมาะสมเช่นการโต้ตอบกับฐานข้อมูลการอ่านและการเขียนข้อมูล ฯลฯ อย่างไรก็ตามฉันไม่มีพลังงานมากนักที่จะนำสิ่งนี้มาใช้ในปีนี้ บทความสองบทความแรกส่วนใหญ่มุ่งเน้นไปที่ฟังก์ชั่นและผลลัพธ์ บทความนี้พูดถึงวิธีการที่ Jewelcrawler เกิดและวางรหัสใน GitHub (ที่อยู่ซอร์สโค้ดในตอนท้ายของบทความ) หากคุณสนใจคุณสามารถให้ความสนใจ (สำหรับการสื่อสารและการเรียนรู้เท่านั้นโปรด Douban โปรด Douban มีความจริงใจและอันตรายน้อยลง)
การแนะนำสภาพแวดล้อม
เครื่องมือพัฒนา: Intellij Idea 14
ฐานข้อมูล: MySQL 5.5 + เครื่องมือการจัดการฐานข้อมูล Navicat (สามารถใช้เพื่อเชื่อมต่อกับฐานข้อมูลแบบสอบถาม)
ภาษา: Java
การจัดการแพ็คเกจ JAR: Maven
การจัดการเวอร์ชัน: Git
โครงสร้างไดเรกทอรี
ใน
com.ansj.vec คือการใช้งานรุ่น Java ของอัลกอริทึม Word2vec
com.jackie.crawler.doubanmovie เป็นโมดูลการใช้งานตัวรวบรวมข้อมูลซึ่งรวมถึงรวมถึง
บางแพ็คเกจว่างเปล่าเนื่องจากโมดูลเหล่านี้ยังไม่ได้ใช้
โมดูลทรัพยากรเก็บไฟล์การกำหนดค่าและไฟล์ทรัพยากรเช่น
โมดูลทดสอบเป็นโมดูลทดสอบที่ใช้ในการเขียน UT
การกำหนดค่าฐานข้อมูล
1. เพิ่มแพ็คเกจการพึ่งพา
Jewelcrawler ใช้การจัดการ Maven ดังนั้นคุณจะต้องเพิ่มการพึ่งพาที่สอดคล้องกันใน pom.xml
<การพึ่งพา> <roupId> org.springframework </groupId> <ratifactId> Spring-Jdbc </artifactid> <sersion> 4.1.1.Release </Sident> </การพึ่งพา> <การพึ่งพา> <roupId> Commons-Pool </GroupId> <RoupID> Commons-DBCP </groupId> <ratifactId> Commons-DBCP </artifactId> <ratifactId> Commons-DBCP </artifactId> <ratifactId> Commons-DBCP </artifactId> <ArtIfactId> mysql-connector-java </artifactid> <persion> 5.1.38 </เวอร์ชัน> </การพึ่งพา> <การพึ่งพาอาศัย> <roupId> mysql </groupId> <ArtIfactId> MySQL-connector-Java </artifactId>
2. ประกาศถั่วแหล่งข้อมูล
เราจำเป็นต้องประกาศถั่วของแหล่งข้อมูลใน beans.xml
<บริบท: สถานที่ตั้งสถานที่ตั้งตำแหน่ง = "classpath*:*. คุณสมบัติ"/> <bean id = "dataSource" destroy-method = "close"> <property name = "driverclassName" value = "$ {jdbc.driver}"/> value = "$ {jdbc.username}"/> <property name = "password" value = "$ {jdbc.password}"/>หมายเหตุ: นี่คือไฟล์การกำหนดค่าภายนอก JDBC.Properties ที่ถูกผูกไว้และพารามิเตอร์ของแหล่งข้อมูลเฉพาะถูกอ่านจากไฟล์นี้
หากคุณพบปัญหา "SQL [แทรกลงในค่าผู้ใช้ (ID) (?)]; ชื่อฟิลด์ 'ชื่อ' ไม่มีค่าเริ่มต้น" วิธีแก้ปัญหาคือการตั้งค่าฟิลด์ที่สอดคล้องกันของตารางเป็นฟิลด์การเติบโตของตนเอง
ปัญหาที่พบเมื่อแยกหน้า
สำหรับข้อมูลหน้าเว็บที่คุณคลานคุณต้องแยกวิเคราะห์โครงสร้าง DOM และรับข้อมูลที่คุณต้องการ ในช่วงเวลานี้คุณพบข้อผิดพลาดต่อไปนี้
org.htmlparser.node ไม่ได้รับการยอมรับ
วิธีแก้ปัญหา: เพิ่มการพึ่งพาแพ็คเกจ JAR
<Ederency> <roupId> org.htmlparser </groupId> <ratifactId> htmlparser </artifactid> <cersion> 1.6 </version>
org.apache.http.htttpentity ไม่ได้รับการยอมรับ
วิธีแก้ปัญหา: เพิ่มการพึ่งพาแพ็คเกจ JAR
<Ederency> <roupId> org.apache.httpComponents </groupId> <ratifactId> httpClient </artifactId> <version> 4.5.2 </เวอร์ชัน>
แน่นอนว่านี่เป็นปัญหาที่พบในช่วงเวลาและการวิเคราะห์หน้าโดย JSOUP ถูกนำมาใช้ในที่สุด
ความเร็วในการดาวน์โหลดคลังสินค้า Maven ช้า
ฉันใช้พื้นที่เก็บข้อมูลกลาง Maven เริ่มต้นมาก่อนและความเร็วในการดาวน์โหลดแพ็คเกจ Jar นั้นช้ามาก ฉันไม่รู้ว่ามันเป็นปัญหาเครือข่ายหรือเหตุผลอื่น ๆ ของฉัน ต่อมาฉันพบที่เก็บ Maven ของ Alibaba Cloud Online หลังจากการอัปเดตขอแนะนำให้อาเจียนเลือดเมื่อเทียบกับก่อน
<มิเรอร์> <มิเรอร์> <id> Alimaven </id> <name> Aliyun Maven </name> <url> http://maven.aliyun.com/nexus/content/groups/public/ </url>
ค้นหาไฟล์ settings.xml ของ Maven และเพิ่มภาพนี้
วิธีการอ่านไฟล์ภายใต้โมดูลทรัพยากร
ตัวอย่างเช่นอ่านไฟล์ seed.properties
@Test โมฆะสาธารณะ testFile () {ไฟล์ seedFile = ไฟล์ใหม่ (this.getClass (). getResource ("/seed.properties"). getPath ()); System.out.print ("============" + seedfile.length () + "============"); -เกี่ยวกับการแสดงออกปกติ
เมื่อใช้นิพจน์ปกติ Regrex หากมีการจับคู่รูปแบบที่กำหนดไว้คุณจะต้องโทรหาวิธีการค้นหาของผู้จับคู่ก่อนที่คุณจะสามารถใช้วิธีการกลุ่มเพื่อค้นหาสตริงย่อย ไม่มีวิธีค้นหาผลลัพธ์ที่คุณต้องการโดยเรียกวิธีการกลุ่มโดยตรง
ฉันดูที่ซอร์สโค้ดของคลาส Matcher ด้านบน
แพ็คเกจ java.util.regex; นำเข้า java.util.Objects; Matcher คลาสสุดท้ายของ Public Public ใช้ MatchResult { /*** วัตถุรูปแบบที่สร้างตัวจับคู่นี้ */ รูปแบบ parentpattern; /*** ที่เก็บข้อมูลที่ใช้โดยกลุ่ม พวกเขาอาจมีค่าที่ไม่ถูกต้องหาก * กลุ่มถูกข้ามระหว่างการจับคู่ */ int [] กลุ่ม; /*** ช่วงภายในลำดับที่จะจับคู่ Anchors * จะตรงกับขอบเขต "ยาก" เหล่านี้ การเปลี่ยนภูมิภาค * เปลี่ยนค่าเหล่านี้ */ int จาก, ถึง; /** * Lookbehind ใช้ค่านี้เพื่อให้แน่ใจว่าการจับคู่แบบ subexpression * สิ้นสุดลง ณ จุดที่พบ Lookbehind */ int lookbehindto; /*** สตริงต้นฉบับที่ตรงกัน */ Charsequence Text; /*** สถานะการจับคู่ที่ใช้โดยโหนดสุดท้าย Noanchor จะถูกใช้เมื่อการจับคู่ * ไม่จำเป็นต้องใช้อินพุตทั้งหมด Endanchor คือ * โหมดที่ใช้สำหรับการจับคู่อินพุตทั้งหมด */ endanchor int สุดท้ายคงที่ = 1; คงที่ int noanchor = 0; int acceptmode = noanchor; /*** ช่วงของสตริงที่ตรงกับรูปแบบล่าสุด หากการจับคู่ * ครั้งสุดท้ายล้มเหลวก่อนอื่นคือ -1; ครั้งแรกถือ 0 จากนั้นมัน * ถือดัชนีของการสิ้นสุดของการแข่งขันสุดท้าย (ซึ่งเป็นจุดเริ่มต้น * การค้นหาถัดไปเริ่มต้น) */ int แรก = -1, last = 0; /*** ดัชนีสิ้นสุดของสิ่งที่ตรงกันในการดำเนินการนัดสุดท้าย */ int oldLast = -1; /*** ดัชนีของตำแหน่งสุดท้ายที่ใช้ในการทดแทน */ int LastAppendPosition = 0; /** * พื้นที่เก็บข้อมูลที่ใช้โดยโหนดเพื่อบอกว่าพวกเขาทำซ้ำอะไรในรูปแบบ * และจุดเริ่มต้นของกลุ่ม โหนดตัวเองไร้สัญชาติ * ดังนั้นพวกเขาจึงพึ่งพาฟิลด์นี้เพื่อถือสถานะในระหว่างการแข่งขัน */ int [] ท้องถิ่น; /** * บูลีนระบุว่าอินพุตเพิ่มเติมสามารถเปลี่ยน * ผลลัพธ์ของการจับคู่สุดท้ายได้หรือไม่ * * หากการเข้าชมเป็นจริงและพบการจับคู่การป้อนข้อมูลเพิ่มเติม * อาจทำให้พบการจับคู่ที่แตกต่างกัน * หากไม่พบการเข้าชมและไม่พบการจับคู่การป้อนข้อมูลเพิ่มเติม * อาจทำให้พบการจับคู่ได้ * หากไม่พบ Hitend เป็นเท็จและไม่พบการแข่งขันการป้อนข้อมูลเพิ่มเติม * จะไม่ทำให้พบการจับคู่ */ บูลีนฮิต; /** * บูลีนระบุว่าอินพุตเพิ่มเติมสามารถเปลี่ยน * การจับคู่บวกเป็นลบได้หรือไม่ * * หากต้องการความเป็นจริงและพบการจับคู่การป้อนข้อมูลเพิ่มเติม * อาจทำให้การจับคู่หายไป * หากต้องการความผิดพลาดเป็นเท็จและพบการแข่งขันแล้วอินพุตเพิ่มเติม * อาจเปลี่ยนการแข่งขัน แต่การจับคู่จะไม่หายไป * หากไม่พบการแข่งขันจำเป็นต้องมีความหมายไม่มีความหมาย */ บูลีนจำเป็นต้องมี; /** * หากโปร่งใสเป็นจริงขอบเขตของภูมิภาค * ของผู้จับคู่นี้จะโปร่งใสในการมองดู, Lookbehind, * และการจับคู่ขอบเขตที่พยายามมองเห็นเกินกว่าพวกเขา */ boolean transparentbounds = false; /** * หาก Anchoringbounds เป็นจริงขอบเขตของภูมิภาค * ตัวจับคู่นี้จะตรงกับจุดยึดเช่น ^ และ $ */ บูลีน anchoringbounds = true; /*** ไม่มีตัวสร้างเริ่มต้น * / matcher () {} / *** matchers ทั้งหมดมีสถานะที่ใช้โดยรูปแบบระหว่างการจับคู่ */matcher (รูปแบบ parent, charsequence text) {this.parentPattern = parent; this.text = text; // จัดสรรสถานะการจัดเก็บสถานะ int parentgroupcount = math.max (parent.capturinggroupcount, 10); กลุ่ม = ใหม่ int [parentgroupcount * 2]; locals = new int [parent.localcount]; // ใส่ฟิลด์ลงในสถานะเริ่มต้นรีเซ็ต ();} ..../*** ส่งคืนคำต่อมาที่ตรงกับการจับคู่ก่อนหน้า * * <p> สำหรับตัวจับคู่ <i> m </i> พร้อมลำดับอินพุต <i> s </i>, * นิพจน์ <i> m. </i> <tt> กลุ่ม () </tt> และ * <i> s. </i> <tt> <i> m. </i> <tt> end ()) </tt> * เทียบเท่า </p> * * <p> โปรดทราบว่าบางรูปแบบตัวอย่างเช่น <tt> a * </tt>, จับคู่สตริง * ที่ว่างเปล่า วิธีนี้จะส่งคืนสตริงเปล่าเมื่อรูปแบบ * ตรงกับสตริงว่างในอินพุตสำเร็จ </p> * * @return ลำดับ (อาจว่างเปล่า) ที่ตรงกับการจับคู่ก่อนหน้า, * ในรูปแบบสตริง * * @throws unlilegalStateException * หากยังไม่มีการพยายามจับคู่ * หรือหากการดำเนินการจับคู่ก่อนหน้านี้ล้มเหลว */กลุ่มสตริงสาธารณะ () {กลุ่มส่งคืน (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> </p> * * <p> <a href = "pattern.html#cg"> การจับกลุ่ม </a> ถูกจัดทำดัชนีจากซ้าย * ไปทางขวาเริ่มต้นที่หนึ่ง กลุ่มศูนย์แสดงถึงรูปแบบทั้งหมดดังนั้น * นิพจน์ <tt> m.group (0) </tt> เทียบเท่ากับ <tt> m.group () </tt> * </p> * * <p> หากการจับคู่สำเร็จ แต่กลุ่มที่ระบุไม่สามารถจับคู่ * ส่วนใดส่วนหนึ่งของลำดับอินพุตจากนั้น <tt> null </tt> จะถูกส่งคืน หมายเหตุ * ว่าบางกลุ่มตัวอย่างเช่น <tt> (a *) </tt> จับคู่สตริงว่าง * วิธีนี้จะส่งคืนสตริงว่างเมื่อกลุ่มดังกล่าวประสบความสำเร็จ * ตรงกับสตริงว่างในอินพุต </p> * @param Group * ดัชนีของกลุ่มการจับภาพในรูปแบบของตัวจับคู่นี้ * * @return (อาจว่างเปล่า) ที่ถูกจับโดยกลุ่ม * ในระหว่างการแข่งขันก่อนหน้านี้หรือ <tt> null </tt> หากกลุ่ม * ล้มเหลวในการจับคู่ ไม่มีกลุ่มการจับภาพในรูปแบบ * กับดัชนีที่กำหนด */กลุ่มสตริงสาธารณะ (กลุ่ม int) {ถ้า (แรก <0) โยนใหม่ผิดกฎหมายเตตเตจ ("ไม่พบการจับคู่"); if (กลุ่ม <0 || group> groupCount ()) โยน indexoutofboundsexception ใหม่ ("ไม่มีกลุ่ม" + กลุ่ม); if ((กลุ่ม [กลุ่ม*2] == -1) || (กลุ่ม [กลุ่ม*2+1] == -1)) ส่งคืนค่า null; Return getSubexence (กลุ่ม [กลุ่ม * 2], กลุ่ม [กลุ่ม * 2 + 1]). toString ();}/** * พยายามค้นหาลำดับถัดไปของลำดับอินพุตที่ตรงกับรูปแบบ * * <p> วิธีนี้เริ่มต้นที่จุดเริ่มต้นของภูมิภาคของผู้จับคู่นี้หรือถ้า * การเรียกใช้ก่อนหน้านี้ของวิธีนี้ประสบความสำเร็จและตัวจับคู่นั้นไม่ได้ถูกรีเซ็ตตั้งแต่ตัวละครแรกที่ไม่ตรงกันโดยการจับคู่ * ก่อนหน้านี้ * * <p> หากการจับคู่ประสบความสำเร็จสามารถรับข้อมูลเพิ่มเติมได้ผ่านทาง * <tt> เริ่มต้น </tt>, <tt> สิ้นสุด </tt> และ <tt> กลุ่ม </tt> </p> * * @return <tt> true </tt> ถ้าและเฉพาะในกรณีที่ลำดับของอินพุต * ลำดับตรงกับรูปแบบของตัวจับคู่นี้ */บูลีนสาธารณะค้นหา () {int nextsearchindex = สุดท้าย; if (nextSearchIndex == ก่อน) nextSearchIndex ++; // หากการค้นหาครั้งต่อไปเริ่มต้นก่อนภูมิภาคให้เริ่มต้นที่ภูมิภาคถ้า (NextSearchIndex <จาก) nextSearchIndex = จาก; // ถ้าการค้นหาครั้งต่อไปเริ่มต้นเกินกว่าภูมิภาคจะล้มเหลวถ้า (nextsearchindex> ถึง) {สำหรับ (int i = 0; i <groups.length; i ++) กลุ่ม [i] = -1; กลับเท็จ; } ส่งคืนการค้นหา (NextSearchIndex);} /*** เริ่มการค้นหาเพื่อค้นหารูปแบบภายในขอบเขตที่กำหนด * กลุ่มจะถูกเติมด้วยค่าเริ่มต้นและการจับคู่ของรูท * ของเครื่องสถานะเรียกว่า เครื่องรัฐจะถือสถานะ * ของการแข่งขันตามที่ดำเนินการในตัวจับคู่นี้ * * matcher.from ไม่ได้ตั้งไว้ที่นี่เพราะมันเป็นขอบเขต "ยาก" ของการเริ่มต้นของการค้นหาที่จุดยึดจะตั้งค่า จาก param * เป็นขอบเขต "อ่อน" ของการเริ่มต้นของการค้นหาซึ่งหมายความว่า * regex พยายามที่จะจับคู่ที่ดัชนีนั้น แต่ ^ จะไม่ตรงกับที่นั่น การโทรไปยังวิธีการค้นหาที่ตามมาเริ่มต้นที่ขอบเขต "อ่อน" ใหม่ซึ่งเป็นจุดสิ้นสุดของการจับคู่ก่อนหน้า */การค้นหาบูลีน (int จาก) {this.hitend = false; this.requireend = false; จาก = จาก <0? 0: จาก; this.first = จาก; this.oldLast = oldLast <0? จาก: Oldlast; สำหรับ (int i = 0; i <groups.length; i ++) กลุ่ม [i] = -1; AcceptMode = noanchor; ผลลัพธ์บูลีน = parentpattern.root.match (นี่, จาก, ข้อความ); ถ้า (! ผลลัพธ์) this.first = -1; this.oldLast = this.last; ผลตอบแทนผลลัพธ์} ... }เหตุผลคือ: หากคุณไม่เรียกวิธีการค้นหาก่อนและโทรหากลุ่มโดยตรงคุณสามารถพบว่ากลุ่มเมธอดการเรียกกลุ่ม (กลุ่ม int) มีถ้า <0 ในวิธีการของวิธีการ เห็นได้ชัดว่าเงื่อนไขนี้เป็นจริงที่นี่เพราะค่าเริ่มต้นของแรกคือ -1 ดังนั้นข้อยกเว้นจะถูกโยนลงที่นี่ อย่างไรก็ตามหากคุณเรียกวิธีการค้นหาคุณสามารถค้นหาการค้นหา (NextSearchIndex) ในที่สุด โปรดทราบว่า NextSearchIndex ที่นี่ได้รับการกำหนดโดยล่าสุดและค่าสุดท้ายคือ 0 จากนั้นข้ามไปที่วิธีการค้นหา
การค้นหาบูลีน (int จาก) {this.hitend = false; this.requireend = false; จาก = จาก <0? 0: จาก; this.first = จาก; this.oldLast = oldLast <0? จาก: Oldlast; สำหรับ (int i = 0; i <groups.length; i ++) กลุ่ม [i] = -1; AcceptMode = noanchor; ผลลัพธ์บูลีน = parentpattern.root.match (นี่, จาก, ข้อความ); ถ้า (! ผลลัพธ์) this.first = -1; this.oldLast = this.last; ผลตอบแทนผลลัพธ์;} NextSearchIndex นี้ถูกส่งผ่านจากและจากนั้นได้รับมอบหมายให้เป็นคนแรกในร่างกายวิธีการ ดังนั้นหลังจากเรียกใช้วิธีการค้นหาสิ่งแรกนี้คือ -1 อีกต่อไปและมันไม่ได้โยนข้อยกเว้น
ซอร์สโค้ดได้ถูกอัปโหลดไปยัง Baidu NetDisk: http://pan.baidu.com/s/1dfwtvnz
ปัญหาที่กล่าวถึงข้างต้นค่อนข้างแตกสลายและพวกเขาเป็นบทสรุปเมื่อพบปัญหาและแก้ปัญหา จะมีปัญหาอื่น ๆ ในระหว่างการดำเนินการเฉพาะ หากคุณมีคำถามหรือคำแนะนำใด ๆ โปรดอย่าลังเลที่จะพูดถึง ^^
ในที่สุดก็ใส่ข้อมูลที่คลานไปสองสามชิ้นจนถึงตอนนี้
ตารางบันทึก
ในหมู่พวกเขามีการจัดเก็บ 79,032 และหน้าเว็บคลาน 48,471 หน้าคือ
โต๊ะหนัง
ปัจจุบันมีการรวบรวมข้อมูลภาพยนตร์และโทรทัศน์ 2964 รายการ
ตารางความคิดเห็น
29,711 บันทึกถูกรวบรวมข้อมูล
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่ามันจะเป็นประโยชน์ต่อการเรียนรู้ของทุกคนและฉันหวังว่าทุกคนจะสนับสนุน wulin.com มากขึ้น