HtmlAgilityPack - 详细简介和使用
HtmlAgilityPack - 简介
HtmlAgilityPack是.net下的一个HTML解析类库。支持用XPath来解析HTML。这个意义不小,为什么呢?因为对于页面上的元素的xpath某些强大的浏览器能够直接获取得到,并不需要手动写。节约了大半写正则表达式的时间,当然正则表达式有时候在进一步获取的时候还需要写,但是通过xpath解析之后,正则表达式已经要匹配的范围已经非常小了。而且,不用正则表达式在整个页面源代码上匹配,速度也会有提升。总而言之,通过该类库,先通过浏览器获取到xpath获取到节点内容然后再通过正则表达式匹配到所需要的内容,无论是开发速度,还是运行效率都有提升。
HtmlAttribute 对应 Html元素的属性
HtmlAttributeCollection 一个元素属性的集合,实现了IList<HtmlAttribute>, ICollection<HtmlAttribute>, IEnumerable<HtmlAttribute>, IEnumerable,都是集合的那一套东西,没有新东西。
HtmlNode 对应 HTML节点,包括注释,文本,元素等
HtmlNodeCollection 一个HtmlNode节点集合,实现了HtmlNodeCollection : IList<HtmlNode>, ICollection<HtmlNode>, IEnumerable<HtmlNode>, IEnumerable继承了这些东西就没什么需要说的了,都是集合的东西,没有新的东西。完全是集合那一套。
HtmlNodeType 一个枚举 表示节点的类型,文档,注释,元素,文本。
HtmlTextNode 对应Html文本节点,很简单的一个类,继承自HtmlNode。
HtmlEntity 对应实体 实用程序类以替换特殊字符的实体,反之亦然
HtmlParseError 表示文档在解析过程中发现的解析错误。
还有一些其他的类,留到以后有用过的时候再补充。
出处:https://www.cnblogs.com/kissdodog/archive/2013/02/19/2917812.html
HtmlAgilityPack 之 HtmlNode类
HtmlAgilityPack中的HtmlNode类与XmlNode类差不多,提供的功能也大同小异。下面来看看该类提供功能。
一、静态属性
public static Dictionary<string, HtmlElementFlag> //ElementsFlags;获取集合的定义为特定的元素节点的特定行为的标志。表包含小写标记名称作为键和作为值的 HtmlElementFlags 组合 DictionaryEntry 列表。
public static readonly string HtmlNodeTypeNameComment; //获取一个注释节点的名称。实际上,它被定义为 '#comment
public static readonly string HtmlNodeTypeNameDocument; //获取文档节点的名称。实际上,它被定义为 '#document'
public static readonly string HtmlNodeTypeNameText; //获取一个文本节点的名称。实际上,它被定义为 '#text'
二、属性
Attributes 获取节点的属性集合
ChildNodes 获取子节点集合(包括文本节点)
Closed 该节点是否已关闭(</xxx>)
ClosingAttributes 在关闭标签的属性集合
FirstChild 获取第一个子节点
HasAttributes 判断该节点是否含有属性
HasChildNodes 判断该节点是否含有子节点
HasClosingAttributes 判断该节点的关闭标签是否含有属性(</xxx class="xxx">)
Id 获取该节点的Id属性
InnerHtml 获取该节点的Html代码
InnerText 获取该节点的内容,与InnerHtml不同的地方在于它会过滤掉Html代码,而InnerHtml是连Html代码一起输出
LastChild 获取最后一个子节点
Line 获取该节点的开始标签或开始代码位于整个HTML源代码的第几行(行号)
LinePosition 获取该节点位于第几列
Name Html元素名
NextSibling 获取下一个兄弟节点
NodeType 获取该节点的节点类型
OriginalName 获取原始的未经更改的元素名
OuterHtml 整个节点的代码
OwnerDocument 节点所在的HtmlDocument文档
ParentNode 获取该节点的父节点
PreviousSibling 获取前一个兄弟节点
StreamPosition 该节点位于整个Html文档的字符位置
XPath 根据节点返回该节点的XPath
代码示例:
static void Main(string[] args)
{
//<ul class="user_match clear">
// <li>年龄:21~30之间</li>
// <li>婚史:未婚</li>
// <li>地区:不限</li>
// <li>身高:175~185厘米之间</li>
// <li>学历:不限</li>
// <li>职业:不限</li>
// <li>月薪:不限</li>
// <li>住房:不限</li>
// <li>购车:不限</li>
//</ul> WebClient wc = new WebClient();
wc.BaseAddress = "http://www.juedui100.com/";
wc.Encoding = Encoding.UTF8;
HtmlDocument doc = new HtmlDocument();
string html = wc.DownloadString("user/6971070.html");
doc.LoadHtml(html);
HtmlNode node = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[2]/ul[1]"); //根据XPath查找节点,跟XmlNode差不多
Console.WriteLine(node.InnerText); //输出节点内容 年龄:21~30之间 婚史:未婚 ...... 与InnerHtml的区别在于,它不会输出HTML代码
Console.WriteLine(node.InnerHtml); //输出节点Html <li>年龄:21~30之间</li> <li>婚史:未婚</li> ....
Console.WriteLine(node.Name); //输出 ul Html元素名 HtmlAttributeCollection attrs = node.Attributes;
foreach(var item in attrs)
{
Console.WriteLine(item.Name + " : " + item.Value); //输出 class :user_match clear
} HtmlNodeCollection CNodes = node.ChildNodes; //所有的子节点
foreach (HtmlNode item in CNodes)
{
Console.WriteLine(item.Name + "-" + item.InnerText); //输出 li-年龄:21~30之间#text- li-婚史:未婚#text- ....... 别忘了文本节点也算
} Console.WriteLine(node.Closed); //输出True //当前的元素节点是否已封闭 Console.WriteLine("================================"); HtmlAttributeCollection attrs1 = node.ClosingAttributes; //获取在结束标记的 HTML 属性的集合。 例如</ul class="">
Console.WriteLine(attrs1.Count); //输出0 HtmlNode node1 = node.FirstChild; //悲剧了ul的第一个节点是一个 \n 换行文本节点 第二个节点才到第一个li
Console.WriteLine(node1.NodeType); //输出Text 文本节点
HtmlNode node3 = node.LastChild; //同样最后一个节点一样是 \n 文本节点
Console.WriteLine(node3.NodeType); //输出Text 文本节点 HtmlNode node2 = node.SelectSingleNode("child::li[1]"); //获取当前节点的第一个子li节点
Console.WriteLine(node2.XPath); //根据节点生成XPath表达式 /html/body/div[4]/div[1]/div[2]/ul[1]/li[1] 妈了个B,强大 Console.WriteLine(node.HasAttributes); //输出 True 判断节点是否含有属性
Console.WriteLine(node.HasChildNodes); //输出 True 判断节点是否含有子节点
Console.WriteLine(node.HasClosingAttributes); //False 判断节点结束标记是否含有属性 Console.WriteLine(node.Line); //输出 155 该节点开始标记位于页面代码的第几行
Console.WriteLine(node.LinePosition); //输出 1 该节点开始标记位于第几列2
Console.WriteLine(node.NodeType); //输出 Element 该节点类型 此处为元素节点
Console.WriteLine(node.OriginalName); //输出 ul
HtmlNode node4 = node.SelectSingleNode("child::li[1]");
Console.WriteLine(node4.InnerText); //输出 年龄:21~30之间
HtmlNode node5 = node4.NextSibling.NextSibling; //获取下一个兄弟元素 因为有一个换行符的文本节点,因此要两次,跳过换行那个文本节点
Console.WriteLine(node5.InnerText); //输出 婚史:未婚
HtmlNode node6 = node5.PreviousSibling.PreviousSibling; //同样两次以跳过换行文本节点
Console.WriteLine(node6.InnerText); //输出 年龄:21~30之间
HtmlNode node7 = node6.ParentNode; //获取父节点
Console.WriteLine(node7.Name); //输出 ul
string str = node.OuterHtml;
Console.WriteLine(str); //输出整个ul代码class="user_match clear"><li>年龄:21~30之间</li>...</ul>
Console.WriteLine(node.StreamPosition); //输出7331 获取此节点的流位置在文档中,相对于整个文档(Html页面源代码)的开始。
HtmlDocument doc1 = node.OwnerDocument;
doc1.Save(@"D:\123.html"); HtmlNode node8 = doc.DocumentNode.SelectSingleNode("//*[@id=\"coll_add_aid59710701\"]");
//<a id="coll_add_aid59710701" style="display:block" class="coll_fix needlogin" href="javascript:coll_add(5971070)">收藏</a>
Console.WriteLine(node8.Id); //输出 coll_add_aid59710701 获取Id属性的内容 Console.ReadKey();
}
三、方法
IEnumerable<HtmlNode> Ancestors(); 返回此元素的所有上级节点的集合。
IEnumerable<HtmlNode> Ancestors(string name); 返回此元素参数名字匹配的所有上级节点的集合。
IEnumerable<HtmlNode> AncestorsAndSelf(); 返回此元素的所有上级节点和自身的集合。
IEnumerable<HtmlNode> AncestorsAndSelf(string name); 返回此元素的名字匹配的所有上级节点和自身的集合。
HtmlNode AppendChild(HtmlNode newChild); 将参数元素追加到为调用元素的子元素(追加在最后)
void AppendChildren(HtmlNodeCollection newChildren); 将参数集合中的元素追加为调用元素的子元素(追加在最后)
HtmlNode PrependChild(HtmlNode newChild); 将参数中的元素作为子元素,放在调用元素的最前面
void PrependChildren(HtmlNodeCollection newChildren); 将参数集合中的所有元素作为子元素,放在调用元素前面
static bool CanOverlapElement(string name); 确定是否可以保存重复的元素
IEnumerable<HtmlAttribute> ChildAttributes(string name); 获取所有子元素的属性(参数名要与元素名匹配)
HtmlNode Clone(); 本节点克隆到一个新的节点
HtmlNode CloneNode(bool deep); 节点克隆到一个新的几点,参数确定是否连子元素一起克隆
HtmlNode CloneNode(string newName); 克隆的同时更改元素名
HtmlNode CloneNode(string newName, bool deep); 克隆的同时更改元素名。参数确定是否连子元素一起克隆
void CopyFrom(HtmlNode node); 创建重复的节点和其下的子树。
void CopyFrom(HtmlNode node, bool deep); 创建节点的副本。
XPathNavigator CreateNavigator(); 返回的一个对于此文档的XPathNavigator
static HtmlNode CreateNode(string html); 静态方法,允许用字符串创建一个新节点
XPathNavigator CreateRootNavigator(); 创建一个根路径的XPathNavigator
IEnumerable<HtmlNode> DescendantNodes(); 获取所有子代节点
IEnumerable<HtmlNode> DescendantNodesAndSelf(); 获取所有的子代节点以及自身
IEnumerable<HtmlNode> Descendants(); 获取枚举列表中的所有子代节点
IEnumerable<HtmlNode> Descendants(string name); 获取枚举列表中的所有子代节点,注意元素名要与参数匹配
IEnumerable<HtmlNode> DescendantsAndSelf(); 获取枚举列表中的所有子代节点以及自身
IEnumerable<HtmlNode> DescendantsAndSelf(string name); 获取枚举列表中的所有子代节点以及自身,注意元素名要与参数匹配
HtmlNode Element(string name); 根据参数名获取一个元素
IEnumerable<HtmlNode> Elements(string name); 根据参数名获取匹配的元素集合
bool GetAttributeValue(string name, bool def); 帮助方法,用来获取此节点的属性的值(布尔类型)。如果未找到该属性,则将返回默认值。
int GetAttributeValue(string name, int def); 帮助方法,用来获取此节点的属性的值(整型)。如果未找到该属性,则将返回默认值。
string GetAttributeValue(string name, string def); 帮助方法,用来获取此节点的属性的值(字符串类型)。如果未找到该属性,则将返回默认值。
HtmlNode InsertAfter(HtmlNode newChild, HtmlNode refChild); 将一个节点插入到第二个参数节点的后面,与第二个参数是兄弟关系
HtmlNode InsertBefore(HtmlNode newChild, HtmlNode refChild); 讲一个节点插入到第二个参数节点的后面,与第二个参数是兄弟关系
static bool IsCDataElement(string name); 确定是否一个元素节点是一个 CDATA 元素节点。
static bool IsClosedElement(string name); 确定是否封闭的元素节点
static bool IsEmptyElement(string name); 确定是否一个空的元素节点。
static bool IsOverlappedClosingElement(string text); 确定是否文本对应于一个节点可以保留重叠的结束标记。
void Remove(); 从父集合中移除调用节点
void RemoveAll(); 移除调用节点的所有子节点以及属性
void RemoveAllChildren(); 移除调用节点的所有子节点
HtmlNode RemoveChild(HtmlNode oldChild); 移除调用节点的指定名字的子节点
HtmlNode RemoveChild(HtmlNode oldChild, bool keepGrandChildren);移除调用节点调用名字的子节点,第二个参数确定是否连孙子节点一起移除
HtmlNode ReplaceChild(HtmlNode newChild, HtmlNode oldChild); 将调用节点原有的一个子节点替换为一个新的节点,第二个参数是旧节点
HtmlNodeCollection SelectNodes(string xpath); 根据XPath获取一个节点集合
HtmlNode SelectSingleNode(string xpath); 根据XPath获取唯一的一个节点
HtmlAttribute SetAttributeValue(string name, string value); 设置调用节点的属性
string WriteContentTo(); 将该节点的所有子级都保存到一个字符串中。
void WriteContentTo(TextWriter outText); 将该节点的所有子级都保存到指定的 TextWriter。
string WriteTo(); 将当前节点保存到一个字符串中。
void WriteTo(TextWriter outText); 将当前节点保存到指定的 TextWriter。
void WriteTo(XmlWriter writer); 将当前节点保存到指定的则 XmlWriter。
示例代码:
static void Main(string[] args)
{
//<ul class="user_match clear">
// <li>年龄:21~30之间</li>
// <li>婚史:未婚</li>
// <li>地区:不限</li>
// <li>身高:175~185厘米之间</li>
// <li>学历:不限</li>
// <li>职业:不限</li>
// <li>月薪:不限</li>
// <li>住房:不限</li>
// <li>购车:不限</li>
//</ul> WebClient wc = new WebClient();
wc.BaseAddress = "http://www.juedui100.com/";
wc.Encoding = Encoding.UTF8;
HtmlDocument doc = new HtmlDocument();
string html = wc.DownloadString("user/6971070.html");
doc.LoadHtml(html);
HtmlNode node = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[2]/ul[1]"); //根据XPath查找节点,跟XmlNode差不多 IEnumerable<HtmlNode> nodeList = node.Ancestors(); //获取该元素所有的父节点的集合
foreach (HtmlNode item in nodeList)
{
Console.Write(item.Name + " "); //输出 div div body html #document
}
Console.WriteLine(); IEnumerable<HtmlNode> nodeList1 = node.Ancestors("body"); //获取名字匹配的该元素的父集合,其实参数就是一个筛选的功能
foreach (HtmlNode item in nodeList1)
{
Console.Write(item.Name + " "); //输出 body
}
Console.WriteLine(); IEnumerable<HtmlNode> nodeList2 = node.AncestorsAndSelf(); //获取所有的父节点和自身
foreach (HtmlNode item in nodeList2)
{
Console.Write(item.Name + " "); //输出 ul div div div body html #document
}
Console.WriteLine(); IEnumerable<HtmlNode> nodeList3 = node.AncestorsAndSelf("div"); //获取父节点和自身,参数用于筛选
foreach (HtmlNode item in nodeList3)
{
Console.Write(item.Name + " "); //输出 div div div
}
Console.WriteLine(); HtmlNode node1 = doc.CreateElement("li");
node1.InnerHtml = "我是附加的li元素";
node.AppendChild(node1); //...<li>购车:不限</li> 后面加了一个<li>我是附加的li元素</li>
Console.WriteLine(node.InnerHtml); HtmlNode node2 = doc.CreateElement("li");
node2.InnerHtml = "新li一";
HtmlNode node3 = doc.CreateElement("li");
node3.InnerHtml = "新li二";
HtmlNodeCollection nc = new HtmlNodeCollection(node2);
nc.Add(node2);
nc.Add(node3);
node.AppendChildren(nc); //一次过追加多个元素
Console.WriteLine(node.InnerHtml); //...<li>我是附加的li元素</li><li>新li一</li><li>新li二</li> Console.WriteLine(HtmlNode.CanOverlapElement("node2")); //输出False 确定是否可以保存一个重复的元素 IEnumerable<HtmlAttribute> attrs = node.ChildAttributes("class"); //获取子节点与自身的所有名为class的属性集合
foreach (HtmlAttribute attr in attrs)
{
Console.Write(attr.Value); //输出 user_match clear
} HtmlNode node4 = node.Clone();
Console.WriteLine(node4.InnerHtml); //输出node的代码,node已被复制到了node HtmlNode node5 = node.CloneNode(false); //参数决定是否复制子节点,与XmlNode一样
Console.WriteLine(node5.OuterHtml); //<ul class="user_match clear"></ul> 因为参数设为了false子节点没有被复制 HtmlNode node6 = node.CloneNode("div"); //复制节点的同时,更改名字
Console.WriteLine(node6.OuterHtml); //输出 <div class="user_match clear"><li>年龄:21~30之间</li>...</div> ul已被改为了div HtmlNode node7 = node.CloneNode("table",false);
Console.WriteLine(node7.OuterHtml); //输出<table class="user_match clear"></table> 参数为false所以没有复制子节点 HtmlNode node8 = node.SelectSingleNode("child::li[1]");
node.CopyFrom(node);
Console.WriteLine(node.OuterHtml);
Console.WriteLine("========================");
//public void CopyFrom(HtmlNode node);
//public void CopyFrom(HtmlNode node, bool deep);
//public XPathNavigator CreateNavigator();
//public XPathNavigator CreateRootNavigator(); HtmlNode node9 = HtmlNode.CreateNode("<li>新节点</li>"); //直接用字符串创建节点,还是挺好用的
Console.WriteLine(node9.OuterHtml); //输出 <li>新节点</li> IEnumerable<HtmlNode> nodeList4 = node.DescendantNodes(); //获取所有的子节点集合
foreach (HtmlNode item in nodeList4)
{
Console.Write(item.OuterHtml); //输出 node的每个子li节点
}
Console.WriteLine("==================="); IEnumerable<HtmlNode> nodeList5 = node.DescendantNodesAndSelf();
foreach (HtmlNode item in nodeList5)
{
Console.Write(item.OuterHtml); //输出自身<ul>..包括子节点<li>...</li></ul> 再输出所有的子li节点
}
Console.WriteLine(); IEnumerable<HtmlNode> nodeList6 = node.DescendantNodes(); //获取枚举列表中的所有子代节
foreach (HtmlNode item in nodeList6)
{
Console.Write(item.InnerText); //输出所有的li节点的内容
}
Console.WriteLine("---------------"); IEnumerable<HtmlNode> nodeList7 = node.Descendants("li"); //获取所有的子后代元素 //文本节点不在此范围内
foreach(HtmlNode item in nodeList7)
{
Console.Write(item.InnerText);
} IEnumerable<HtmlNode> nodeList8 = node.DescendantsAndSelf("ul"); //获取所有的子后代元素 //文本节点不在此范围内
foreach (HtmlNode item in nodeList8)
{
Console.Write(item.Name); //输出 ul 参数实际上只相当于过滤的作用
} HtmlNode node10 = node.Element("li"); //获取第一个子节点名称匹配的元素
Console.WriteLine(node10.InnerText); //输出 年龄:年龄:21~30之间
Console.WriteLine("----------------------------------------"); IEnumerable<HtmlNode> nodeList9 = node.Elements("li");
foreach (HtmlNode item in nodeList9)
{
Console.Write(item.InnerText); //输出 所有的li节点内容
}
Console.WriteLine(); //换一个新的,好像有点乱了
HtmlNode newnode = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[3]");
//<div class="col say">
// <h3>爱情独白</h3>
// <p>愿得一心人,白首不相离。我一直相信我的另一半就在茫茫人海中,有一天一定会与我相遇。</p>
//</div> //bool b = newnode.GetAttributeValue("class", false); //获取一个布尔值的属性,没有找到则返回第二个参数的默认值
//Console.WriteLine(b);
//int i = newnode.GetAttributeValue("class", 0); //获取一个整形的属性,没有找到则返回第二个参数的默认值
//Console.WriteLine(i); string str = newnode.GetAttributeValue("class", ""); //获取一个字符串属性
Console.WriteLine(str); //输出 col say HtmlNode node11 = HtmlNode.CreateNode("<b>我是加粗节点</b>");
HtmlNode node12 = newnode.SelectSingleNode("h3");
newnode.InsertAfter(node11, node12); //意思是在node12代表的h3节点后面插入node11节点
Console.WriteLine(newnode.InnerHtml); //h3>爱情独白</h3><b>我是加粗节点</b><p>愿得一心人... 留意到b节点已经被插入到h3后面 newnode.InsertBefore(node11, node12); //再插入多一次,方法不同罢了,这次是在node12带包的h3前面插入
Console.WriteLine(newnode.InnerHtml); //<b>我是加粗节点</b><h3>爱情独白</h3><b>我是加粗节点</b><p>愿得一心人 Console.WriteLine("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
newnode.RemoveChild(node11); //移除了第一个<b>我是加粗节点</b> 此方法的重载,第二个参数决定是否移除孙子节点
Console.WriteLine(newnode.InnerHtml); //<h3>爱情独白</h3><b>我是加粗节点</b><p>愿得一心人.... newnode.RemoveAllChildren(); //移除所有子节点
Console.WriteLine(newnode.OuterHtml); //<div class="col say"></div> 所有子节点都被移除了 newnode.RemoveAll(); //移除所有的属性和子节点,由于子节点已经被上个方法移除了,因此这次连属性也移除了
Console.WriteLine(newnode.OuterHtml); //输出 <div></div> 注意到属性也被移除了。 //都移除光了,再来一个,还是刚才那个
HtmlNode newnode1 = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[3]");
Console.WriteLine("===================");
Console.WriteLine(newnode1.OuterHtml); //输出 <div></div> 注意 移除是从HtmlDocument中移除的,再次获取获取不到了 HtmlNode newnode2 = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[2]/div[2]/p");
Console.WriteLine(newnode2.OuterHtml);
//<p class="no_tip">她还没有设置不能忍受清单
// <a href="javascript:invite(5971070,8,'邀请设置不能忍受');" class="link_b needlogin">邀请她设置</a>
//</p>
newnode2.Remove(); //从文档树中移除newnode2节点
HtmlNode newnode3 = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[2]/div[2]/p"); //再次获取该节点
//Console.WriteLine(newnode3.OuterHtml); //报未将对象引用到对象的实例异常,明显是找不到了, HtmlNode newnode4 = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[1]/div/div[1]/p[2]/b[1]");
Console.WriteLine(newnode4.OuterHtml);
//<b>相册:
// <a href="/photo/6971070.html" class="red">4</a>张
//</b>
HtmlNode node17 = HtmlNode.CreateNode("<div>再次创建一个节点</div>");
newnode4.PrependChild(node17); //跟AppengChild类似,只是插入位置不同PrependChildren接受一个节点集合,一次过插入多个节点而已
Console.WriteLine(newnode4.OuterHtml);
//输出
//<b>相册:
// <div>再次创建一个节点</div>
// <a href="/photo/6971070.html" class="red">4</a>张
//</b>
HtmlNode node16 = newnode4.SelectSingleNode("child::a[1]");
HtmlNode node18 = HtmlNode.CreateNode("<p>新建一行</p>");
newnode4.ReplaceChild(node18, node16);
Console.WriteLine(newnode4.OuterHtml);
//输出
//<b>相册:
// <div>再次创建一个节点</div>
// <p>新建一行</p>张 //留意到node16代表得节点已经被替换掉了
//</b> HtmlNode node19 = newnode4.SelectSingleNode("child::p[1]");
node19.SetAttributeValue("class","class1");
Console.WriteLine(node19.OuterHtml); //输出 <p class="class1">新建一行</p> Console.WriteLine(HtmlNode.IsOverlappedClosingElement("<a>我爱你</a>")); //输出 False
Console.WriteLine(HtmlNode.IsCDataElement("<a>我爱你</a>")); //输出 False
Console.WriteLine(HtmlNode.IsClosedElement("<a>我爱你</a>")); //输出 False
Console.WriteLine(HtmlNode.IsEmptyElement("<a>我爱你</a>")); //输出 False
Console.WriteLine(newnode4.OuterHtml); HtmlNode node20 = HtmlNode.CreateNode("<p>新的第二行</p>");
newnode4.AppendChild(node20);
HtmlNodeCollection hnc = newnode4.SelectNodes("//p"); //根据XPath一次过获取多个Node
Console.WriteLine(hnc.Count); //输出29 string str1 = node20.WriteContentTo();
Console.WriteLine(str1); //输出 新的第二行 将节点内容写入字符串 //public void WriteContentTo(TextWriter outText);
//public string WriteTo();
//public void WriteTo(TextWriter outText);
//public void WriteTo(XmlWriter writer);
Console.ReadKey();
}
出处:https://www.cnblogs.com/kissdodog/archive/2013/02/28/2936950.html
HtmlNodeType枚举
HtmlNodeType是一个枚举,用于说明一个节点的类型。
源代码如下所示:
public enum HtmlNodeType
{
Document = ,
Element = ,
Comment = ,
Text = ,
}
1、Document 是文档
2、Element 是元素节点
3、Conment 是注释节点
4、Text 是文本节点
HtmlAttribute HTML属性处理类
HtmlAttribute 在HtmlAgilityPack扮演的是一个HTML代码属性的容器,同时提供了用于处理HTML属性的一些功能。
一、属性
int Line { get; } 获取文档中的此属性的行数。
int LinePosition { get; } 获取文档中此属性所在列数
string Name { get; set; } 当前属性的名称
string OriginalName { get; } 当前属性未经更改的属性
HtmlDocument OwnerDocument { get; } 返回当前属性所在的文档引用
HtmlNode OwnerNode { get; } 当前属性所在节点的引用
AttributeValueQuote QuoteType { get; set; } 返回一个枚举值,指示属性包装在单引号里还是双引号里
int StreamPosition { get; } 此属性开始位置位于整个文档的字符位置
string Value { get; set; } 此属性的值
string XPath { get; } 返回属性的访问XPath表达式
二、方法
HtmlAttribute Clone(); 克隆到另外一个HttpAttribute
int CompareTo(object obj); 将当前实例与另一个属性进行比较。比较基于属性的名称。
void Remove(); 从文档中移除该属性
static void Main(string[] args)
{
//<ul class="user_match clear">
// <li>年龄:21~30之间</li>
// <li>婚史:未婚</li>
// <li>地区:不限</li>
// <li>身高:175~185厘米之间</li>
// <li>学历:不限</li>
// <li>职业:不限</li>
// <li>月薪:不限</li>
// <li>住房:不限</li>
// <li>购车:不限</li>
//</ul> WebClient wc = new WebClient();
wc.BaseAddress = "http://www.juedui100.com/";
wc.Encoding = Encoding.UTF8;
HtmlDocument doc = new HtmlDocument();
string html = wc.DownloadString("user/6971070.html");
doc.LoadHtml(html);
HtmlNode node = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[2]/ul[1]"); //根据XPath查找节点,跟XmlNode差不多
HtmlAttribute attr = node.Attributes["class"];
Console.WriteLine(attr.Line); //输出 155 此属性所在文档的行数
Console.WriteLine(attr.LinePosition); //输出 6 此属性位于文档的列数
Console.WriteLine(attr.Name); //输出 class 属性名
Console.WriteLine(attr.OriginalName); //输出 class 未经过更改的原始属性名
Console.WriteLine(attr.OwnerDocument); //获取所在文档
HtmlNode node1 = attr.OwnerNode;
Console.WriteLine(node1.Name); //输出 ul
AttributeValueQuote ty = attr.QuoteType; //指定的数据包装在双引号里还是单引号里
Console.WriteLine(ty.ToString()); //输出 DoubleQuote AttributeValueQuote是一个枚举,只有两个值SingleQuote与DoubleQuote
Console.WriteLine(attr.StreamPosition); //输出7355 属性所在文档的字符位置
Console.WriteLine(attr.Value); //输出 user_match clear 属性的值
Console.WriteLine(attr.XPath); //输出 /html[1]/body[1]/div[4]/div[1]/div[2]/ul[1]/@class[1] 当前属性的访问XPath表达式 HtmlAttribute attr1 = attr.Clone();
Console.WriteLine(attr1.Name + " : " + attr1.Value); //输出 class : user_match clear
//Compareto(Object obj)
attr.Remove();
Console.WriteLine(node.OuterHtml); //输出 <ul><li>.....</ul> 该属性已被移除 Console.ReadKey();
}
出处:https://www.cnblogs.com/kissdodog/archive/2013/02/28/2937795.html
HtmlTextNode & HtmlCommentNode
在HtmlAgilityPack里,HtmlTextNode对应的是文本节点。这是一个非常简单的一个类,方法和字段都比较少。
一、属性
override string InnerHtml { get; set; } 文本内的HTML代码(不包括自身)
override string OuterHtml { get; } 整个文本节点的Html代码
string Text { get; set; } 文本字符串
二、方法
internal HtmlTextNode(HtmlDocument ownerdocument, int index);
代码示例:
static void Main(string[] args)
{
//<ul class="user_match clear">
// <li>年龄:21~30之间</li>
// <li>婚史:未婚</li>
// <li>地区:不限</li>
// <li>身高:175~185厘米之间</li>
// <li>学历:不限</li>
// <li>职业:不限</li>
// <li>月薪:不限</li>
// <li>住房:不限</li>
// <li>购车:不限</li>
//</ul> WebClient wc = new WebClient();
wc.BaseAddress = "http://www.juedui100.com/";
wc.Encoding = Encoding.UTF8;
HtmlDocument doc = new HtmlDocument();
string html = wc.DownloadString("user/6971070.html");
doc.LoadHtml(html);
HtmlNode node = doc.DocumentNode.SelectSingleNode("/html/body/div[4]/div[1]/div[2]/ul[1]/li[1]"); //根据XPath查找节点,跟XmlNode差不多
//在此处node是第一个li节点
HtmlTextNode tNode = node.FirstChild as HtmlTextNode;
Console.WriteLine(tNode.Text); //输出 年龄:21~30之间
Console.WriteLine(tNode.InnerHtml); //输出 年龄:21~30之间
Console.WriteLine(tNode.OuterHtml); //输出 年龄:21~30之间 奇怪没什么变化 Console.ReadKey();
}
HtmlCommentNode类与HtmlTextNode几乎一样,因此不再叙述。
出处:https://www.cnblogs.com/kissdodog/archive/2013/02/28/2937838.html
HtmlDocument
HtmlDocument类对应着一个HTML文档代码。它提供了创建文档,装载文档,修改文档等等一系列功能,来看看它提供的功能。
一、属性
int CheckSum { get; } 如果 OptionComputeChecksum 设置为 true 之前解析,0 否则获取文档 CRC32 校验和。
Encoding DeclaredEncoding { get; } 获取文档的声明的编码。声明确定编码使用 meta http-equiv ="内容类型"内容 ="文本/html ; charset = XXXXX"html 节点。
HtmlNode DocumentNode { get; } 获取文档的根节点。
Encoding Encoding { get; } 获取文档的输出编码。
IEnumerable<HtmlParseError> ParseErrors { get; } 获取文档在解析过程中,发现的解析错误集合
string Remainder { get; } 获取剩余的文本。如果 OptionStopperNodeName 为空,此属性将始终为空。
int RemainderOffset { get; } 获取原始 Html 文本中其余部分的偏移量。如果 OptionStopperNodeName 为 null,这将返回原始 Html 文本的长度。
Encoding StreamEncoding { get; } 获取文档的流的编码。
二、方法
HtmlAttribute CreateAttribute(string name); 创建一个属性,指定名称
HtmlAttribute CreateAttribute(string name, string value); 创建一个属性,指定名称和值
HtmlCommentNode CreateComment(); 创建一个空的注释节点
HtmlCommentNode CreateComment(string comment); 使用指定的名称创建一个注释节点
HtmlNode CreateElement(string name); 使用指定的名称创建一个 HTML 元素节点。
XPathNavigator CreateNavigator(); 创建一个XPathNavigator 对象
HtmlTextNode CreateTextNode(); 创建一个文本节点
HtmlTextNode CreateTextNode(string text); 创建一个文本节点,并用参数的值赋值
Encoding DetectEncoding(Stream stream); 检测到的 HTML 流的编码。
Encoding DetectEncoding(string path); 检测编码的 HTML 文本。
Encoding DetectEncoding(TextReader reader); 检测到的关于 TextReader 提供 HTML 文本的编码。
void DetectEncodingAndLoad(string path); 检测到第一,从一个文件的 HTML 文档的编码,然后加载该文件。
void DetectEncodingAndLoad(string path, bool detectEncoding); 检测到第一,从一个文件的 HTML 文档的编码,然后加载该文件。
Encoding DetectEncodingHtml(string html); 检测编码的 HTML 文本。
HtmlNode GetElementbyId(string id); 根据Id查找一个节点
static string GetXmlName(string name); 获取一个有效的 XML 名称。
static string HtmlEncode(string html); 静态方法,对一个字符串进行HTML编码
static bool IsWhiteSpace(int c); 确定指定的字符是否是一个空白字符。
void Load(Stream stream); 从流中加载一个文档
void Load(string path); 从路径中加载一个文档
void Load(TextReader reader);
void Load(Stream stream, bool detectEncodingFromByteOrderMarks);
void Load(Stream stream, Encoding encoding);
void Load(string path, bool detectEncodingFromByteOrderMarks);
void Load(string path, Encoding encoding);
void Load(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks);
void Load(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks);
void Load(Stream stream, Encoding encoding, bool detectEncodingFromByteOrderMarks, int buffersize);
void Load(string path, Encoding encoding, bool detectEncodingFromByteOrderMarks, int buffersize);
void LoadHtml(string html); 从字符串中加载一个文档
void Save(Stream outStream); 将当前HTML文档保存入流
void Save(StreamWriter writer);
void Save(string filename); 将HTML文档保存到指定的路径
void Save(TextWriter writer);
void Save(XmlWriter writer);
void Save(Stream outStream, Encoding encoding);
void Save(string filename, Encoding encoding);
属性代码示例:
static void Main(string[] args)
{
WebClient wc = new WebClient();
wc.BaseAddress = "http://www.juedui100.com/";
wc.Encoding = Encoding.UTF8;
HtmlDocument doc = new HtmlDocument();
string html = wc.DownloadString("user/6971070.html");
doc.LoadHtml(html); int i = doc.CheckSum; //如果 OptionComputeChecksum 设置为 true 之前解析,0 否则获取文档 CRC32 校验和。
Console.WriteLine(i); //输出 0 Encoding enc = doc.DeclaredEncoding; //获取文档的声明的编码。
Console.WriteLine(enc.BodyName); //输出 utf-8 HtmlNode node = doc.DocumentNode; //获取文档的根节点
Console.WriteLine(node.Name); //输出 #document Encoding enc1 = doc.Encoding; //获取文档的输出编码
Console.WriteLine(enc1.BodyName); //输出utf-8 IEnumerable<HtmlParseError> eList = doc.ParseErrors; //文档在解析过程中发现的解析错误集合 string str = doc.Remainder; //获取剩余的文本。
Console.WriteLine(str); //什么都没输出 int offset = doc.RemainderOffset; //获取原始 Html 文本中其余部分的偏移量。
Console.WriteLine(offset); //输出 25762 Encoding enc2 = doc.StreamEncoding;
Console.WriteLine(enc2.BodyName); Console.ReadKey();
}
方法代码示例:
用于测试的HTML代码:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body> </body>
</html>
主程序代码:
static void Main(string[] args)
{
HtmlDocument doc = new HtmlDocument();
doc.Load(@"D:\1234.html"); //此方法有11个重载,支持各种加载Html文档
//Console.WriteLine(doc.DocumentNode.InnerHtml); 已经加载成功,输出D:123.html的页面代码
HtmlNode node1 = doc.CreateElement("div"); //本文档创建一个节点
node1.InnerHtml = "我是一个div";
doc.DocumentNode.SelectSingleNode("//body").AppendChild(node1); //将节点追加到body里 HtmlAttribute attr = doc.CreateAttribute("class", "class1");
doc.DocumentNode.SelectSingleNode("/html/body/div[1]").Attributes.Add(attr); //此方法也可以用两个参数添加。
//以上代码执行之后 body里的内容变为 <body><div class="class1">我是一个div</div></body> 看到属性又被添加进去了 HtmlCommentNode cNode = doc.CreateComment();
cNode.Comment = "<!--这是一段注释-->"; //应该不是这样写的吧?可能是我写错了,先跳过这一段
doc.DocumentNode.SelectSingleNode("/html/body/div[1]").AppendChild(cNode); //虽然达到了目的,但是应该不是这样写的吧
//执行之后
//<body>
// <div class="class1">我是一个div<!--这是一段注释--></div> 留意到注释节点已添加进去了
//</body> HtmlTextNode tNode = doc.CreateTextNode("我是一个文本节点");
doc.DocumentNode.SelectSingleNode("/html/body/div[1]").AppendChild(tNode);
//执行之后
//<body>
// <div class="class1">我是一个div<!--这是一段注释-->我是一个文本节点</div> //留意到文本节点已添加进去了
//</body> Encoding enc = doc.DetectEncoding(@"D:\1234.html"); //3个重载,应该是从流中,TextWriter中和 路径中检测编码
//Console.WriteLine(enc.BodyName); //获取不到对象,不知道哪里错了 HtmlNode node = doc.CreateElement("p");
node.InnerHtml = "我是一个p";
HtmlAttribute attr2 = doc.CreateAttribute("id","id1");
node.Attributes.Add(attr2);
doc.DocumentNode.AppendChild(node); HtmlNode node2 = doc.GetElementbyId("id1"); //根据Id查找节点
Console.WriteLine(node2.InnerText); //输出 我是一个p string strHtml = "<b>我是一个加粗节点</b>";
string s = HtmlDocument.HtmlEncode(strHtml);
Console.WriteLine(s); //输出 <b>我是一个加粗节点</b> 这是经过HTML编码的字符串 string str = HtmlDocument.GetXmlName("<sss"); //根据字符串获取一个有效的XML名称
Console.WriteLine(str); //输出 _3c_sss Console.WriteLine(HtmlDocument.IsWhiteSpace()); //True
Console.WriteLine(HtmlDocument.IsWhiteSpace()); //False doc.Save(@"D:\123.html"); //Save方法有多个重载,可以通过流,路径,并且还能指定编码等等。 HtmlDocument doc1 = new HtmlDocument();
string html = File.ReadAllText(@"D:\123.html");
doc1.LoadHtml(html); //此方法表示从一个字符串中载入HtmlDocument Console.ReadKey();
}
出处: https://www.cnblogs.com/kissdodog/archive/2013/02/28/2937900.html
HtmlWeb类
HtmlWeb类是一个从网络上获取一个HTML文档的类,其提供的功能大多是基于完成此需求出发。现在来来HtmlWeb类有哪些方法以及属性。
一、属性
bool AutoDetectEncoding { get; set; } 获取或设置一个值,该值指示是否必须将自动检测文档编码。
bool CacheOnly { get; set; } 获取或设置一个值,该值指示是否只从缓存中获取的文档。如果此设置为 true 并且文档未找到在缓存中,并不会加载。
string CachePath { get; set; } 获取或设置缓存路径。如果为 null,则将使用无缓存的机制。
bool FromCache { get; } 获取一个值,该值指示是否从缓存中检索的最后一次加载的文档。
Encoding OverrideEncoding { get; set; } 获取或设置用于重写从任何 web 请求的响应流的编码。
int RequestDuration { get; } 获取上次请求持续时间,以毫秒为单位。
Uri ResponseUri { get; } 获取的互联网资源的实际响应请求的 URI。
HttpStatusCode StatusCode { get; } 获取上次请求状态。
int StreamBufferSize { get; set; } 获取或设置用于内存操作的缓冲区的大小。
bool UseCookies { get; set; } 获取或设置一个值,该值指示是否将存储的 cookie。
string UserAgent { get; set; } 获取或设置任何 webrequest 上发送的用户代理 HTTP 1.1 标头
bool UsingCache { get; set; } 获取或设置一个值,指示是否使用的缓存机制。
二、方法(删减了不少重载)
object CreateInstance(string url, Type type); 从指定的互联网资源创建给定类型的实例。
void Get(string url, string path); 从互联网资源获取 HTML 文档并将其保存到指定的文件。
string GetCachePath(Uri uri); 获取指定的 url 缓存文件路径。
static string GetContentTypeForExtension(string extension, string def); 获取给定的路径扩展的 MIME 内容类型。
static string GetExtensionForContentType(string contentType, string def); 获取给定的 MIME 内容类型的路径扩展。
HtmlDocument Load(string url); 从一个网址加载代码并返回一个HtmlDocument
void LoadHtmlAsXml(string htmlUrl, string xsltUrl, XsltArgumentList xsltArgs, XmlTextWriter writer); 从互联网资源加载 HTML 文档,并将其保存到指定的 XmlTextWriter。
代码示例:由于对HTTP方面的很多知识尚不熟悉,因此先跳过,以后再补充那些不懂的。
static void Main(string[] args)
{
HtmlWeb web = new HtmlWeb();
Console.WriteLine(web.AutoDetectEncoding); //输出 True
Console.WriteLine(web.CacheOnly); //输出 False
Console.WriteLine(web.CachePath); //输出 空白(啥都不输出)
Console.WriteLine(web.OverrideEncoding); //输出 空白
Console.WriteLine(web.RequestDuration); //输出 0 上次持续请求时间为0?
Console.WriteLine(web.ResponseUri); //输出 空白
Console.WriteLine(web.StatusCode); //输出 Ok 就是 200了,枚举来的
Console.WriteLine(web.StreamBufferSize); //输出 1024
Console.WriteLine(web.UseCookies); //输出 False
Console.WriteLine(web.UserAgent); //输出 FireFox...................................
Console.WriteLine(web.UsingCache); //输出 False
HtmlDocument doc = web.Load("http://www.juedui100.com");
Console.WriteLine(doc.DocumentNode.SelectSingleNode("//title").InnerText); //输出 交友_征婚_找对象,上绝对100婚恋交友网 Uri uri = new Uri("http://www.juedui100.com"); web.CachePath = @"D:\juedui100\";
web.UsingCache = true; //要先开启使用缓存,下面的方法才能够使用
Console.WriteLine(web.GetCachePath(uri)); web.Get("http://www.juedui100.com",@"D:\juedui100.html");
//二、方法(删减了不少重载) //object CreateInstance(string url, Type type); 从指定的互联网资源创建给定类型的实例。
//static string GetContentTypeForExtension(string extension, string def); 获取给定的路径扩展的 MIME 内容类型。
//static string GetExtensionForContentType(string contentType, string def); 获取给定的 MIME 内容类型的路径扩展。
//void LoadHtmlAsXml(string htmlUrl, string xsltUrl, XsltArgumentList xsltArgs, XmlTextWriter writer); 从互联网资源加载 HTML 文档,并将其保存到指定的 XmlTextWriter。 Console.ReadKey();
}
出处:https://www.cnblogs.com/kissdodog/archive/2013/03/01/2938698.html
HtmlAgilityPack下载开启压缩的页面乱码
当一个被采集的网页是开启压缩了的话,如果使用HtmlAgilityPack 的HtmlWeb默认配置去下载,下载回来的HTML代码是乱码,应该进行如下操作
HtmlWeb web = new HtmlWeb();
HtmlAgilityPack.HtmlWeb.PreRequestHandler handler = delegate(HttpWebRequest request)
{
request.Headers[HttpRequestHeader.AcceptEncoding] = "gzip, deflate";
request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
request.CookieContainer = new System.Net.CookieContainer();
return true;
};
web.PreRequest += handler;
web.OverrideEncoding = Encoding.Default;
而如果仅仅只是网页的编码问题,则只需要配置这个参数:
web.OverrideEncoding = Encoding.Default;
HtmlAgilityPack - 详细简介和使用的更多相关文章
- HtmlAgilityPack 详细使用
使用HtmlAgilityPack抓取网页数据 http://www.cnblogs.com/yxlblogs/p/3499420.html http://www.cnblogs.com/xdpxyx ...
- JWT的详细简介
什么是JWT Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准.该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场 ...
- .Net Core HTML解析利器之HtmlAgilityPack
一 .HtmlAgilityPack简介 这是一个敏捷的HTML解析器,它构建了一个读/写DOM,并支持简单的XPATH或XSLT(实际上,你实际上并不了解XPATH和XSLT来使用它,不必担心).它 ...
- logging日志模块详细,日志模块的配置字典,第三方模块的下载与使用
logging日志模块详细 简介 用Python写代码的时候,在想看的地方写个print xx 就能在控制台上显示打印信息,这样子就能知道它是什么 了,但是当我需要看大量的地方或者在一个文件中查看的时 ...
- js基础篇——变量
a.变量类型 变量类型 构造函数 举例 类型检测typeof 字符串 function String() var t = "chua"; var m = new String(&q ...
- WOSA协议(转)
WOSA(全称是Windows开放式系统体系结构 Windows Open System Architecture),是微软公司提出的一种在Windows操作系统下的软件架构 WOSA/XFS是基于W ...
- ArcGIS 10.3 安装及破解
系统环境:win7 64位操作系统. 一.ArcGIS 10.3包简介 ArcGIS 10.3 下载包含 1. ArcGIS for Desktop ArcGIS for Desktop简介: Ar ...
- phpstorm-file watcher
在项目中使用了sass,将scss编译成css的时候,每次都需要compass watch netbeans产品带有file watcher功能 三大类 1,less,scss,sass into c ...
- linux_vim_最佳快捷键
如何使用vi文本编辑器 vi由比尔·乔伊(Bill Joy)撰写,所有UNIX like均默认安装此文本编辑器.详细简介请点击维基中文. 1.首先复制一个文件到/tmp目录(本例中为复制根目录 ...
随机推荐
- 【IntelliJ IDEA学习之七】maven专题
版本:IntelliJIDEA2018.1.4 一.maven全局设置 设置Runner 运行VM参数:-Xms128m -Xmx512m -Duser.language=zh -Dfile.enco ...
- MySQL 索引小结
1.!=.not in 在primary key上使用 !=.not in,explain 的 type 是 range,非primary key是全表扫描(即非主键字段即使有索引也无法应用) 2.a ...
- Java学习:等待唤醒机制
等待唤醒机制 线程的状态 NEW 至今尚未启动的线程处于这种状态 RUNNABLE 正在Java虚拟机中执行的线程处于这种状态 BLOCKED 受阻塞并等待某个监视器锁的线程处于这种状态 WA ...
- 使用Docker安装mysql,挂载外部配置和数据
.挂载外部配置和数据安装 mkdir /opt mkdir /opt/mysql mkdir /opt/mysql/conf.d mkdir /opt/mysql/data/ 创建my.cnf配置文件 ...
- mvc企业微信开发全局配置
protected void RegisterSenparcWork() { var isGLobalDebug = false;//设置全局 Debug 状态 ...
- C# 使用SuperSocket
一.需求场景 (1)使用SuperSocket进行网络通信 二.所需软件 (1)SocketTool 三.实现步骤 (1)使用Nuget管理器安装SuperSocket.Engine 该插件已经包 ...
- Navicat 破解版(操作非常简单)
Navicat 破解版(操作非常简单) 参考这位老哥的博客,之前试过好多个,只有这个是最简单有效的 https://blog.csdn.net/WYpersist/article/details/86 ...
- 推荐算法之E&E
一.定义 E&E就是探索(explore)和利用(exploit). Exploit:基于已知最好策略,开发利用已知具有较高回报的item(贪婪.短期回报),对于推荐来讲就是用户已经发现的兴趣 ...
- [转]C++类成员修饰const和mutable
const:常量,不变的 mutable:易变的 从意思上理解,可见const和mutable是一对反义词,它们都是C++的关键字. const成员函数不能修改调用它的对象.类的成员函数可以被声明为c ...
- 使用代码获得Hybris Commerce里显示的产品图片
使用下面这个API去取Hybris Commerce系统里产品主数据的明细信息: https://:9002/rest/v2/electronics/products/300938?fields=FU ...