# IMDB Name und Bewertung auslesen



## JavaNase (24. Aug 2012)

Hallo Leute,

ich und mein Studienpartner verzweifeln gerade regelrecht an der gestellten Aufgabe "HTML Knoten mit Eclipse auslessen bzw crawlen zu lassen". Vorweg schon mal entschuldigung, wenn wir hier irgendwelche falschen bzw nicht gängigen Formatierungen benutzen. 

Wir haben uns als Ziel gesetzt bei der Internetseite "http://www.imdb.de/title/tt1637725/" den Namen des Films und die Bewertung crawlen zu lassen. In diesem Fall wäre das "Ted" und "7.6".

Wir benutzen dafür 3 Klasse deren Code entsprechen aussieht ...

Der Code für unseren simpleController sieht so aus


```
import edu.uci.ics.crawler4j.crawler.CrawlController;

	/**
	 * @author Yasser Ganjisaffar <yganjisa at uci dot edu>
	 */

	public class simpleController {

			public static void main(String[] args) throws Exception {
				if (args.length < 2) {
					System.out.println("Please specify 'root folder' and 'number of crawlers'.");
					return;
				}
				
				/*
				 * rootfolder is a folder where intermediate crawl data is
				 * stored. 
				 */
				String rootFolder = args[0];
				
				/*
				 * numberOfCrawlers shows the number of concurrent threads
				 * that should be initiated for crawling.
				 */
				int numberOfCrawlers = Integer.parseInt(args[1]);
				
				/*
				 * Instantiate the controller for this crawl. Note that if you want
				 * your crawl to be resumable (meaning that you can resume the crawl
				 * from a previously interrupted/crashed crawl) you can either set
				 * crawler.enable_resume to true in crawler4j.properties file or you
				 * can use the second parameter to the CrawlController constructor.
				 * 
				 * Note: if you enable resuming feature and want to start a fresh
				 * crawl, you need to delete the contents of rootFolder manually.
				 */
				CrawlController controller = new CrawlController(rootFolder);
				
				/*
				 * For each crawl, you need to add some seed urls.
				 * These are the first URLs that are fetched and
				 * then the crawler starts following links which
				 * are found in these pages
				 */
				// controller.addSeed("http://www.lamberti-apotheke-hildesheim.de/");
				controller.addSeed("http://www.imdb.de/title/tt0120338/");
				// controller.addSeed("http://www.bild.de/");
				
				/*
				 * Be polite:
				 * Make sure that we don't send more than 5 requests per 
				 * second (200 milliseconds between requests).
				 */
				controller.setPolitenessDelay(200);
				
				/*
				 * Optional:
				 * You can set the maximum crawl depth here.
				 * The default value is -1 for unlimited depth
				 */
				controller.setMaximumCrawlDepth(3);
				
				/*
				 * Optional:
				 * You can set the maximum number of pages to crawl.
				 * The default value is -1 for unlimited depth
				 */
				controller.setMaximumPagesToFetch(50);
				
				/*
				 * Do you need to set a proxy?
				 * If so, you can use: 
				 * controller.setProxy("proxyserver.example.com", 8080);
				 * OR
				 * controller.setProxy("proxyserver.example.com", 8080, username, password);
				 */
				
				/*
				 * Note: you can configure several other parameters by modifying 
				 * crawler4j.properties file
				 */
				
				/*
				 * Start the crawl. This is a blocking operation, meaning
				 * that your code will reach the line after this only when
				 * crawling is finished.
				 */
				System.out.println("Crawl starting ... ");

				controller.start(theCrawlerAnalyzer.class, numberOfCrawlers);
				
				System.out.println("Crawl ready");
			}


}
```

Code für den theCrawler


