#38 Change algorithm of SmartContentSelector

master
yihua.huang 2013-11-23 13:56:55 +08:00
parent 296a68920e
commit 04fcf3193f
2 changed files with 61 additions and 74 deletions

View File

@ -23,7 +23,7 @@ public class OschinaBlogPageProcesser implements PageProcessor {
//skip this page //skip this page
page.setSkip(true); page.setSkip(true);
} }
page.putField("content", page.getHtml().xpath("//div[@class='BlogContent']/tidyText()").toString()); page.putField("content", page.getHtml().smartContent().toString());
page.putField("tags", page.getHtml().xpath("//div[@class='BlogTags']/a/text()").all()); page.putField("tags", page.getHtml().xpath("//div[@class='BlogTags']/a/text()").all());
} }

View File

@ -1,100 +1,87 @@
package us.codecraft.webmagic.selector; package us.codecraft.webmagic.selector;
import org.apache.log4j.Logger;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;
import us.codecraft.webmagic.utils.Experimental; import us.codecraft.webmagic.utils.Experimental;
import java.util.*; import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicInteger; import java.util.Arrays;
import java.util.List;
/** /**
* Extract the text content of html.<br> * Borrowed from https://code.google.com/p/cx-extractor/
* Using Readability algorithm: find parents of all p tags.
* *
* @author code4crafter@gmail.com <br> * @author code4crafter@gmail.com <br>
* @since 0.1.0 * @since 0.4.1
*
*/ */
@Experimental @Experimental
public class SmartContentSelector implements Selector { public class SmartContentSelector implements Selector {
private Logger logger = Logger.getLogger(getClass());
public SmartContentSelector() { public SmartContentSelector() {
} }
@Override @Override
public String select(String text) { public String select(String html) {
HtmlCleaner htmlCleaner = new HtmlCleaner(); html = html.replaceAll("(?is)<!DOCTYPE.*?>", "");
TagNode tagNode = htmlCleaner.clean(text); html = html.replaceAll("(?is)<!--.*?-->", ""); // remove html comment
if (tagNode == null) { html = html.replaceAll("(?is)<script.*?>.*?</script>", ""); // remove javascript
return null; html = html.replaceAll("(?is)<style.*?>.*?</style>", ""); // remove css
} html = html.replaceAll("&.{2,5};|&#.{2,5};", " "); // remove special char
TagNode[] nodes = tagNode.getElementsByName("p", true); html = html.replaceAll("(?is)<.*?>", "");
TagNode[] pres = tagNode.getElementsByName("pre", true); List<String> lines;
Map<TagNode, Double> pDensityCountMap = new HashMap<TagNode, Double>(); int blocksWidth =3;
countPdensity(nodes, pDensityCountMap); int threshold =86;
countPdensity(pres, pDensityCountMap); int start;
for (TagNode pre : pres) { int end;
addCounter(pre, pDensityCountMap, 2); StringBuilder text = new StringBuilder();
} ArrayList<Integer> indexDistribution = new ArrayList<Integer>();
List<Map.Entry<TagNode, Double>> sortList = new ArrayList<Map.Entry<TagNode, Double>>();
if (pDensityCountMap.size() == 0) {
return null;
}
for (Map.Entry<TagNode, Double> entry : pDensityCountMap.entrySet()) {
// if (logger.isDebugEnabled()) {
// logger.debug("p\t" + entry.getKey().getName() + "#" + entry.getKey().getAttributeByName("id") +
// "@" + entry.getKey().getAttributeByName("class") + ":" + entry.getValue());
// }
sortList.add(entry);
}
Collections.sort(sortList, new Comparator<Map.Entry<TagNode, Double>>() { lines = Arrays.asList(html.split("\n"));
@Override
public int compare(Map.Entry<TagNode, Double> o1, Map.Entry<TagNode, Double> o2) { for (int i = 0; i < lines.size() - blocksWidth; i++) {
Double d1 = o1.getValue(); int wordsNum = 0;
Double d2 = o2.getValue(); for (int j = i; j < i + blocksWidth; j++) {
return -d1.compareTo(d2); lines.set(j, lines.get(j).replaceAll("\\s+", ""));
wordsNum += lines.get(j).length();
} }
}); indexDistribution.add(wordsNum);
TagNode contentNode = sortList.get(0).getKey();
if (logger.isDebugEnabled()) {
logger.debug("p\t" + contentNode.getName() + "#" + contentNode.getAttributeByName("id") +
"@" + contentNode.getAttributeByName("class"));
} }
return htmlCleaner.getInnerHtml(contentNode);
}
private void addCounter(TagNode node, Map<TagNode, Double> countMap, double delta) { start = -1; end = -1;
Double counter = countMap.get(node); boolean boolstart = false, boolend = false;
if (counter == null) { text.setLength(0);
countMap.put(node, delta);
} else {
countMap.put(node, counter + delta);
}
}
private static final double parentWeight = 0.7; for (int i = 0; i < indexDistribution.size() - 1; i++) {
if (indexDistribution.get(i) > threshold && ! boolstart) {
private void countPdensity(TagNode[] nodes, Map<TagNode, Double> pDensityCountMap) { if (indexDistribution.get(i+1).intValue() != 0
for (TagNode node : nodes) { || indexDistribution.get(i+2).intValue() != 0
if (node == null) { || indexDistribution.get(i+3).intValue() != 0) {
continue; boolstart = true;
start = i;
continue;
}
} }
TagNode parent = node.getParent(); if (boolstart) {
double pDensity = 1; if (indexDistribution.get(i).intValue() == 0
while (parent != null) { || indexDistribution.get(i+1).intValue() == 0) {
addCounter(parent, pDensityCountMap, pDensity); end = i;
parent = parent.getParent(); boolend = true;
pDensity = pDensity * parentWeight; }
}
StringBuilder tmp = new StringBuilder();
if (boolend) {
//System.out.println(start+1 + "\t\t" + end+1);
for (int ii = start; ii <= end; ii++) {
if (lines.get(ii).length() < 5) continue;
tmp.append(lines.get(ii) + "\n");
}
String str = tmp.toString();
//System.out.println(str);
if (str.contains("Copyright") ) continue;
text.append(str);
boolstart = boolend = false;
} }
} }
} return text.toString();
private TagNode findLowestCommonParent(List<TagNode> tagNodes, int maxMargin, Map<TagNode, AtomicInteger> countMap) {
TagNode contentNode = tagNodes.get(0);
return contentNode;
} }
@Override @Override