目录
4.1.4 HtmlCleaner(HTML文档解析器)——支持Xpath语法
4.1.5 HtmlParser(HTML文档解析器)——支持CSS语法
4 网页内容解析
4.1 HTML
4.1.1 CSS选择器
1——基础选择器(略过)
2——属性选择器 扩(可以指定具体标签的属性):div[title]
选择器(举例) | 含义 |
[title] | 选择带有title元素的所有属性 |
[^tit] | 查找属性前缀为titl的所有元素 |
[title="abc"] | 选择title="abc"的所有元素 |
[title~="ab"] | 选择title包含ab的所有元素 |
[title^="abc"] | 选择title开头是abc的所有元素 |
[title$="abc"] | 选择title结尾是abc的所有元素 |
3——组合选择器
选择器(举例) | 含义 |
div,p | 所有<div>、<p> |
div p | <div>内部的<p> |
div.content>p | <div>[class="content"]内的第一个<p> |
div~p | <div>之后所有<p> |
div+p | <div>之后第一个<p> |
4——伪选择器
选择器(举例) | 含义 |
td:lt(3) | 小于3列的元素 |
div p:gt(2) | 包含2个以上<p>的<div> |
form input:eq(1) | 包含1个<input>的<form> |
div:has(p) | 包含<p>的<div> |
div:not(p) | 不包含<p>的<div> |
p:contains(abc) | 包含abc文本的<p> |
div:nth-child(5) | <div>内的第5个元素 |
4.1.2 Xpath语法(用于-选取节点)
表达式(案例) | 含义 |
body | <body>的全部子节点 |
/html | 根<html> |
//div | 所有任意位置<div> |
./p | 当前<p> |
//a[@href] | 有href的<a> |
//div[@id='course'] | id='course'的<div> |
案例 | 含义 |
//div[@id='ki']/h1 | <div id="ki">节点下的<h1> |
//body/a[1] | <body>下的第一个<a> |
//body//a[last()] | <body>下的最后一个<a> |
4.1.3 Jsoup解析HTML
//document.select()选择节点元素;节点元素.attr()选择属性
1.解析URL加载的Document
package com.xp.climb.climb04.css;
import java.io.IOException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
public class JsoupCSS {
public static void main(String[] args) throws IOException {
//这里笔者使用Jsoup获取html文件
Document doc = Jsoup.connect("http://www.w3school.com.cn/b.asp").timeout(5000).get();
Elements elements = doc.select("[^titl]");
Elements elements1 = doc.select("div#navsecond+div");
System.out.println(elements1);
//伪选择器---查找包含指定文本的元素
Elements elements2 = doc.select("p:contains(JavaScript)");
System.out.println(elements2);
Elements elements3 = doc.select("div:only-child");
System.out.println(elements3);
Elements elements4 = doc.select("#maincontent > div:nth-child(5)");
System.out.println(elements4);
}
}
2.Jsoup遍历元素(XML)
package com.xp.climb.climb04.xml;
import java.io.IOException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
public class JsoupXML {
public static void main(String[] args) throws IOException {
//获取URL对应的HTML内容
String url = "http://db.auto.sohu.com/cxdata/xml/sales/model/model1001sales.xml";
Document doc = Jsoup.connect(url).timeout(5000).get();
//Jsoup选择器解析
//select()选择节点
Elements sales_ele = doc.select("sales");
for (Element elem:sales_ele) {
//节点.attr()选择属性
int salesnum=Integer.valueOf(elem.attr("salesnum"));
String date = elem.attr("date");
System.out.println("月份:" + date + "\t销量:" + salesnum);
}
}
}
3. Jsoup获取元素的其他方法
除了select() 还有很多获取节点的方法——如下:
//Jsoup获取html
Document doc = Jsoup.connect("http://www.w3school.com.cn/b.asp").timeout(5000).get();
//基于id获取元素
Element element_id = doc.getElementById("course");
//获取a标签 集合
Elements elements_tag = doc.getElementsByTag("a");
//获取属性有href的标签 集合
Elements elements_A = doc.getElementsByAttribute("href");
//获取class为joke的元素 集合
Elements elements5 =doc.getElementsByClass("joke");
//获取属性前缀为hre的元素 集合
Elements elements_As = doc.getElementsByAttributeStarting("hre");
//获取属性id=course的元素 集合
Elements elements_Av = doc.getElementsByAttributeValue("id","course");
//获取id=course的兄弟元素 集合
Elements elements_Se = doc.getElementById("course").siblingElements();
//获取id=course下一个兄弟元素
Element element_Ns = doc.getElementById("course").nextElementSibling();
//获取id=course上一个兄弟元素
Element element_Ps = doc.getElementById("course").previousElementSibling();
4.支持Xpath语法的JsoupXpath(需要导入相关依赖)自行了解
4.1.4 HtmlCleaner(HTML文档解析器)——支持Xpath语法
1. 导入依赖
<!-- HTML文档解析器 -->
<dependency>
<groupId>net.sourceforge.htmlcleaner</groupId>
<artifactId>htmlcleaner</artifactId>
<version>2.24</version>
</dependency>
2. HtmlCleaner类和TagNode类
方法 | 说明 |
TagNode clean(String htmlContent) | 将String类型的HTML转化成TagNode |
TagNode clean(File file,String charset) | 按照指定字符集将File中存储的HTML转化成TagNode |
TagNode clean(File file) | 默认字符集转化成TagNode |
TagNode clean(URL url,String charset) | 获取URL的HTMl内容(指定字符集),并转化成TagNode |
TagNode clean(URL url) | 获取URL的HTMl内容(默认字符集),并转化成TagNode |
TagNode clean(InputStream in,String charset) | (指定)转化输入流HTML成TagNode |
TagNode clean(InputStream in) | (默认)转化输入流HTML成TagNode |
方法 | 说明 |
Object[] evaluateXPath(String xPath) | Xpath语法获取Object数组 |
String getAttributeByName(String attName) | 属性名获取属性值 |
TagNode findeElementByName(String findName,boolean isRecursive) | 标签名获取节点 |
CharSequence getText() | 获取节点中的文本 |
TagNode getParent() | 获取父节点 |
List<TagNode> getChildTagList() | 获取所有子节点,返回值-集合 |
boolean hasChildren() | 判断是否存在子节点 |
3. 操作案例
package com.xp.climb.climb04.htmlcleaner;
import java.io.IOException;
import org.htmlcleaner.HtmlCleaner;
import org.htmlcleaner.TagNode;
import org.htmlcleaner.XPatherException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
public class HtmlcleanerTest1 {
public static void main(String[] args) throws IOException, XPatherException {
//使用Jsoup获取html文件
Document doc = Jsoup.connect("http://www.w3school.com.cn/b.asp").
timeout(5000).get();
//转化成String格式
String html =doc.html();
//实例化HtmlCleaner
HtmlCleaner cleaner = new HtmlCleaner();
//转化成TagNode
TagNode node = cleaner.clean(html);
//----------------------------------------------------//
//通过Xpath定位标题的位置,这里使用//h1和/h1的结果是一样的
Object[] ns = node.evaluateXPath("//div[@id='w3school']//h1");
System.out.println("HTML中的标题是:\t" + ((TagNode)ns[0]).getText());
Object[] ns1 = node.evaluateXPath("//*[@id='w3school']/h1");
System.out.println("HTML中的标题是:\t" + ((TagNode)ns1[0]).getText());
//这里使用//a表示不考虑位置,如果使用/a获取不到内容
Object[] ns2 = node.evaluateXPath("//*[@id='course']/ul//a");
for(Object on : ns2) { //遍历获取课程名以及课程地址
TagNode n = (TagNode) on;
System.out.println("课程名为:\t" + n.getText() +
"\t地址为:\t" + n.getAttributeByName("href"));
}
//获取每个课程名称以及其对应的简介
Object[] ns3 = node.evaluateXPath("//*[@id='maincontent']//div");
for (int i = 1; i < ns3.length-1; i++) {
TagNode n = (TagNode) ns3[i];
//获取课程名称
String courseName = n.findElementByName("h2", true).getText().toString();
//循环遍历所有的p节点获取课程简介
Object[] objarrtr = n.evaluateXPath("//p");
String summary = "";
for(Object on : objarrtr) {
summary += ((TagNode) on).getText().toString();
}
System.out.println(courseName + "\t" + summary);
}
}
}
4.1.5 HtmlParser(HTML文档解析器)——支持CSS语法
1. 导入依赖
<!-- HTML文档解析器 CSS-->
<dependency>
<groupId>org.htmlparser</groupId>
<artifactId>htmlparser</artifactId>
<version>2.1</version>
</dependency>
2. 相关方法
①HTMLParser先实例化Parser
方法 | 说明 |
Parser() | 无参构造 |
Parser(Lexer lexer) | 通过Lexer构造Parser,-----案例中使用了 |
Parser(String resource) | 给一个URL或文件资源,构造Parser |
Parser(URLConnection connection) | 使用URLConnection构造Parser |
②执行过滤任务(Filter)
方法 | 说明 |
TagNameFilter(String name) | 根据标签名称进行过滤 |
NodeClassFilter(Class cls) | 根据类名进行过滤 |
HasAttributeFilter(String attribute) | 匹配出包含指定属性的节点 |
HasAttributeFilter(String attribute,String value) | 匹配出包含指定属性与属性值的节点 |
AndFilter(NodeFilter left,NodeFilter right) | 结合几种过滤条件的“与”过滤器 |
OrFilter(NodeFilter left,NodeFilter right) | “或”过滤器 |
StringFilter(String pattern) | 根据模式对象进行过滤 |
RegexFilter(String pattern) | 根据正则表达式进行过滤 |
LinkStringFilter(String pattern) | 判断链接中是否有某个特定字符串 |
CssSelectorNodeFilter(String selector) | 根据CSS选择器获取节点(最常用) |
③对节点进行操作
方法 | 说明 |
NodeList getChildren() | 获取子节点列表 |
Node getParent() | 获取父节点 |
Node getFirstChild() | 第一个子节点 |
Node getLastChild() | 最后一个子节点 |
Node getPreviousSibling() | 上一个兄弟节点 |
Node getNextSibling() | 下一个兄弟节点 |
String getText() | 节点数据(除了标签号< >) |
String toPlainTextString() | 节点标签内的数据 |
String toHtml() | 返回节点对应的HTML片段 |
Page getPage() | 节点对应的Page对象 |
int getStartPosition() | 节点在HTML起始位置 |
int getEndPosition() | 节点在HTML结束位置 |
String getLink() | 节点的链接属性 |
Tip-1 关于AndFilter()“与”过滤的操作:
//过滤页面中的标签 NodeFilter filtertag= new TagNameFilter("ul"); //父节点包含ul NodeFilter filterParent = new HasParentFilter(filtertag); //包含li标签,并且li节点中包含id属性 NodeFilter filtername = new TagNameFilter("li"); NodeFilter filterId= new HasAttributeFilter("id"); //过滤器的并操作 NodeFilter filter = new AndFilter(filterParent,filtername); NodeFilter filterfinal = new AndFilter(filter,filterId); //选择匹配到的内容 NodeList list = parser.extractAllNodesThatMatch(filterfinal);
非常推荐的CSS选择器方式
package com.xp.climb.climb04.htmlparser;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import org.htmlparser.Node;
import org.htmlparser.Parser;
import org.htmlparser.filters.CssSelectorNodeFilter;
import org.htmlparser.tags.LinkTag;
import org.htmlparser.util.NodeList;
import org.htmlparser.util.ParserException;
public class HTMLParserTest3 {
public static void main(String[] args) throws IOException, ParserException {
//使用URLConnection请求数据
URL url = new URL("https://www.w3school.com.cn/b.asp");
URLConnection conn = url.openConnection();
Parser parser = new Parser(conn);
//css选择器进行过滤操作
CssSelectorNodeFilter divFilter=new CssSelectorNodeFilter ("#course > ul > li");
//选择匹配到的内容
NodeList list = parser.extractAllNodesThatMatch(divFilter);
//循环遍历
for(int i=0; i<list.size();i++){
//获取li的第一个子节点
Node node = (Node)list.elementAt(i).getFirstChild();
System.out.println( "链接为:" + ((LinkTag) node).getLink()
+"\t标题为:" + node.toPlainTextString() );
}
}
}
4.2 JSON的解析(Fastjson)
①导入依赖
<!-- JSON解析器 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
②操作JSON的三种方法案例
package com.xp.climb.climb04.fastjson.parse;
import java.util.List;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.xp.climb.climb04.model.BookModel;
public class FastjsonParseIArrayTest {
public static void main(String[] args) {
//json数组
String json = "[{\"id\":\"01\",\"language\": \"Java\",\"edition\": \"third\",\"author\": \"Herbert Schildt\"},{\"id\":\"07\", \"language\": \"C++\",\"edition\": \"second\",\"author\": \"E.Balagurusamy\"}]";
//使用fastjson解析Json数组
//第一种方式---(泛型)---可操作JSON数组
List<BookModel> listmodel = JSON.parseObject(json, new TypeReference<List<BookModel>>(){});
//第二种方式---(数组)---
// List<BookModel> listmodel = JSON.parseArray(json, BookModel.class);
//json对象
String json1 = "{\"id\":\"07\",\"language\": \"C++\",\"edition\": \"second\",\"author\": \"E.Balagurusamy\"}";
//第三种方式---(对象)---
// BookModel model1 = JSON.parseObject(json1, BookModel.class);
//输出数据
for (BookModel model : listmodel) {
System.out.println(model.getId() + "\t" + model.getLanguage() +
"\t" + model.getEdition());
}
}
}
4.2——实战案例
package com.xp.climb.climb04.demo;
import java.io.IOException;
import java.util.List;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import com.alibaba.fastjson.JSON;
import com.xp.climb.climb04.model.CommentModel;
public class Demo {
private static HttpClient httpClient = HttpClients.custom().build(); //初始化httpclient
public static void main(String[] args) throws Exception {
String url = "http://www.haodou.com/comment.php?do=list&callback=jQuery183016721538977115902_1531563599327&channel=recipe&item=853171&sort=desc&page=1&size=5&comment_id=0&cate=0&purify=common&_=1531563599599";
//获取JSON数据
String jsonstring = getJson(url);
//解析JSON数据
List<CommentModel> datalist = parseData(jsonstring);
//输出数据
for (CommentModel comm : datalist) {
System.out.println(comm.getCommentId() + "\t" + comm.getItemId() + "\t" + comm.getContent());
}
}
//获取JSON内容
public static String getJson(String url) throws ClientProtocolException, IOException{
HttpGet httpget = new HttpGet(url); //使用的请求方法
//发出get请求
HttpResponse response = httpClient.execute(httpget);
//获取网页内容流
HttpEntity httpEntity = response.getEntity();
//以字符串的形式(需设置编码)
String entity = EntityUtils.toString(httpEntity, "gbk");
//关闭内容流
EntityUtils.consume(httpEntity);
return entity; //返回JSON
}
//解析Json内容
public static List<CommentModel> parseData (String json) throws Exception{
json = decode(json); //将uncode码转化为中文
//使用分割以及正则取代,处理成标准化JSON数组
String jsondata = "{"+json.split("data\":\\{")[2].split("\"avatar")[0].replaceAll("\"_\\d*[0-9]\":", "");
String jsonStr = jsondata.substring(0, jsondata.length()-2);
//将json数组解析成对象集合
List<CommentModel> datalis = JSON.parseArray("["+jsonStr.substring(1,jsonStr.length())+"]", CommentModel.class);
return datalis;
}
//将uncode码转化为中文
public static String decode(String unicodeStr) {
if (unicodeStr == null) {
return null;
}
StringBuffer retBuf = new StringBuffer();
int maxLoop = unicodeStr.length();
for (int i = 0; i < maxLoop; i++) {
if (unicodeStr.charAt(i) == '\\') {
if ((i < maxLoop - 5)
&& ((unicodeStr.charAt(i + 1) == 'u') || (unicodeStr
.charAt(i + 1) == 'U')))
try {
retBuf.append((char) Integer.parseInt(
unicodeStr.substring(i + 2, i + 6), 16));
i += 5;
} catch (NumberFormatException localNumberFormatException) {
retBuf.append(unicodeStr.charAt(i));
}
else
retBuf.append(unicodeStr.charAt(i));
} else {
retBuf.append(unicodeStr.charAt(i));
}
}
return retBuf.toString();
}
}