```
import java.util.regex.Pattern;

import edu.uci.ics.crawler4j.crawler.Page;
import edu.uci.ics.crawler4j.crawler.WebCrawler;
import edu.uci.ics.crawler4j.url.WebURL;

import java.util.List;

public class theCrawler extends WebCrawler {

        Pattern filters = Pattern.compile(".*(\\.(css|js|bmp|gif|jpe?g"  + "|png|tiff?|mid|mp2|mp3|mp4"  + "|wav|avi|mov|mpeg|ram|m4v|pdf"  + "|rm|smil|wmv|swf|wma|zip|rar|gz))$");

        /*
         * 
         * You should implement this function to specify
         * whether the given URL should be visited or not.
         */
        public boolean shouldVisit(WebURL url) {
                String href = url.getURL().toLowerCase();
                if (filters.matcher(href).matches()) {
                        return false;
                }
                if (href.startsWith("http")) {
                        return true;
                }
                return false;
        }

        /*
         * This function is called when a page is fetched
         * and ready to be processed by your program
         */
        
        public void visit(Page page) {
                int docid = page.getWebURL().getDocid();
                String url = page.getWebURL().getURL();         
                String text = page.getText();
                List<WebURL> links = page.getURLs();   
                
                System.out.println ("\nNew URL"); 
                System.out.println (docid);
                
                if (text.contains("Titanic") && url.contains("tt1735898") )  
                {
                // System.out.println (text);
                System.out.println (url);
                System.out.println ("Anzahl der Links: "+ links.size());
                System.out.println ("Länge Text"+ text.length());
                String snippet = "";
                snippet = text.substring(text.indexOf("Titanic"), text.indexOf("Titanic")+30);
                snippet = snippet.trim();
                System.out.println (snippet);
        		}	
        }

}
```

Code für den theCrawlerAnalyzer


```
import java.util.regex.Pattern;
import java.util.Vector;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import org.w3c.dom.Attr;
import org.w3c.dom.NodeList;
import org.w3c.dom.NamedNodeMap;

import java.io.StringReader; 

import edu.uci.ics.crawler4j.crawler.Page;
import edu.uci.ics.crawler4j.crawler.WebCrawler;
import edu.uci.ics.crawler4j.url.WebURL;

import java.util.List;

import javax.swing.text.html.HTML.Attribute;

import org.cyberneko.html.parsers.DOMParser;
import org.xml.sax.InputSource; 


public class theCrawlerAnalyzer extends WebCrawler {

	Pattern filters = Pattern.compile(".*(\\.(css|js|bmp|gif|jpe?g"  + "|png|tiff?|mid|mp2|mp3|mp4"  + "|wav|avi|mov|mpeg|ram|m4v|pdf"  + "|rm|smil|wmv|swf|wma|zip|rar|gz))$");

	/*
	 * You should implement this function to specify
	 * whether the given URL should be visited or not.
	 */

	public boolean shouldVisit(WebURL url) {
		String href = url.getURL().toLowerCase();
		if (filters.matcher(href).matches()) {
			return false;
		}
		if (href.startsWith("http")) {
			return true;
		}
		return false;
	}

	/*
	 * This function is called when a page is fetched
	 * and ready to be processed by your program
	 */

	public void visit(Page page) {
		int docid = page.getWebURL().getDocid();
		String url = page.getWebURL().getURL();         
		List<WebURL> links = page.getURLs();  

		System.out.println ("\nNew URL:  " + docid + " URL: "+ url + "  with  " + links.size()+ "  links");

		// HTML DOM

		DOMParser parser = new DOMParser();

		try {
			parser.parse(new InputSource( new StringReader (page.getHTML())));

			//System.out.println ("print DOM ");
			//print(parser.getDocument(), " ");
			// try und catch führt parsing aus

		} 
		catch (Exception e) {
			System.out.println ("Parse-Fehler in Klasse \n: ");
			System.out.println (e.getClass() ); 
			System.out.println ("\n: " + e.getMessage() );
			e.printStackTrace();
		}
		try {
			
		

			Vector<Element> elems = new Vector<Element> (); 
			extractElements(parser.getDocument().getDocumentElement() ,"TABLE",elems);
			System.out.println ("Nr of table  elements: " + elems.size());   

			Vector<Element> elems2 = new Vector<Element> (); 
			extractElements(parser.getDocument().getDocumentElement() ,"TR",elems2);
			System.out.println ("Nr of TR elements: " + elems2.size());   
			
			Vector<Element> aelems = new Vector<Element> (); 
			extractElements(parser.getDocument().getDocumentElement() ,"a",aelems);
			System.out.println ("Nr of Anchors elements: " + aelems.size());
			
			
			for (int f=0; f<aelems.size(); aelems.size())
			{
				NamedNodeMap aelem = aelems.elementAt(f).getAttributes();

				// Get number of attributes in the element
				if (aelems != null) {
					int numAttrs = aelem.getLength() ;

					// Process each attribute
					for (int i=0; i<numAttrs; i++) {
						//NodeList attrs;
						Attr attr = (Attr)aelem.item(i);

						// Get attribute name and value
						String attrName = attr.getNodeName();
						String attrValue = attr.getNodeValue();
	
						
						//
					if (attrName == "Nutzer-Bewertung:")
					{
					
						System.out.println (aelems.toString()); 
					}
					}
					}
				}
			}
				
		catch (Exception e)
		{
			System.out.println ("Parse-Fehler in Klasse \n: ");
			System.out.println (e.getClass() ); 
			System.out.println (e.getMessage() );
		}

	}


	public static void print(Node node, String indent) {
		//		String s = node.getNodeValue();
		//		if ((s != null)  && (s != "")  ) {

		System.out.println(indent +  "node.NodeValue: " + node.getNodeValue());
		System.out.println(indent +  "name: " + node.getClass().getName());
		System.out.println(indent +  "node.toString: " +  node.toString());
		System.out.println(indent +  "node.toNodeName: " + node.getNodeName());
		System.out.println(indent +  "node.NodeValue: " + node.getNodeValue());
		System.out.println("");

		// Get all the attributes of an element in a map
		NamedNodeMap attrs = node.getAttributes();

		// Get number of attributes in the element
		if (attrs != null) {
			int numAttrs = attrs.getLength();

			// Process each attribute
			for (int i=0; i<numAttrs; i++) {
				Attr attr = (Attr)attrs.item(i);

				// Get attribute name and value
				String attrName = attr.getNodeName();
				String attrValue = attr.getNodeValue();
				System.out.println (indent +  "attrName :" +attrName );
				System.out.println (indent +  "attrValue :" +attrValue);
				System.out.println ();

			}
		}
		Node child = node.getFirstChild();
		while (child != null) {
			print(child, indent + " ");
			child = child.getNextSibling();
		}
	}


	protected void extractElements(Element element, String type, Vector<Element> elementList) {
		// this should not happen !
		if (element==null) {
			return;
		}
		String name = element.getNodeName();
		if (name.equals(type)) {
			elementList.add(element);
		}

		// recursive travel through all childs
		NodeList childs = element.getChildNodes();

		for (int i=0; i<childs.getLength(); i++) {
			if (childs.item(i) instanceof Element) {
				extractElements((Element)childs.item(i),type,elementList);
			}
		}

	}
}
```

