또한 지정된 씨앗을 기어 다니기 위해 Nutch를 사용하고, 크롤링 된 데이터를 기반으로 검색하고, 거의 소스 코드를보고 등 크롤러를 사용했습니다. 물론 Nutch는 크롤러를 매우 포괄적이고 세 심하게 고려합니다. 화면에 기어 들어간 웹 페이지 정보와 처리 정보가 볼 때마다 항상 이것이 매우 어두운 기술이라고 생각합니다. 이번에는 Spring MVC를 분류 할 수있는 기회를 이용했고 혼자서 작은 크롤러를 만들고 싶었습니다. 작은 버그를 쉽게 얻을 수 있는지 여부는 중요하지 않습니다. 내가 필요한 것은 내가 원하는 정보를 기어 다닐 수있는 종자 웹 사이트뿐입니다. 예외가있는 경우 일부 API가 부적절하게 사용되거나 비정상적인 HTTP 요청 상태에 직면하거나 데이터베이스 읽기 및 쓰기에 문제가 있기 때문일 수 있습니다. 예외를보고하고 예외를 해결하는 과정에서 JewelCrawler (Son의 별명)는 이미 독립적으로 데이터를 크롤링 할 수 있으며 Word2VEC 알고리즘을 기반으로 감정 분석을위한 작은 기술도 있습니다.
나중에 해결되기를 기다리는 예외가있을 수 있으며 데이터베이스와의 상호 작용, 읽기 및 쓰기 데이터 등과 같이 일부 성능을 최적화해야합니다. 그러나 해에이를 넣을 에너지가 많지 않으므로 오늘 간단한 요약을하겠습니다. 처음 두 기사는 주로 기능과 결과에 중점을 둡니다. 이 기사에서는 JewelCrawler가 어떻게 태어 났는지에 대해 이야기하고 코드를 GitHub에 넣습니다 (소스 코드 주소는 기사 끝에 있습니다). 관심이 있으시면주의를 기울일 수 있습니다 (의사 소통과 학습을 위해서만, Douban을 제발하십시오. 더반. 더 진실하고 덜 해를 끼치십시오)
환경 소개
개발 도구 : Intellij 아이디어 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에 해당 종속성 만 추가하면됩니다.
<pectionency> <groupId> org.springframework </groupid> <artifactid> spring-jdbc </artifactid> <버전> 4.1.1. Release </version> </fectionency> <groupId> commons-pool </groupIcid> <artifactid> commons-pool </artifactid> </version> </rependency> <groupid> commons-dbcp </groupid> <artifactid> commons-dbcp </artifactid> <artifactid> commons-dbcp </artifactid> commons-dbcp </artifactid> <버전> 1.4 </version> </dependency> <groupid> mysql> mysql> <아티 팩트> mysql-connector-java </artifactid> <bersion> 5.1.38 </version> </dependency> <groupid> mysql </groupid> <artifactid> mysql-connector-java </artifactid> <bersion> 5.1.38 </dependency> </dependency>
2. 데이터 소스 Bean을 선언합니다
Beans.xml에서 데이터 소스의 Bean을 선언해야합니다.
<context : property-placeholder location = "classpath*:*. 속성"/> <bean id = "dataSource"dastaSource "dasve-method ="close "> <속성 이름 ="driver className "value ="$ {jdbc.driver} "/> <속성 이름 ="url "value ="$ {jdbc.url} "/> <property name ="userame " value = "$ {jdbc.username}"/> <속성 이름 = "password"value = "$ {jdbc.password}"/>참고 : 다음은 외부 구성 파일 JDBC.Properties 바운드이며 특정 데이터 소스의 매개 변수는이 파일에서 읽습니다.
문제가 발생하면 "SQL [user (id) value (?)]; 필드 '이름'에 기본값이 없습니다." 해결책은 테이블의 해당 필드를 자체 성장 필드로 설정하는 것입니다.
페이지를 구문 분석 할 때 문제가 발생합니다
크롤링 한 웹 페이지 데이터의 경우 DOM 구조를 구문 분석하고 원하는 데이터를 가져와야합니다. 이 기간 동안 다음 오류가 발생합니다
org.htmlparser.node는 인식되지 않습니다
해결책 : JAR 패키지 종속성을 추가하십시오
<pectionency> <groupid> org.htmlparser </groupid> <artifactid> htmlparser </artifactid> <bersion> 1.6 </version> </fectionency>
org.apache.http.httpentity는 인식되지 않습니다
해결책 : JAR 패키지 종속성을 추가하십시오
<pectionency> <groupid> org.apache.httpcomponents </groupid> <artifactid> httpclient </artifactid> <버전> 4.5.2 </version> </fectionency>
물론 이것은 기간 동안 발생하는 문제이며 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>
maven의 settings.xml 파일을 찾아이 이미지를 추가하십시오.
리소스 모듈에서 파일을 읽는 방법
예를 들어 Seed.Properties 파일을 읽으십시오
@test public void testfile () {file seedfile = 새 파일 (this.getClass (). getResource ( "/seed.properties"). getPath ()); System.out.print ( "============" + SeedFile.Length () + "=========="); }정규 표현에 대해
Regrex 정규 표현식을 사용하는 경우 정의 된 패턴이 일치하는 경우 그룹 메소드를 사용하여 하위 문자열을 찾기 전에 매치 자의 찾기 메소드를 호출해야합니다. 그룹 메소드를 직접 호출하여 원하는 결과를 찾을 방법이 없습니다.
위의 매칭 클래스의 소스 코드를 보았습니다.
패키지 java.util.regex; import java.util.objects; public final class matcher는 matchresult { /***이 매칭을 생성 한 패턴 객체를 구현합니다. */ 패턴 PARSINGPATTERN; /*** 그룹이 사용하는 스토리지. * 그룹이 일치하는 동안 건너 뛰면 잘못된 값을 포함 할 수 있습니다. */ int [] 그룹; /*** 일치 할 시퀀스 내의 범위. 앵커 *는이 "하드"경계에서 일치합니다. 지역 변경 *이 값을 변경합니다. */ int에서까지; /** * LookBehind는이 값을 사용하여 Subexpression * 일치가 LookBehind가 발생한 지점에서 끝나는지 확인합니다. */ int lookbehindto; /*** 원래 문자열이 일치합니다. */ charSequence 텍스트; /*** 마지막 노드에서 사용하는 매칭 상태. noanchor는 A * 매치가 모든 입력을 소비 할 필요가 없을 때 사용됩니다. Endanchor는 * 모든 입력을 일치시키는 데 사용되는 모드입니다. */ static final int endanchor = 1; 정적 최종 int noanchor = 0; int acceptmode = noanchor; /*** 마지막으로 패턴과 일치하는 문자열 범위. 마지막 * 일치가 실패하면 먼저 -1입니다. 마지막으로 0을 유지 한 다음 * It *는 마지막 경기의 끝의 인덱스를 보유합니다 ( * 다음 검색이 시작되는 곳). */ int first = -1, last = 0; /*** 마지막 경기 작업에서 일치하는 엔드 인덱스. */ int oldlast = -1; /*** 대체에 적용된 마지막 위치의 색인. */ int lastAppendPosition = 0; /** * 노드가 사용하는 스토리지는 패턴에 어떤 반복이 있는지, 그리고 그룹이 시작되는지를 알려줍니다. 노드 자체는 상태가 없기 때문에이 필드에 의존하여 경기 중에 상태를 유지합니다. */ int [] 현지인; /** * 부울이 더 이상 입력이 변경 될 수 있는지 여부를 나타내는 * 마지막 경기의 결과를 나타냅니다. * * HITEND가 사실이고 경기가 발견되면 더 많은 입력 *이 다른 일치를 찾을 수 있습니다. * HITEND가 사실이고 경기가 발견되지 않으면 더 많은 * 입력으로 인해 일치가 발견 될 수 있습니다. * Hitend가 False이고 일치가 발견되지 않으면 더 많은 * 입력으로 인해 일치가 발견되지 않습니다. */ 부울 히텐드; /** * 부울이 더 많은 입력이 변경 될 수 있는지 여부 * 긍정적 인 일치를 부정적인 일치로 바꿀 수 있습니다. * * 요구 사항이 사실이고 경기가 발견되면 더 많은 * 입력으로 인해 일치가 손실 될 수 있습니다. * 요구 사항이 거짓이고 일치가 발견되면 더 많은 * 입력이 일치를 변경할 수 있지만 경기는 손실되지 않습니다. * 경기가 발견되지 않으면 요구 사항에는 의미가 없습니다. */ 부울 요구 사항; /** * 투명한 바운드가 사실이라면,이 매칭 지역의 경계는 룩 헤드, 룩시 딩, * 및 경계를 넘어서 보려고하는 경계 일치 구성을 투명합니다. */ 부울 투명한 바운드 = 거짓; /** * 앵커링 바운드가 사실이라면이 * 매칭 지역 일치 앵커의 경계는 ^ 및 $와 같은 앵커입니다. */ 부울 앵커링 바운드 = true; /*** 기본 생성자가 없습니다. * / matcher () {} / *** 모든 매치자는 경기 중에 패턴별로 사용되는 상태를 가지고 있습니다. */matcher (Pattern Parent, charSevence Text) {this.parentPattern = parent; this.text = 텍스트; // 상태 저장소 int parentgroupcount = math.max (parent.capturinggroupcount, 10); 그룹 = new int [ParentgroupCount * 2]; 지역 주민 = New int [parent.localcount]; // 필드를 초기 상태에 넣습니다. reset ();} ..../*** 이전 일치와 일치하는 입력 후시를 반환합니다. * * * <p> 입력 시퀀스 <i> s </i>를 갖는 매칭 </i>의 경우, * 표현식 <i> m. <i> m. </i> <tt> end ()) </tt> *는 동일합니다. </p> * * <p> 예를 들어 <tt> a * </tt>과 같은 일부 패턴은 빈 * 문자열과 일치합니다. 이 메소드는 패턴 *이 입력의 빈 문자열과 성공적으로 일치 할 때 빈 문자열을 반환합니다. </p> * * @return 이전 일치와 일치하는 (아마도 비어있는) 시퀀스, * 문자열 형식 * * @throws 불법 스테이트 exception * 또는 일치 시합이 아직 시도되지 않은 경우 * 또는 이전 일치 작업이 실패한 경우 */public string group () {return group (0);}/** * 이전 일치 작업 중에 주어진 입력 후속 후속 후속 캡처를 반환합니다. * * * <p>는 매칭 <i> m </i>, 입력 시퀀스 <i> s </i> 및 그룹 인덱스 * <i> g </i>, 표현식 <i> m. <i> s. </i> <tt> 서브 스트링 (</tt> <i> m. </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>가 입력 * * @throws의 일부를 일치시키지 못한 경우 * * * * @throw가 아직 실패했는지 * 또는 * * * * @throw가 아직 실패했는지 * 또는 * * * * @throw. INDEXOUTOFBOUNDSEXCEPTION * 주어진 색인 */public string group (int group)과 함께 패턴에 캡처 그룹이없는 경우 {if (First <0) 새로운 불법 스테이트 렉싱 ( "일치하지 않음"); if (group <0 || group> groupCount ()) 던지기 새로운 indexOutOfBoundSexception ( "그룹 없음" + 그룹); if ((그룹 [그룹*2] == -1) || (그룹 [그룹*2+1] == -1)) return null; return getSubsequence (그룹 [그룹 * 2], 그룹 [그룹 * 2 + 1]). toString ();}/** * 패턴과 일치하는 입력 순서의 다음 순서를 찾으려고 시도합니다. * * <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 ++) 그룹 [i] = -1; 거짓을 반환합니다. } return search (nextsearchIndex);} /*** 주어진 범위 내에서 패턴을 찾기 위해 검색을 시작합니다. * 그룹은 기본값으로 채워져 있으며 상태 머신의 루트 *의 일치가 호출됩니다. State Machine 은이 경기자에서 진행되는 상태에서 경기의 상태를 유지합니다. * * matcher.from은 여기에 설정되지 않았습니다. 앵커가 설정할 검색 시작의 "하드"경계 *이기 때문입니다. From Param *는 검색 시작의 "소프트"경계이므로 * Regex는 해당 색인과 일치하려고하지만 ^는 일치하지 않습니다. 후속 * 검색 방법에 대한 호출은 이전 일치의 끝인 새로운 "소프트"경계에서 시작합니다. */부울 검색 (int from) {this.hitend = false; this.requireend = false; <0에서 =에서? 0 : From; this.first = from; this.oldlast = oldlast <0? 에서 : Oldlast; for (int i = 0; i <groups.length; i ++) 그룹 [i] = -1; acceptMode = noanchor; 부울 결과 = parentpattern.root.match (this, from, text); if (! result) this.first = -1; this.oldlast = this.last; 반환 결과;} ...}그 이유는 다음과 같습니다. 찾기 메소드를 먼저 호출하지 않고 그룹을 직접 호출하지 않으면 그룹 메소드가 그룹 (int group)을 호출하는 것을 찾을 수 있습니다. 메소드의 메소드 본문에 첫 번째 <0이 있습니다. 분명히,이 조건은 첫 번째 값이 -1이기 때문에 여기서는 여기에서 사실입니다. 따라서 여기서 예외가 발생합니다. 그러나 찾기 메소드를 호출하면 검색 (nextsearchIndex)이 호출되는 것을 찾을 수 있습니다. 여기에서 nextsearchIndex는 Last에 의해 지정되었고 Last의 값은 0이며 검색 방법으로 이동합니다.
부울 검색 (int from) {this.hitend = false; this.requireend = false; <0에서 =에서? 0 : From; this.first = from; this.oldlast = oldlast <0? 에서 : Oldlast; for (int i = 0; i <groups.length; i ++) 그룹 [i] = -1; acceptMode = noanchor; 부울 결과 = parentpattern.root.match (this, from, text); if (! result) this.first = -1; this.oldlast = this.last; 반환 결과;} 이 nextsearchIndex는 전달되며, 그 중에서 메소드 본문에서 먼저 할당됩니다. 따라서 Find Method를 호출 한 후 첫 번째는 더 이상 -1이 아니며 예외가 발생하지 않습니다.
소스 코드는 Baidu NetDisk : http://pan.baidu.com/s/1dfwtvnz에 업로드되었습니다.
위에서 언급 한 문제는 상대적으로 산산조각 났으며 문제를 해결하고 해결할 때 요약합니다. 특정 작업 중에 다른 문제가 있습니다. 질문이나 제안이 있으시면 언제든지 언급하십시오 ^^.
마지막으로, 지금까지 크롤링 된 데이터 몇 개를 넣으십시오.
레코드 테이블
그중에는 79,032 명이 저장되고 48,471 개의 크롤링 웹 페이지가
영화 테이블
현재 2964 영화와 텔레비전 작품이 기어 다니고 있습니다
주석 테이블
29,711 개의 기록이 크롤링되었습니다
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.