1. package xsmeral.artnet.scraper;
  2.  
  3. import java.util.List;
  4. import java.util.regex.Matcher;
  5. import java.util.regex.Pattern;
  6. import javax.xml.datatype.DatatypeConstants;
  7. import javax.xml.datatype.DatatypeFactory;
  8. import javax.xml.datatype.XMLGregorianCalendar;
  9. import org.htmlcleaner.TagNode;
  10. import org.openrdf.model.URI;
  11. import org.openrdf.model.vocabulary.OWL;
  12. import org.openrdf.model.vocabulary.RDF;
  13. import org.openrdf.model.vocabulary.RDFS;
  14. import xsmeral.semnet.scraper.onto.EntityClass;
  15. import xsmeral.semnet.scraper.onto.Term;
  16. import xsmeral.semnet.crawler.model.EntityDocument;
  17. import xsmeral.semnet.scraper.AbstractScraper;
  18. import static xsmeral.semnet.util.XPathUtil.*;
  19.  
  20. public class CSFDScraper {
  21.  
  22.     public static class Film extends AbstractScraper {
  23.  
  24.         private static final String NODE_ACTORS = "Hrají:";
  25.         private static final String NODE_DIRECTORS = "Režie:";
  26.         private final Pattern PATT_ORIGIN = Pattern.compile("(?:(?!\\d+)([^,]+),?\\s*)?(?:(\\d{4}),?\\s*)?(?:(?:\\d+\\s*x\\s*)?(?:\\s*(\\d+)\\s*min))?");
  27.         public static final String NAMESPACE = "http://www.csfd.cz#";
  28.         @Term
  29.         public static final URI TYPE = RDF.TYPE;
  30.         @Term("Used for IMDb link")
  31.         public static final URI SAME_AS = OWL.SAMEAS;
  32.         @Term("Used for www link")
  33.         public static final URI SEE_ALSO = RDFS.SEEALSO;
  34.         @Term
  35.         @EntityClass
  36.         public static final URI FILM = f.createURI(NAMESPACE, "film");
  37.         @Term
  38.         public static final URI FILM_NAME = RDFS.LABEL;
  39.         @Term
  40.         public static final URI DIRECTED_BY = f.createURI(NAMESPACE, "directed_by");
  41.         @Term
  42.         public static final URI ACTS_IN = f.createURI(NAMESPACE, "acts_in");
  43.         @Term
  44.         public static final URI GENRE = f.createURI(NAMESPACE, "genre");
  45.         @Term
  46.         public static final URI ORIGIN = f.createURI(NAMESPACE, "origin");
  47.         @Term
  48.         public static final URI YEAR = f.createURI(NAMESPACE, "year");
  49.         @Term("Duration in minutes")
  50.         public static final URI DURATION = f.createURI(NAMESPACE, "duration");
  51.  
  52.         @Override
  53.         protected void scrape(EntityDocument doc) throws Exception {
  54.             TagNode root = doc.getDocument();
  55.             TagNode contentNode = (TagNode) root.evaluateXPath("//*[@id='profile']/div[@class='content']")[0];
  56.             TagNode infoNode = (TagNode) contentNode.evaluateXPath("div[@class='info']")[0];
  57.  
  58.             // state type of this entity
  59.             fact(TYPE, FILM);
  60.  
  61.             // name
  62.             String mainName = queryText(infoNode, "h1");
  63.             fact(FILM_NAME, lit(mainName));
  64.  
  65.             // other names
  66.             for (TagNode node : queryNodes(infoNode, "ul[@class='names']/li/h3")) {
  67.                 fact(FILM_NAME, lit(getText(node)));
  68.             }
  69.  
  70.             // genres
  71.             // Drama / Thriller / Mysteriózní
  72.             String genresStr = queryText(infoNode, "p[@class='genre']");
  73.             if (genresStr != null) {
  74.                 String[] genres = genresStr.split("/");
  75.                 for (String genreStr : genres) {
  76.                     String genre = genreStr.trim();
  77.                     fact(GENRE, lit(genre));
  78.                 }
  79.             }
  80.  
  81.             // (countries), (year), ((nn x ) duration) - each optional
  82.             // USA / Velká Británie / Německo / Francie, 2011, 30 x 22 min
  83.             String originStr = queryText(infoNode, "p[@class='origin']");
  84.             if (originStr != null) {
  85.                 Matcher m = PATT_ORIGIN.matcher(originStr);
  86.                 if (m.find()) {
  87.                     String countriesStr = m.group(1);
  88.                     String yearStr = m.group(2);
  89.                     String durationStr = m.group(3);
  90.                     if (countriesStr != null) {
  91.                         String[] countries = countriesStr.split("/");
  92.                         for (String country : countries) {
  93.                             fact(ORIGIN, lit(country));
  94.                         }
  95.                     }
  96.                     if (yearStr != null) {
  97.                         fact(YEAR, f.createLiteral(Integer.parseInt(yearStr.trim())));
  98.                     }
  99.                     if (durationStr != null) {
  100.                         fact(DURATION, f.createLiteral(Integer.parseInt(durationStr.trim())));
  101.                     }
  102.                 }
  103.             }
  104.  
  105.             List<TagNode> creatorNodes = queryNodes(infoNode, "div[h4]");
  106.             for (TagNode creatorsNode : creatorNodes) {
  107.                 String nodeType = queryText(creatorsNode, "h4");
  108.                 if (nodeType.contains(NODE_ACTORS)) {
  109.                     for (String actor : queryTextNodes(creatorsNode, "span/a/@href")) {
  110.                         fact(uri(actor), ACTS_IN, current());
  111.                     }
  112.                 } else if (nodeType.contains(NODE_DIRECTORS)) {
  113.                     for (String director : queryTextNodes(creatorsNode, "span/a/@href")) {
  114.                         fact(DIRECTED_BY, uri(director));
  115.                     }
  116.                 }
  117.             }
  118.  
  119.             TagNode linksNode = (TagNode) contentNode.evaluateXPath("ul[@class='links']")[0];
  120.             if (linksNode != null && linksNode.hasChildren()) {
  121.                 // imdb link
  122.                 String imdbLink = queryText(linksNode, "li/a[@class='imdb']/@href");
  123.                 if (imdbLink != null) {
  124.                     fact(SAME_AS, uri(imdbLink));
  125.                 }
  126.                 // www link
  127.                 String wwwLink = queryText(linksNode, "li/a[@class='www']/@href");
  128.                 if (wwwLink != null) {
  129.                     fact(SEE_ALSO, uri(wwwLink));
  130.                 }
  131.             }
  132.         }
  133.  
  134.         @Override
  135.         public String getNamespace() {
  136.             return NAMESPACE;
  137.         }
  138.     }
  139.  
  140.     public static class Creator extends AbstractScraper {
  141.  
  142.         private static final String DIR_FILMOGRAPHY = "Režijní";
  143.         private static final String ACT_FILMOGRAPHY = "Herecká";
  144.         private static final Pattern PATT_BIRTH = Pattern.compile("nar\\.\\s*(\\d{1,2})\\.(\\d{1,2})\\.(\\d{4})");
  145.         public static final String NAMESPACE = "http://www.csfd.cz#";
  146.         @Term
  147.         public static final URI TYPE = RDF.TYPE;
  148.         @Term("Used for IMDb link")
  149.         public static final URI SAME_AS = OWL.SAMEAS;
  150.         @Term
  151.         @EntityClass
  152.         public static final URI DIRECTOR = f.createURI(NAMESPACE, "director");
  153.         @Term
  154.         @EntityClass
  155.         public static final URI ACTOR = f.createURI(NAMESPACE, "actor");
  156.         @Term
  157.         public static final URI PERSON_NAME = RDFS.LABEL;
  158.         @Term
  159.         public static final URI BIRTH_DATE = f.createURI(NAMESPACE, "birth_date");
  160.  
  161.         @Override
  162.         protected void scrape(EntityDocument doc) throws Exception {
  163.  
  164.             TagNode root = doc.getDocument();
  165.             TagNode contentNode = (TagNode) root.evaluateXPath("//*[@id='profile']/div[@class='content']")[0];
  166.             TagNode infoNode = (TagNode) contentNode.evaluateXPath("div[@class='info']")[0];
  167.  
  168.  
  169.             // name
  170.             String name = queryText(infoNode, "h1");
  171.             if (name != null) {
  172.                 fact(PERSON_NAME, lit(name));
  173.             }
  174.  
  175.             // type
  176.             for (String filmography : queryTextNodes(root, "data(//*[@id='filmography']//div[@class='header']/h2)")) {
  177.                 if (filmography.contains(DIR_FILMOGRAPHY)) {
  178.                     fact(TYPE, DIRECTOR);
  179.                 } else if (filmography.contains(ACT_FILMOGRAPHY)) {
  180.                     fact(TYPE, ACTOR);
  181.                 }
  182.             }
  183.  
  184.             // birth date
  185.             String birthStr = queryText(infoNode, "ul[1]/li");
  186.             if (birthStr != null) {
  187.                 Matcher m = PATT_BIRTH.matcher(birthStr);
  188.                 if (m.find()) {
  189.                     try {
  190.                         int day = Integer.parseInt(m.group(1));
  191.                         int month = Integer.parseInt(m.group(2));
  192.                         int year = Integer.parseInt(m.group(3));
  193.                         XMLGregorianCalendar birthDate = DatatypeFactory.newInstance().newXMLGregorianCalendarDate(year, month, day, DatatypeConstants.FIELD_UNDEFINED);
  194.                         fact(BIRTH_DATE, f.createLiteral(birthDate));
  195.                     } catch (NumberFormatException ex) {
  196.                     }
  197.                 }
  198.             }
  199.             // imdb link
  200.             String imdbLink = queryText(contentNode, "ul[@class='links']/li/a[@class='imdb']/@href");
  201.             if (imdbLink != null) {
  202.                 fact(SAME_AS, uri(imdbLink));
  203.             }
  204.         }
  205.  
  206.         @Override
  207.         public String getNamespace() {
  208.             return NAMESPACE;
  209.         }
  210.     }
  211.  
  212. }
  213.