Wir wissen das wir lediglich die theCrawlerAnalyzer-Klasse entsprechend umschreiben müssen, jedoch wissen wir nicht wie genau. Das einzige was wir wissen ist wo wir die gewünschten Werte im HTML-Code finden. Jedoch nicht wie wir direkt auf die zugreifen können und wie wir es dann schlussendlich auslesen lassen können.

Quelltext

Der auszugebende Name befindet sich immer in der Überschrift <h1>...</h1>


```
<h1 xmlns="http://www.w3.org/1999/xhtml">
    
    
    Ted <span>(2012) <span class="pro-link"><a target="_blank" href="http://www.imdb.com/rg/maindetails-title/warp-link/title/tt1637725/">Mehr auf IMDb.com</a> »</span><span class="title-extra"/></span>
    
</h1>
```

Hier findet man den Wert für die Benutzer bewertung ...


```
<div xmlns="http://www.w3.org/1999/xhtml" class="info stars">
      <h5>Nutzer-Bewertung:</h5>

      <div class="starbar">
        <div class="outer">
          <div style="width: 152px;" class="inner" id="general-voting-stars"/>
          <div class="starbar-votes">
            <a class="s10" onclick="return vote(10)" href="vote?v=10;
...
...
...
          </div>

        </div>
      </div>
  
      <div class="starbar-meta">
       <b>7,6/10</b>                      
           
           
             <a class="tn15more" href="ratings">51.041 Stimmen</a> »
        
        
      </div>
    </div>
```

Wir sind echt stark überfragt wie man hier weiter dran arbeiten kann und könnten wirklich eure Hilfe gebrauchen. Vielen Dank!

LG M&M


----------



## Mujahiddin (24. Aug 2012)

Alles was du machen musst ist den Quelltext der Seite lesen und ein Muster herauslesen:


```
<title>Ted (2012)</title>
```


```
<div class="starbar-meta">
        <b>7,6/10</b> 
           
           
           &nbsp;&nbsp;<a href="ratings" class="tn15more">51.041 Stimmen</a>&nbsp;&raquo;
        
        
      </div>
```

