Skip to content

Commit 87483c6

Browse files
igiguereIsabelle Giguere
andauthored
NUTCH-1732: allow deleting non-parsable documents (#891)
Add CrawlDatum status "parse_failed"/"db_parse_failed", set status in CrawlDbReducer. Handle deletion in IndexerMapReduce. Add unit tests. Improve test for failed parsing in TestCrawlDbStates and TestIndexerMapReduce. TestFetchWithPArseFailure "should" test the fetcher, but it strangely does not fully run most of the time. Disabled while thinking of a solution. Change the type of ExecutorService, to no avail. Fix test conf to have just 1 fetcher thread. Add resource handler Replace the string content with random bytes injected from a resource handler. No change. Test still fails. Fix unit test: set "mime.type.magic"=false to force an exception from ParserFactory. Log a message in ParseSegment when skipping un-parseable content. Remove spaces ant end of lines. Add ASF header. --------- Co-authored-by: Isabelle Giguere <igiguere71@yahoo.ca>
1 parent 5bcb0ab commit 87483c6

18 files changed

Lines changed: 890 additions & 344 deletions

File tree

conf/nutch-default.xml

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1503,6 +1503,14 @@
15031503
</description>
15041504
</property>
15051505

1506+
<property>
1507+
<name>indexer.delete</name>
1508+
<value>false</value>
1509+
<description>Whether the indexer will delete documents that are gone. Gone pages include redirects and duplicates.
1510+
See also: 'link.delete.gone'.
1511+
</description>
1512+
</property>
1513+
15061514
<property>
15071515
<name>indexer.indexwriters.file</name>
15081516
<value>index-writers.xml</value>
@@ -1808,6 +1816,15 @@ CAUTION: Set the parser.timeout to -1 or a bigger value than 30, when using this
18081816
</description>
18091817
</property>
18101818

1819+
<property>
1820+
<name>parser.delete.failed.parse</name>
1821+
<value>false</value>
1822+
<description>Boolean value for whether we should delete a page from the index when parsing the page fails.
1823+
By default this property is deactivated, because it will delete an existing page from the index, where a
1824+
previous fetch produced content that was successfully parsed.
1825+
</description>
1826+
</property>
1827+
18111828
<property>
18121829
<name>parser.store.text</name>
18131830
<value>true</value>
@@ -2258,14 +2275,14 @@ CAUTION: Set the parser.timeout to -1 or a bigger value than 30, when using this
22582275
</property>
22592276

22602277
<!-- index-geoip plugin properties -->
2261-
<!--
2278+
<!--
22622279
To use the index-geoip plugin, you must set 'store.ip.address' to true.
2263-
2280+
22642281
Configure one or more GeoIP databases by setting the corresponding property below.
22652282
Each database type can be enabled independently - set any combination you need.
22662283
Database files must be in MMDB format and available on the Hadoop classpath
22672284
(e.g., place them in $NUTCH_HOME/conf).
2268-
2285+
22692286
More information: https://support.maxmind.com/knowledge-base/articles/maxmind-database-formats
22702287
-->
22712288

@@ -2408,7 +2425,7 @@ CAUTION: Set the parser.timeout to -1 or a bigger value than 30, when using this
24082425
<value></value>
24092426
<description>The values (as strings) to pass into the POJO constructor.
24102427
The POJO must accept a String representation of the NutchDocument's URL
2411-
as the first parameter in the constructor. The values you specify here
2428+
as the first parameter in the constructor. The values you specify here
24122429
will populate the constructor arguments 1,..,n-1 where n=the count of
24132430
arguments to the constructor. Argument #0 will be the NutchDocument's URL.
24142431
</description>
@@ -2518,7 +2535,7 @@ CAUTION: Set the parser.timeout to -1 or a bigger value than 30, when using this
25182535
<property>
25192536
<name>link.delete.gone</name>
25202537
<value>false</value>
2521-
<description>Whether to delete gone pages from the web graph.</description>
2538+
<description>Whether to delete gone pages from the web graph. Gone pages include redirects and duplicates.</description>
25222539
</property>
25232540

25242541
<property>

src/java/org/apache/nutch/crawl/CrawlDatum.java

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ public class CrawlDatum implements WritableComparable<CrawlDatum>, Cloneable {
7575
public static final byte STATUS_DB_DUPLICATE = 0x07;
7676
/** Page was marked as orphan, e.g. has no inlinks anymore */
7777
public static final byte STATUS_DB_ORPHAN = 0x08;
78+
/** Page parsing failed */
79+
public static final byte STATUS_DB_PARSE_FAILED = 0x09;
7880

7981
/** Maximum value of DB-related status. */
8082
public static final byte STATUS_DB_MAX = 0x1f;
@@ -103,6 +105,8 @@ public class CrawlDatum implements WritableComparable<CrawlDatum>, Cloneable {
103105
public static final byte STATUS_LINKED = 0x43;
104106
/** Page got metadata from a parser */
105107
public static final byte STATUS_PARSE_META = 0x44;
108+
/** Page parse failed */
109+
public static final byte STATUS_PARSE_FAILED = 0x45;
106110

107111
public static final HashMap<Byte, String> statNames = new HashMap<>();
108112
static {
@@ -114,6 +118,7 @@ public class CrawlDatum implements WritableComparable<CrawlDatum>, Cloneable {
114118
statNames.put(STATUS_DB_NOTMODIFIED, "db_notmodified");
115119
statNames.put(STATUS_DB_DUPLICATE, "db_duplicate");
116120
statNames.put(STATUS_DB_ORPHAN, "db_orphan");
121+
statNames.put(STATUS_DB_PARSE_FAILED, "db_parse_failed");
117122
statNames.put(STATUS_SIGNATURE, "signature");
118123
statNames.put(STATUS_INJECTED, "injected");
119124
statNames.put(STATUS_LINKED, "linked");
@@ -124,6 +129,7 @@ public class CrawlDatum implements WritableComparable<CrawlDatum>, Cloneable {
124129
statNames.put(STATUS_FETCH_GONE, "fetch_gone");
125130
statNames.put(STATUS_FETCH_NOTMODIFIED, "fetch_notmodified");
126131
statNames.put(STATUS_PARSE_META, "parse_metadata");
132+
statNames.put(STATUS_PARSE_FAILED, "parse_failed");
127133

128134
oldToNew.put(OLD_STATUS_DB_UNFETCHED, STATUS_DB_UNFETCHED);
129135
oldToNew.put(OLD_STATUS_DB_FETCHED, STATUS_DB_FETCHED);
@@ -144,16 +150,14 @@ public class CrawlDatum implements WritableComparable<CrawlDatum>, Cloneable {
144150
private long modifiedTime;
145151
private org.apache.hadoop.io.MapWritable metaData;
146152

153+
/** Validate DB Status (ref.: CrawlDbReducer, IndexerReducer) */
147154
public static boolean hasDbStatus(CrawlDatum datum) {
148-
if (datum.status <= STATUS_DB_MAX)
149-
return true;
150-
return false;
155+
return (datum.status <= STATUS_DB_MAX) || CrawlDatum.STATUS_DB_PARSE_FAILED == datum.getStatus();
151156
}
152-
157+
/** Validate Fetch Status (ref.: CrawlDbReducer, IndexerReducer, SegmentMergerReducer) */
153158
public static boolean hasFetchStatus(CrawlDatum datum) {
154-
if (datum.status > STATUS_DB_MAX && datum.status <= STATUS_FETCH_MAX)
155-
return true;
156-
return false;
159+
return (datum.status > STATUS_DB_MAX && datum.status <= STATUS_FETCH_MAX)
160+
|| CrawlDatum.STATUS_PARSE_FAILED == datum.getStatus();
157161
}
158162

159163
public CrawlDatum() {

src/java/org/apache/nutch/crawl/CrawlDbReducer.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ public void setup(Reducer<Text, CrawlDatum, Text, CrawlDatum>.Context context) {
7676
* Get counter for status, caching for subsequent lookups.
7777
*/
7878
private Counter getStatusCounter(byte status, Context context) {
79-
return statusCounters.computeIfAbsent(status,
80-
s -> context.getCounter(NutchMetrics.GROUP_CRAWLDB,
79+
return statusCounters.computeIfAbsent(status,
80+
s -> context.getCounter(NutchMetrics.GROUP_CRAWLDB,
8181
CrawlDatum.getStatusName(s)));
8282
}
8383

@@ -233,7 +233,7 @@ public void reduce(Text key, Iterable<CrawlDatum> values,
233233
}
234234
break;
235235

236-
case CrawlDatum.STATUS_FETCH_SUCCESS: // succesful fetch
236+
case CrawlDatum.STATUS_FETCH_SUCCESS: // successful fetch
237237
case CrawlDatum.STATUS_FETCH_REDIR_TEMP: // successful fetch, redirected
238238
case CrawlDatum.STATUS_FETCH_REDIR_PERM:
239239
case CrawlDatum.STATUS_FETCH_NOTMODIFIED: // successful fetch, notmodified
@@ -243,7 +243,7 @@ public void reduce(Text key, Iterable<CrawlDatum> values,
243243
result.getMetaData().put(e.getKey(), e.getValue());
244244
}
245245
}
246-
246+
247247
// determine the modification status
248248
int modified = FetchSchedule.STATUS_UNKNOWN;
249249
if (fetch.getStatus() == CrawlDatum.STATUS_FETCH_NOTMODIFIED) {
@@ -320,6 +320,14 @@ public void reduce(Text key, Iterable<CrawlDatum> values,
320320
}
321321
break;
322322

323+
case CrawlDatum.STATUS_PARSE_FAILED: // successful fetch, but parse failed
324+
if (oldSet)
325+
result.setSignature(old.getSignature()); // use old signature
326+
result.setStatus(CrawlDatum.STATUS_DB_PARSE_FAILED);
327+
result = schedule.setPageGoneSchedule(key, result, prevFetchTime,
328+
prevModifiedTime, fetch.getFetchTime());
329+
break;
330+
323331
case CrawlDatum.STATUS_FETCH_GONE: // permanent failure
324332
if (oldSet)
325333
result.setSignature(old.getSignature()); // use old signature

src/java/org/apache/nutch/fetcher/Fetcher.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -582,11 +582,11 @@ public void fetch(Path segment, int threads) throws IOException,
582582
} catch (InterruptedException | ClassNotFoundException e) {
583583
LOG.error(StringUtils.stringifyException(e));
584584
throw e;
585+
} finally {
586+
stopWatch.stop();
587+
LOG.info("Fetcher: finished, elapsed: {} ms", stopWatch.getTime(
588+
TimeUnit.MILLISECONDS));
585589
}
586-
587-
stopWatch.stop();
588-
LOG.info("Fetcher: finished, elapsed: {} ms", stopWatch.getTime(
589-
TimeUnit.MILLISECONDS));
590590
}
591591

592592
/**

src/java/org/apache/nutch/fetcher/FetcherThread.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ public class FetcherThread extends Thread {
112112
URLNormalizers normalizersForOutlinks;
113113

114114
private boolean skipTruncated;
115+
private boolean deleteFailedParse;
115116

116117
private boolean halted = false;
117118

@@ -181,6 +182,7 @@ public FetcherThread(Configuration conf, AtomicInteger activeThreads, FetchItemQ
181182
this.scfilters = new ScoringFilters(conf);
182183
this.parseUtil = new ParseUtil(conf);
183184
this.skipTruncated = conf.getBoolean(ParseSegment.SKIP_TRUNCATED, true);
185+
this.deleteFailedParse = conf.getBoolean(ParseSegment.DELETE_FAILED_PARSE, false);
184186
this.signatureWithoutParsing = conf.getBoolean("fetcher.signature", false);
185187
this.protocolFactory = new ProtocolFactory(conf);
186188
this.normalizers = new URLNormalizers(conf, URLNormalizers.SCOPE_FETCHER);
@@ -221,7 +223,7 @@ public FetcherThread(Configuration conf, AtomicInteger activeThreads, FetchItemQ
221223
.getInt("http.robots.503.defer.visits.retries", 3);
222224
}
223225

224-
if((activatePublisher=conf.getBoolean("fetcher.publisher", false)))
226+
if ((activatePublisher = conf.getBoolean("fetcher.publisher", false)))
225227
this.publisher = new FetcherThreadPublisher(conf);
226228

227229
queueMode = conf.get("fetcher.queue.mode",
@@ -442,7 +444,7 @@ public void run() {
442444
case ProtocolStatus.SUCCESS: // got a page
443445
pstatus = output(fit.url, fit.datum, content, status,
444446
CrawlDatum.STATUS_FETCH_SUCCESS, fit.outlinkDepth);
445-
updateStatus(content.getContent().length);
447+
updateStatus(content.getContent() != null ? content.getContent().length : 0);
446448
if (pstatus != null && pstatus.isSuccess()
447449
&& pstatus.getMinorCode() == ParseStatus.SUCCESS_REDIRECT) {
448450
String newUrl = pstatus.getMessage();
@@ -734,14 +736,18 @@ private ParseStatus output(Text key, CrawlDatum datum, Content content,
734736
.calculate(content, new ParseStatus().getEmptyParse(conf));
735737
datum.setSignature(signature);
736738
}
739+
740+
if (parseResult == null && parsing && deleteFailedParse) {
741+
datum.setStatus(CrawlDatum.STATUS_PARSE_FAILED);
742+
status = CrawlDatum.STATUS_PARSE_FAILED;
743+
}
737744
}
738745

739746
/*
740747
* Store status code in content So we can read this value during parsing
741748
* (as a separate job) and decide to parse or not.
742749
*/
743-
content.getMetadata().add(Nutch.FETCH_STATUS_KEY,
744-
Integer.toString(status));
750+
content.getMetadata().add(Nutch.FETCH_STATUS_KEY, Integer.toString(status));
745751
}
746752

747753
try {
@@ -759,6 +765,10 @@ private ParseStatus output(Text key, CrawlDatum datum, Content content,
759765
LOG.warn("{} {} Error parsing: {}: {}", getName(),
760766
Thread.currentThread().getId(), key, parseStatus);
761767
parse = parseStatus.getEmptyParse(conf);
768+
if (deleteFailedParse && content != null) {
769+
// forward the failure status in the content
770+
content.getMetadata().add(Nutch.FETCH_STATUS_KEY, Integer.toString(CrawlDatum.STATUS_PARSE_FAILED));
771+
}
762772
}
763773

764774
// Calculate page signature. For non-parsing fetchers this will

src/java/org/apache/nutch/indexer/IndexerMapReduce.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import org.apache.nutch.parse.Parse;
5252
import org.apache.nutch.parse.ParseData;
5353
import org.apache.nutch.parse.ParseImpl;
54+
import org.apache.nutch.parse.ParseSegment;
5455
import org.apache.nutch.parse.ParseText;
5556
import org.apache.nutch.protocol.Content;
5657
import org.apache.nutch.scoring.ScoringFilterException;
@@ -206,6 +207,7 @@ public static class IndexerReducer extends
206207
private boolean delete = false;
207208
private boolean deleteRobotsNoIndex = false;
208209
private boolean deleteSkippedByIndexingFilter = false;
210+
private boolean deleteFailedParse = false;
209211
private boolean base64 = false;
210212
private IndexingFilters filters;
211213
private ScoringFilters scfilters;
@@ -226,6 +228,7 @@ public static class IndexerReducer extends
226228
private Counter deletedGoneCounter;
227229
private Counter deletedRedirectsCounter;
228230
private Counter deletedDuplicatesCounter;
231+
private Counter deletedFailedParseCounter;
229232
private Counter skippedNotModifiedCounter;
230233
private Counter deletedByIndexingFilterCounter;
231234
private Counter skippedByIndexingFilterCounter;
@@ -244,6 +247,7 @@ public void setup(Reducer<Text, NutchWritable, Text, NutchIndexAction>.Context c
244247
false);
245248
deleteSkippedByIndexingFilter = conf.getBoolean(INDEXER_DELETE_SKIPPED,
246249
false);
250+
deleteFailedParse = conf.getBoolean(ParseSegment.DELETE_FAILED_PARSE, false);
247251
skip = conf.getBoolean(INDEXER_SKIP_NOTMODIFIED, false);
248252
base64 = conf.getBoolean(INDEXER_BINARY_AS_BASE64, false);
249253

@@ -279,6 +283,8 @@ private void initCounters(Reducer<Text, NutchWritable, Text, NutchIndexAction>.C
279283
NutchMetrics.GROUP_INDEXER, NutchMetrics.INDEXER_DELETED_REDIRECTS_TOTAL);
280284
deletedDuplicatesCounter = context.getCounter(
281285
NutchMetrics.GROUP_INDEXER, NutchMetrics.INDEXER_DELETED_DUPLICATES_TOTAL);
286+
deletedFailedParseCounter = context.getCounter(
287+
NutchMetrics.GROUP_INDEXER, NutchMetrics.INDEXER_DELETED_FAILED_PARSE_TOTAL);
282288
skippedNotModifiedCounter = context.getCounter(
283289
NutchMetrics.GROUP_INDEXER, NutchMetrics.INDEXER_SKIPPED_NOT_MODIFIED_TOTAL);
284290
deletedByIndexingFilterCounter = context.getCounter(
@@ -354,6 +360,15 @@ public void reduce(Text key, Iterable<NutchWritable> values,
354360
}
355361
}
356362

363+
// Whether to delete pages where parsing failed
364+
if (deleteFailedParse && fetchDatum != null) {
365+
if (fetchDatum.getStatus() == CrawlDatum.STATUS_PARSE_FAILED
366+
|| dbDatum != null && dbDatum.getStatus() == CrawlDatum.STATUS_DB_PARSE_FAILED) {
367+
deletedFailedParseCounter.increment(1);
368+
context.write(key, DELETE_ACTION);
369+
return;
370+
}
371+
}
357372
// Whether to delete GONE or REDIRECTS
358373
if (delete && fetchDatum != null) {
359374
if (fetchDatum.getStatus() == CrawlDatum.STATUS_FETCH_GONE

src/java/org/apache/nutch/metadata/Nutch.java

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,62 +26,62 @@
2626
*/
2727
public interface Nutch {
2828

29-
public static final String ORIGINAL_CHAR_ENCODING = "OriginalCharEncoding";
29+
public static final String ORIGINAL_CHAR_ENCODING = "OriginalCharEncoding";
3030

31-
public static final String CHAR_ENCODING_FOR_CONVERSION = "CharEncodingForConversion";
31+
public static final String CHAR_ENCODING_FOR_CONVERSION = "CharEncodingForConversion";
3232

33-
public static final String SIGNATURE_KEY = "nutch.content.digest";
33+
public static final String SIGNATURE_KEY = "nutch.content.digest";
3434

35-
public static final String SEGMENT_NAME_KEY = "nutch.segment.name";
35+
public static final String SEGMENT_NAME_KEY = "nutch.segment.name";
3636

37-
public static final String SCORE_KEY = "nutch.crawl.score";
37+
public static final String SCORE_KEY = "nutch.crawl.score";
3838

39-
public static final String GENERATE_TIME_KEY = "_ngt_";
39+
public static final String GENERATE_TIME_KEY = "_ngt_";
4040

41-
public static final Text WRITABLE_GENERATE_TIME_KEY = new Text(
42-
GENERATE_TIME_KEY);
41+
public static final Text WRITABLE_GENERATE_TIME_KEY = new Text(
42+
GENERATE_TIME_KEY);
4343

44-
public static final Text PROTOCOL_STATUS_CODE_KEY = new Text("nutch.protocol.code");
44+
public static final Text PROTOCOL_STATUS_CODE_KEY = new Text("nutch.protocol.code");
4545

46-
public static final String PROTO_STATUS_KEY = "_pst_";
46+
public static final String PROTO_STATUS_KEY = "_pst_";
4747

48-
public static final Text WRITABLE_PROTO_STATUS_KEY = new Text(
49-
PROTO_STATUS_KEY);
48+
public static final Text WRITABLE_PROTO_STATUS_KEY = new Text(
49+
PROTO_STATUS_KEY);
5050

51-
public static final String FETCH_TIME_KEY = "_ftk_";
51+
public static final String FETCH_TIME_KEY = "_ftk_";
5252

53-
public static final String FETCH_STATUS_KEY = "_fst_";
53+
public static final String FETCH_STATUS_KEY = "_fst_";
5454

5555
/**
5656
* Name to store the <a href="https://www.robotstxt.org/meta.html">robots
5757
* metatag</a> in {@link org.apache.nutch.parse.ParseData}'s metadata.
5858
*/
5959
public static final String ROBOTS_METATAG = "robots";
6060

61-
/**
62-
* Sites may request that search engines don't provide access to cached
63-
* documents.
64-
*/
65-
public static final String CACHING_FORBIDDEN_KEY = "caching.forbidden";
61+
/**
62+
* Sites may request that search engines don't provide access to cached
63+
* documents.
64+
*/
65+
public static final String CACHING_FORBIDDEN_KEY = "caching.forbidden";
6666

67-
/** Show both original forbidden content and summaries (default). */
68-
public static final String CACHING_FORBIDDEN_NONE = "none";
67+
/** Show both original forbidden content and summaries (default). */
68+
public static final String CACHING_FORBIDDEN_NONE = "none";
6969

70-
/** Don't show either original forbidden content or summaries. */
71-
public static final String CACHING_FORBIDDEN_ALL = "all";
70+
/** Don't show either original forbidden content or summaries. */
71+
public static final String CACHING_FORBIDDEN_ALL = "all";
7272

73-
/** Don't show original forbidden content, but show summaries. */
74-
public static final String CACHING_FORBIDDEN_CONTENT = "content";
73+
/** Don't show original forbidden content, but show summaries. */
74+
public static final String CACHING_FORBIDDEN_CONTENT = "content";
7575

76-
public static final String REPR_URL_KEY = "_repr_";
76+
public static final String REPR_URL_KEY = "_repr_";
7777

78-
public static final Text WRITABLE_REPR_URL_KEY = new Text(REPR_URL_KEY);
78+
public static final Text WRITABLE_REPR_URL_KEY = new Text(REPR_URL_KEY);
7979

80-
/** Used by AdaptiveFetchSchedule to maintain custom fetch interval */
81-
public static final String FIXED_INTERVAL_KEY = "fixedInterval";
80+
/** Used by AdaptiveFetchSchedule to maintain custom fetch interval */
81+
public static final String FIXED_INTERVAL_KEY = "fixedInterval";
8282

83-
public static final Text WRITABLE_FIXED_INTERVAL_KEY = new Text(
84-
FIXED_INTERVAL_KEY);
83+
public static final Text WRITABLE_FIXED_INTERVAL_KEY = new Text(
84+
FIXED_INTERVAL_KEY);
8585

8686
/** For progress of job (programmatic / tooling). */
8787
public static final String STAT_PROGRESS = "progress";

0 commit comments

Comments
 (0)