Echando código: ¿Como hacer web-scrapping con Java?

En un articulo anterior les mostré como hacer Web scraping con Perl. Ahora vamos a probar con Java, pero utilizando una estrategía un poco diferente.
Todo esto vino de un articulo que leí en Internet acerca de como hacer lo mismo pero con Java; Me encantó la solución ya que divieron la resolución de el problema en los siguientes pasos:
- Convierta el HTML con problemas a "HTML bien formado". Es decir, convertimos el HTML a XML (XHTML)
- Una vez convertido a XML, utilice XQUERY para extraer la información de el documento. Yo no quiero hacer eso, sino más bien quiero usar XSLT (igual me toca usar XPATH, aunque más con XSLT que con XQUERY).
Después de fumarme un rato los JavaDoc de Jtydi, ver el API de XML de Java y de jugar con la hoja de estilos, llegué a esto:
1:package com.kodegeek.blog.veneblogs.webscraping;
2:
3:import org.w3c.tidy.Tidy;
4:import org.w3c.tidy.Configuration;
5:
6:import java.io.InputStream;
7:import java.io.OutputStream;
8:import java.io.FileOutputStream;
9:import java.io.File;
10:import java.io.IOException;
11:import java.net.URL;
12:
13:import javax.xml.transform.stream.StreamSource;
14:import javax.xml.transform.stream.StreamResult;
15:import javax.xml.transform.Transformer;
16:import javax.xml.transform.TransformerFactory;
17:
18:/**
19: * This program will download the VeneBlogsTop100 and will
20: * output the list as a text file
21: * @author Jose Vicente Nunez Zuleta (josevnz@yahoo.com)
22: * @version 0.1 - 08/20/2005
23: * License: GPL
24: * Blog: KodeGeek.com
25: */
26:public final class VeneBlogsTop100 {
27:
28: /**
29: * URL with the VeneBlogs top 100
30: */
31: public static final String DEFAULT_VENEBLOGS_TOP100_URL =
32: "http://www.veneblogs.com/feeds/ultimos100.php";
33:
34: /**
35: * Default Stylesheet to be used if none is provided
36: */
37: public static final String XSLT_DEFAULT_VENEBLOGS_TOP100 =
38: "/VeneBlogsTop100.xsl";
39:
40: /**
41: * Command line processing
42: * @param args
43: * args[0]: URL with the VeneBlogs HTML.
44: * args[1] Location of the XSLT
45: * @since 0.1
46: * @throws Exception
47: */
48: public static void main(String [] args) throws Exception {
49: URL url = null;
50: InputStream input = null;
51: OutputStream out = null;
52: StreamSource xsltSource = null;
53: StreamSource source = null;
54: // Create a file that is unique among runs (sort of)
55: File xmlOutFile = new File(
56: System.getProperty("user.home") +
57: System.getProperty("file.separator") +
58: System.getProperty("user.name") +
59: "-" +
60: VeneBlogsTop100.class.getName() +
61: "-" +
62: System.currentTimeMillis() +
63: ".xml"
64: );
65: try {
66: if ( (args != null) && (args.length == 1) && (args[0] != null) ) {
67: url = new java.net.URL(args[0]);
68: } else {
69: url = new java.net.URL(DEFAULT_VENEBLOGS_TOP100_URL);
70: }
71: input = url.openStream();
72: out = new FileOutputStream(xmlOutFile);
73: // Get the HTML and convert it to XHTML
74: Tidy tidy = new Tidy();
75: /*
76: * Very important. Set the proper flags so we convert the document
77: * from HTML to XHTML. Then we can proceed to parse it with an XSLT
78: *
79: */
80: tidy.setTidyMark(false);
81: tidy.setDocType("omit");
82: // Uncomment only if you know your XML has not weird escape
sequences
83: //tidy.setCharEncoding(Configuration.UTF8);
84: tidy.setAltText("");
85: tidy.setFixBackslash(true);
86: tidy.setFixComments(true);
87: tidy.setXmlPi(true);
88: tidy.setQuoteAmpersand(true);
89: tidy.setQuoteNbsp(true);
90: tidy.setNumEntities(true);
91: tidy.setXmlOut(true);
92: tidy.setWraplen(999);
93: tidy.setWriteback(true);
94: tidy.setQuoteMarks(true);
95: tidy.setLogicalEmphasis(true);
96: tidy.setEncloseText(true);
97: tidy.setHideEndTags(true);
98: tidy.setShowWarnings(false);
99: tidy.setQuiet(true);
100: tidy.setXHTML(true);
101:
102: tidy.parse(input, out);
103: out.close();
104: // take the XML and convert it to a text report using the sylesheet
105: source = new StreamSource(xmlOutFile);
106: if ( (args != null) && (args.length == 2) && (args[1] != null) ) {
107: xsltSource = new StreamSource(args[1]);
108: } else {
109: xsltSource = new StreamSource(
110: Class.class.
getResourceAsStream(XSLT_DEFAULT_VENEBLOGS_TOP100)
111: );
112: }
113: StreamResult reportOut = new StreamResult(System.out);
// The report will go to STDOUT
114: TransformerFactory tFactory = TransformerFactory.newInstance();
115:
116: Transformer trans = tFactory.newTransformer(xsltSource);
117:
118: // Do the transformation
119: trans.transform(source, reportOut);
120:
121: } catch (Exception exp) {
122: throw exp;
123: } finally {
124: if (out != null) {
125: try {
126: out.close();
127: } catch (IOException ignore) {
128: // Ignore
129: }
130: }
131: if (input != null) {
132: try {
133: input.close();
134: } catch (IOException ignore) {
135: // Ignore
136: }
137: }
138: if ((source != null) && (source.getInputStream() != null)) {
139: try {
140: source.getInputStream().close();
141: } catch (IOException ignore) {
142: // Ignore
143: }
144: }
145: if ((xsltSource != null) &&
(xsltSource.getInputStream() != null)) {
146: try {
147: xsltSource.getInputStream().close();
148: } catch (IOException ignore) {
149: // Ignore
150: }
151: }
152: xmlOutFile.deleteOnExit();
153: }
154: }
155:}
Fijense que seguimos la secuencia descrita anteriormente para procesar el HTML. Hay varias opciones de Jtidy las cuales van a garantizar que la salida va a ser un XML sin problema; Cuando quiera, corra la página de VeneBlogs con la opción de las advertencias encendida y verá que el HTML que ellos generan tienen aún pequeños problemas, los cuales se convierten en serios cuando el procesador de XSLT tiene que hacer el trabajo.
Ahora les muestro la hoja de estilos que toma el XHTML:
1:<?xml version="1.0" encoding="utf-8"?>
2:<!--
3:Author: José Vicente Núñez Zuleta
4:Blog: KodeGeek - http://kodegeek.com
5:Declare a namespace for the XHTML, otherwise the XPATH will not work.
6:-->
7:<xsl:stylesheet
8: xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
9: xmlns:xhtml="http://www.w3.org/1999/xhtml"
10: version="1.0">
11:
12:<xsl:output method="text" encoding="UTF-8"/>
13:<xsl:strip-space elements="*"/>
14:
15:<xsl:variable name="newline">
16:<xsl:text>
17:</xsl:text>
18:</xsl:variable>
19:
20:<xsl:template match="/">
21:Blog title, URL
22:<xsl:for-each select="xhtml:html/xhtml:body/xhtml:div/
xhtml:div[@id='innercontent']/xhtml:dl/xhtml:dd/xhtml:a"><!--
23:Extract real URL is stored. It's easy to pull with two
24:substrings, one to remove the beginning of the Javascript and the other to remove
25:the end.
26:--><xsl:value-of select="substring(substring(@onmouseover, 17,
string-length(@onmouseover)), 1,string-length(substring(@onmouseover, 17,
string-length(@onmouseover)))-14)"/><xsl:text>, </xsl:text><
xsl:value-of select="text()"/><xsl:value-of select="$newline"/></xsl:for-each>
27:</xsl:template>
28:
29:</xsl:stylesheet>
Y esta es la salida de el programa:
[josevnz@localhost VeneblogsTop100]$ java -jar dist/VeneBlogsTop100-0.1.jar¿Interesante, no es así?. Algunas reflexiones:
Blog title, URL
http://hegodigital.blogspot.com, Algunas cosas que pienso cuando no debo pensar
http://tecnochica.blogspot.com/, TecnoCHICA
http://www.drabutterfly.blogspot.com, butterfly effect
http://lilisecreta.blogspot.com, Vida Secreta
http://mariws.blogspot.com, A mi manera...
http://danielhenriquez.blogspot.com/, Lo que nunca nos contaron.
http://franengotas.timsam.com, Fran en Gotas Provisional
http://diarreaverbal.blogspot.com, Diarrea Verbal
http://www.reindertot.blogspot.com/, El nunca diestro Reindertot
http://luisma72.blogspot.com, VOYAGE!!! (LuisMa Blog)
http://lubrio.blogspot.com, El Espacio de LUBRIO
http://www.qtpd.com/trippis, Trippis: Creatividad para llevar
http://aventura4x4.blogspot.com, Aventura 4x4
http://currusaonline.blogspot.com, |___LINEAS TONTAS___| Vol.2
http://marco28.blogspot.com, The Way Iam
- El código en Perl es mucho más compacto que el de Java. No requiere compilación y debe correr en cualquier máquina en donde usted tenga los módulos instalados.
- La instalación de el código en Java es mucho más fácil. Todas las librerías requeridas forman parte de el Jar que estamos distribuyendo y de las que vienen con el JDK 1.5.0 (Usted deberá bajar JAXP para el JDK 1.4.2 de lo contrario).
- Java ofrece más posibilidades de reuso ya que si usted ya se dió cuenta, puede cambiar el URL y la hoja de estilos en el programa de Java y con eso puede procesar cualquier página. En Perl esa separación no es tan limpia (aunque el código de Perl es casi trivial).
Los dejo con una pregunta: ¿Como se puede hacer lo mismo en Python, Ruby o Unix Shell?
Como siempre, se pueden bajar el código de aqui.
Buscar en Technorati: java, jtidy, xml, xslt



0 Comentarios:
Publicar un comentario en la entrada
Enlaces a este articulo:
Crear un vínculo
<< Regresar