Darauf machst du ein Regexpattern und lässt es über die Seite laufen.


----------



## JavaNase (24. Aug 2012)

Erst einmal danke natürlich.

Wir müssen leider gestehen noch nie was von einem Regexpattern gehört zu haben und darüber hinaus wollten wir gerne wissen, ob sich dieses Pattern dann in der "theCrawlerAnalyzer-Klasse" entsprechend implementieren ließe? Wir müssen nämlich mit dem gegeben arbeiten. 

Danke für jede weitere Hilfe.


----------



## Mujahiddin (25. Aug 2012)

Ganz ehrlich, ich kenne mich mit deinem Konstrukt nicht aus und habe auch keinerlei HTML-Erfahrung.
Also bei deinem theCrawlerAnalyzer kann ich dir nicht helfen.
Ich sag mal so - Voraussetzung ist, dass du den gesamten Quelltext der URL als String hast (oder nur die 2 Schnipsel aus meinem vorigen Beitrag).
Dann wendest du ein Pattern auf diese an. Fertig.
Ich kann dir mal ein Codebeispiel geben:


```
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class IMDBMovie {
	
	public final String title;
	public final int year;
	public final double rating;
	public final int votes;
	
	public IMDBMovie(String title, int releaseYear, double rating, int votes) {
		this.title = title;
		this.year = releaseYear;
		this.rating = rating;
		this.votes = votes;
	}
	
	private static String regex = "(?s)<title>(\\w+)\\((\\d+)\\)</title>.*?< *div +class *= *\"starbar-meta\" *>[.&&[^<]]*?<b>(\\d[,|.]\\d)/10</b>.*?<a +href=\".*?\" class=\".*?\">(\\d|.|,) .*?</a>";
	private static Pattern pattern = Pattern.compile( regex );
	
	public static IMDBMovie of(String source) {
		Matcher m = pattern.matcher( Objects.requireNonNull( source ) );
		if( m.find() ) {
			String title = m.group( 1 );
			int year = Integer.valueOf( m.group( 2 ) );
			double rating = Double.valueOf( m.group( 3 ).replace( ",", "." ) );
			int votes = Integer.valueOf( m.group( 4 ).replace( ",", "" ).replace( ".", "" ) );
			return new IMDBMovie( title, year, rating, votes );
		}
		return null;
	}
	
	@Override
	public String toString() {
		return title + " (" + year + ") - " + rating + "/10 (" + votes + " Stimmen)";
	}
	
	public static void main(String... args) throws Exception {
		BufferedReader reader = new BufferedReader( new InputStreamReader( new URL( "http://www.imdb.com/title/tt0068646" ).openStream() ) );
		String source = "";
		String line;
		while( ( line = reader.readLine() ) != null )
			source += line + "\n";
		System.out.println( IMDBMovie.of( source ) );
	}
}
```

Evtl. muss am Regex noch gearbeitet werden - ich bekomme dauernd Error 403 bei 
	
	
	
	





```
URL#openStream()
```
Aber so ungefähr müsste das stimmen.


----------



## Mujahiddin (26. Aug 2012)

Mit folgendem Code funktioniert's:


