1、1、 页面解析技术 练习:进入当当网中的任意一个商品列表页面:(将这个页面的链接取得,分析链接地址为商品详细信息页的内容保留下来这种链接有以下特点:1) 网址中会有 2) 网址最后会以 数字.html 结尾将这些链接提取到某个集合中保存。public class DangdangDemo private static List allLinks = new ArrayList();private static Set allOverLinks = new HashSet();public static void main(String args) throws Exception / 先去完成
2、将所有链接提取出来的功能URL url = new URL(URLConnection conn = url.openConnection();InputStream is = conn.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(is);String line = null;Pattern p = Ppile(a .*href=.*);while (line = reader.readLine() != null) Matcher m = p.matcher(line);w
3、hile (m.find() String link = m.group();link = link.substring(link.indexOf(href=) + 6);link = link.substring(0, link.indexOf();/ 判断是否包含if (link.startsWith(http) & link.contains() / 截取出最后一段的内容String temp = link.substring(link.lastIndexOf(/) + 1);/ 由于正则表达式中 . 表示任意字符,因此如果想让 . 只表示 .,就需要对其进行转义if (temp.mat
4、ches(d+.html) allLinks.add(link);reader.close();对集合中的所有链接地址的网页内容进行分析,提取出里面的商品名称和价格。商品名称可以找 h1 中的文字内容商品价格可以找 中的内容。同样将数据也保存到文本文件中来进行处理。package org.liky.test;import java.io.BufferedReader;import java.io.File;import java.io.InputStream;import java.io.InputStreamReader;import java.io.PrintWriter;import .
5、URL;import .URLConnection;import java.util.ArrayList;import java.util.HashSet;import java.util.List;import java.util.Set;import java.util.regex.Matcher;import java.util.regex.Pattern;public class DangdangDemo private static List allLinks = new ArrayList();private static Set allOverLinks = new HashSe
6、t();public static void main(String args) throws Exception / 先去完成将所有链接提取出来的功能URL url = new URL(URLConnection conn = url.openConnection();InputStream is = conn.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(is);String line = null;Pattern p = Ppile(a .*href=.*);while
7、(line = reader.readLine() != null) Matcher m = p.matcher(line);while (m.find() String link = m.group();link = link.substring(link.indexOf(href=) + 6);link = link.substring(0, link.indexOf();/ 判断是否包含if (link.startsWith(http) & link.contains() / 截取出最后一段的内容String temp = link.substring(link.lastIndexOf(
8、/) + 1);/ 由于正则表达式中 . 表示任意字符,因此如果想让 . 只表示 .,就需要对其进行转义if (temp.matches(d+.html) allLinks.add(link);reader.close();getDangdangData();public static void getDangdangData() throws Exception / 提取出一个链接进行处理if (allLinks.size() 0) String urlStr = allLinks.get(0);allLinks.remove(0);if (!allOverLinks.contains(ur
9、lStr) / 同样需要读取网页的内容URL url = new URL(urlStr);URLConnection conn = url.openConnection();InputStream is = conn.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(is, UTF-8);String line = null;Pattern pTitle = Ppile(.*);Pattern pPrice = Ppile(.*);/ 不再一行一行的进行匹配,而是将整个网页的所有代
10、码内容放到一个字符串中进行匹配。/ 准备一个大的字符串,保存网页所有的源代码内容StringBuilder allContent = new StringBuilder();while (line = reader.readLine() != null) / 只需要将line中的内容拼接到allContent里即可allContent.append(line);reader.close();Matcher mTitle = pTitle.matcher(allContent.toString();Matcher mPrice = pPrice.matcher(allContent.toStri
11、ng();String title = null;if (mTitle.find() title = mTitle.group();title = title.substring(title.indexOf() + 1);title = title.substring(0, title.indexOf() + 1);price = price.substring(0, price.indexOf();System.out.println(price);/ 我们发现,这里爬取的规则,不能直接看页面源代码来设计,而需要通过读取到的源代码进行规划。/ 读取到的源代码与看到的源代码不同的原因可能是以下
12、两种:/ 1:爬取的信息过多,导致网站将你的信息屏蔽或加密后返回/ 2:通过爬虫访问网页时,网站认为你是通过手机访问的,而且网页还做了移动端的规划,所以返回的是手机看到的信息。/ 最后将内容保存到文本中。String fileName = System.currentTimeMillis() + .txt;PrintWriter writer = new PrintWriter(new File(D:/web_data/ + fileName);writer.println(title);writer.println(price);writer.print(urlStr);writer.clo
13、se();/ 将该链接设置为已经访问过的链接allOverLinks.add(urlStr);/ 需要重新调用这个方法,直到所有的链接都处理完成getDangdangData();1.1、HTMLParser解析网页内容HTMLParser是一个第三方提供的Java扩展库,专门针对HTML内容进行解析的。通过这个库,可以实现按照给定的条件,解析网页内容的功能。这里主要包含的类如下:1) Parser:解析的核心类2) Filter:设置解析条件3) Node:解析出的HTML节点。先从网上下载一个开发包。并解压缩。将lib目录下的支持jar包导入到自己的项目里,建立直接拷贝到项目的lib目录下
14、,来方便自己使用。同时,将这些jar包加入到build path中。之后我们如果想解析HTML内容时,就可以建立一个Parser对象,来准备解析了。public class DangdangDemoHTMLParser private static List allLinks = new ArrayList();private static Set allOverLinks = new HashSet();public static void main(String args) throws Exception / 先去完成将所有链接提取出来的功能URL url = new URL(URLCo
15、nnection conn = url.openConnection();InputStream is = conn.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(is);String line = null;Pattern p = Ppile(a .*href=.*);while (line = reader.readLine() != null) Matcher m = p.matcher(line);while (m.find() String link = m.grou
16、p();link = link.substring(link.indexOf(href=) + 6);link = link.substring(0, link.indexOf();/ 判断是否包含if (link.startsWith(http) & link.contains() / 截取出最后一段的内容String temp = link.substring(link.lastIndexOf(/) + 1);/ 由于正则表达式中 . 表示任意字符,因此如果想让 . 只表示 .,就需要对其进行转义if (temp.matches(d+.html) allLinks.add(link);re
17、ader.close();getDangdangData();public static void getDangdangData() throws Exception / 提取出一个链接进行处理if (allLinks.size() 0) try String urlStr = allLinks.get(0);allLinks.remove(0);if (!allOverLinks.contains(urlStr) / 同样需要读取网页的内容URL url = new URL(urlStr);URLConnection conn = url.openConnection();String t
18、itle = null;String price = null;/ 建立解析的核心对象ParserParser parser = new Parser(conn);parser.setEncoding(UTF-8);/ 建立解析规则/ 先找到TagNameFilter titleFilter = new TagNameFilter(article);/ 进行规则的解析Node titleNode = parser.parse(titleFilter).elementAt(0);/ 读取节点中的文本内容title = titleNode.toHtml();System.out.println(t
19、itle);/ 读取PriceAndFilter priceFilter = new AndFilter(new TagNameFilter(span),new HasAttributeFilter(id, main_price);parser.reset();Node priceNode = parser.parse(priceFilter).elementAt(0);price = priceNode.toPlainTextString();System.out.println(price);/ 我们发现,这里爬取的规则,不能直接看页面源代码来设计,而需要通过读取到的源代码进行规划。/ 读
20、取到的源代码与看到的源代码不同的原因可能是以下两种:/ 1:爬取的信息过多,导致网站将你的信息屏蔽或加密后返回/ 2:通过爬虫访问网页时,网站认为你是通过手机访问的,而且网页还做了移动端的规划,所以返回的是手机看到的信息。/ 最后将内容保存到文本中。String fileName = System.currentTimeMillis() + .txt;PrintWriter writer = new PrintWriter(new File(D:/web_data/ + fileName);writer.println(title);writer.println(price);writer.p
21、rint(urlStr);writer.close();/ 将该链接设置为已经访问过的链接allOverLinks.add(urlStr); catch (Exception e) / 需要重新调用这个方法,直到所有的链接都处理完成getDangdangData();1.2、JSoup解析工具JSoup这个工具对爬虫信息处理的支持是现在最好的,可以很容易定位到要处理的文件内容。而且基本没有bug,不会出现读取不到内容的情况。JSoup就是一个支持jar包,直接可以拷贝到项目里来使用。public class DangdangDemoJSoup private static List allLi
22、nks = new ArrayList();private static Set allOverLinks = new HashSet();public static void main(String args) throws Exception / 先去完成将所有链接提取出来的功能URL url = new URL(URLConnection conn = url.openConnection();InputStream is = conn.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamR
23、eader(is);String line = null;Pattern p = Ppile(a .*href=.*);while (line = reader.readLine() != null) Matcher m = p.matcher(line);while (m.find() String link = m.group();link = link.substring(link.indexOf(href=) + 6);link = link.substring(0, link.indexOf();/ 判断是否包含if (link.startsWith(http) & link.con
24、tains() / 截取出最后一段的内容String temp = link.substring(link.lastIndexOf(/) + 1);/ 由于正则表达式中 . 表示任意字符,因此如果想让 . 只表示 .,就需要对其进行转义if (temp.matches(d+.html) allLinks.add(link);reader.close();getDangdangData();public static void getDangdangData() throws Exception / 提取出一个链接进行处理if (allLinks.size() 0) String urlStr
25、= allLinks.get(0);allLinks.remove(0);if (!allOverLinks.contains(urlStr) try / 这里测试时会发现并不是按照移动端的规则,而是直接用电脑的网页解析规则完成的。/ import org.jsoup.nodes.Document;Document doc = Jsoup.connect(urlStr).get();/ import org.jsoup.nodes.ElementElement titleEl = doc.getElementsByTag(h1).get(0);String title = titleEl.te
26、xt();System.out.println(title);Element priceEl = doc.getElementsByAttributeValue(id, dd-price).get(0);String price = priceEl.text();price = price.substring(1);System.out.println(price);/ 最后将内容保存到文本中。String fileName = System.currentTimeMillis() + .txt;PrintWriter writer = new PrintWriter(new File(D:/
27、web_data/ + fileName);writer.println(title);writer.println(price);writer.print(urlStr);writer.close();/ 将该链接设置为已经访问过的链接allOverLinks.add(urlStr); catch (Exception e) / 需要重新调用这个方法,直到所有的链接都处理完成getDangdangData();练习:使用JSoup尝试提取sina新闻的标题(title)和描述(description)信息,看一下能否简化代码。public class SinaNewsDemoJSoup pr
28、ivate static List allUrls = new ArrayList();private static Set allOverUrls = new HashSet();public static void main(String args) throws Exception getWebData();public static void getWebData(String urlStr) throws Exception / 判断网页是否被处理过,如果已经被处理过了,就不要再重新处理了。if (allOverUrls.contains(urlStr) else try Docum
29、ent doc = Jsoup.connect(urlStr).get();/ 解析出title标题信息String title = doc.title();/ 解析出description的信息String desc = null;Element descEl = doc.getElementsByAttributeValue(name, description).get(0);desc = descEl.attr(content);/ 解析出所有的超连接Elements allLink = doc.getElementsByTag(a);/ 循环取得所有的超连接/ 这种循环方式主要针对的是
30、数组和集合/ 既然Elements类型可以用这种循环,就说明它是List的子类for (Element link:allLink) / 每一个link就是一个超连接/ 需要取得连接的href属性,并判断是否为一个合法连接String href = link.attr(href);if (href.startsWith(http) allUrls.add(href);/ 将读取到的title和desc信息保存到一个文本文件中。String fileName = System.currentTimeMillis() + .txt;PrintWriter writer = new PrintWrit
31、er(new File(D:/web_data/ + fileName);/ 保存时,我们按照固定的规格来保存,第一行放网址,第二行放title,第三行放descwriter.println(urlStr);writer.println(title);writer.print(desc);writer.close();/ 将当前的url放入到allOverUrls里,表示已经被处理过了allOverUrls.add(urlStr); catch (Exception e) e.printStackTrace();System.out.println(已经处理完成: + urlStr + , 等
32、待队列中还有 + allUrls.size() + 等待处理!);/ 当当前的网页已经处理完成了,我们需要把队列中等待的网页拿出来一个/ 也进行一样的处理if (allUrls.size() 0) String newUrl = allUrls.get(0);/ 取得后这个连接就没用了,应该删除掉,为后面的连接腾空间allUrls.remove(0);getWebData(newUrl);测试会发现,我们已经可以完成爬虫操作,但是这种爬取方式基本没有办法将所有内容都爬取下来,有很多内容在底层的连接中,相关度不高,不需要爬取但是也放到队列中。因此在这里就需要进行爬虫的优化。3、爬虫功能优化主要的
33、优化方式有两种:1) 通过爬取深度的检测,来屏蔽掉深度过深的连接地址。2) 通过多线程,提升爬虫的并发处理速度。3.1、深度检测在进行内容的爬取时,可以记录当前连接是在第几层深度的连接,当进行数据的提取时,判断深度是否在我们需要的范围内,如果不在,就没必要进行爬取,该连接可以直接忽略掉。这里需要通过一个 Map集合来记录下所有的连接地址。key为连接的地址字符串,value就是该连接的深度。public class SinaNewsDemoJSoup private static List allUrls = new ArrayList();private static Set allOver
34、Urls = new HashSet();/ 最大允许爬取的深度private static int MAX_DEPTH = 2;/ 这个集合记录下所有的url的深度。private static Map allUrlDepth = new HashMap();public static void main(String args) throws Exception allUrlDepth.put(, 1);getWebData();public static void getWebData(String urlStr) throws Exception / 判断网页是否被处理过,如果已经被处
35、理过了,就不要再重新处理了。if (allOverUrls.contains(urlStr) else / 取得当前url的深度int depth = allUrlDepth.get(urlStr);if (depth = MAX_DEPTH) try Document doc = Jsoup.connect(urlStr).get();/ 解析出title标题信息String title = doc.title();/ 解析出description的信息String desc = null;Element descEl = doc.getElementsByAttributeValue(na
36、me, description).get(0);desc = descEl.attr(content);/ 解析出所有的超连接Elements allLink = doc.getElementsByTag(a);/ 循环取得所有的超连接/ 这种循环方式主要针对的是数组和集合/ 既然Elements类型可以用这种循环,就说明它是List的子类for (Element link : allLink) / 每一个link就是一个超连接/ 需要取得连接的href属性,并判断是否为一个合法连接String href = link.attr(href);if (href.startsWith(http) allUrls.add(href);/ 新提取出来的连接,需要记录这些连接的深度。/ 这些连接的深度,应该是当前连接 深度 + 1/ 处理这个之前先要判断原本这个连接是否已经记录深度了。/ 之前没有记录过这个连接深度的话,我们再进行记录if (!allUrlDepth.containsKey(href) allUrlDepth.put(href, depth + 1);/ 将读取到的title和desc信息保存到一个文本文件中。String fileName = System.curren