```
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.NumberFormat;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class IMDBMovie {
	
	public final String title;
	public final int year;
	public final double rating;
	public final int votes;
	
	public IMDBMovie(String title, int releaseYear, double rating, int votes) {
		this.title = title;
		this.year = releaseYear;
		this.rating = rating;
		this.votes = votes;
	}
	
	private static final String regexEng = "(?si)<title>([\\w ]+) \\((\\d+)\\).*?</title>.*?<span itemprop=\"ratingValue\">([\\d\\.,]+)</span>.*?<span itemprop=\"ratingCount\">([\\d\\.,]+)</span>";
	private static final String regexDe = "(?s)<title>([\\w ]+) \\((\\d+)\\).*?</title>.*?<div class=\"starbar-meta\">.*?<b>([\\d\\.,]+)/10</b>.*?<a href=\".*?\" class=\".*?\">([\\d\\.,]+).*?</a>";
	
	@SuppressWarnings("serial")
	private static class InvalidURLException extends RuntimeException {
		
		private InvalidURLException() {
			super();
		}
		
		private InvalidURLException(String message) {
			super( message );
		}
	}
	
	private static IMDBMovie of(String source, String ext) {
		Pattern p;
		if( ext.equals( "com" ) )
			p = Pattern.compile( regexEng );
		else if( ext.equals( "de" ) )
			p = Pattern.compile( regexDe );
		else
			throw new InvalidURLException( "Extension is not supported. Only \"com\" and \"de\" are supported." );
		Matcher m = p.matcher( Objects.requireNonNull( source ) );
		if( m.find() ) {
			String title = m.group( 1 );
			int year = Integer.valueOf( m.group( 2 ) );
			double rating = Double.valueOf( m.group( 3 ).replace( ",", "." ) );
			int votes = Integer.valueOf( m.group( 4 ).replace( ",", "" ).replace( ".", "" ) );
			return new IMDBMovie( title, year, rating, votes );
		}
		return null;
	}
	
	public static IMDBMovie of(URL url) throws IOException {
		if( !url.getHost().startsWith( "www.imdb." ) )
			throw new InvalidURLException( "Only host \"www.imdb.*\" ist supported." );
		HttpURLConnection con = (HttpURLConnection) url.openConnection();
		con.addRequestProperty( "User-Agent", "Mozilla/4.76" );
		BufferedReader reader = new BufferedReader( new InputStreamReader( con.getInputStream() ) );
		String source = "";
		String line;
		while( ( line = reader.readLine() ) != null )
			source += line + "\n";
		String ext = url.getHost().substring( url.getHost().lastIndexOf( '.' ) + 1 );
		return of( source, ext );
	}
	
	public static IMDBMovie of(String url) throws IOException {
		return of( new URL( url ) );
	}
	
	@Override
	public String toString() {
		return title + " (" + year + ") - " + NumberFormat.getInstance().format( rating ) + "/10 (" + NumberFormat.getInstance().format( votes ) + " Stimmen)";
	}
	
	public static void main(String... args) throws Exception {
		System.out.println( IMDBMovie.of( "http://www.imdb.de/title/tt1637725/" ) );
	}
}
```


----------



## JavaNase (29. Aug 2012)

Wow, danke für die Arbeit! Jedoch muss ich sagen, dass bei Zeile 7 "import java.util.Objects;" immer ein Fehler (import ... cannot be resolved) angezeigt wird und damit Fehler in Zeile 49 auslöst. Vllt ist das aber nur ein sehr kleines Problem, dass nur blutige Java-Anfänger nicht sehen können.

und ließe sich diese Zeilen einmal genauer erklären.


```
"(?s)<title>([\\w ]+) \\((\\d+)\\).*?</title>.*?<div class=\"starbar-meta\">.*?<b>([\\d\\.,]+)/10</b>.*?<a href=\".*?\" class=\".*?\">([\\d\\.,]+).*?</a>";
```

Ich meine zu wissen, dass dies die Mustererkennung zum Auslesen des HTML-Codes ist. Jedoch verstehe ich z.B. nicht warum "([\\w ]+) \\((\\d+)\\).*?" geschrieben werden muss um den Titel auszulesen.  Ebenso der Rest scheint in meinen Augen sehr schwer nachvollziehbar. 

Danke im Voraus!


----------



## AngryDeveloper (29. Aug 2012)

Bevor man da selbst rum schustert und mit Regex rumhantiert, könnte man sich auch eine Library wie jsoup mal angucken:
jsoup Java HTML Parser, with best of DOM, CSS, and jquery


----------



## andreT (29. Aug 2012)

Und nicht zu viel Requests/Sekunde absetzen. Wenn die da mittloggen meldet sich nachher noch so'n Abmahn-Anwalts-Parasit wegen AGBs und so


----------



## JavaNase (29. Aug 2012)

andreT hat gesagt.:


> Und nicht zu viel Requests/Sekunde absetzen. Wenn die da mittloggen meldet sich nachher noch so'n Abmahn-Anwalts-Parasit wegen AGBs und so



Das haben wir auch schon gedacht und dementsprechend eine Begrenzung gesetzt.


----------



## JavaNase (30. Aug 2012)

Ich weiß mir immer noch nicht zu helfen ... in Zeile 7 kommt immer "import java.util.Objects;cannot be resolved". Frage mich wie ich diesen Fehler beheben kann.

Irgendwelche Vorschläge?


----------



## Mujahiddin (30. Aug 2012)

Du brauchst Java7.
Google mal: "JDK7 Oracle"

Ansonsten kannst du das einfach rausmachen, das ist nicht unbedingt notwendig.


----------

