前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >左手用R右手Python系列17——CSS表达式与网页解析

左手用R右手Python系列17——CSS表达式与网页解析

作者头像
数据小磨坊
发布于 2018-04-11 09:48:09
发布于 2018-04-11 09:48:09
1.7K00
代码可运行
举报
文章被收录于专栏:数据小魔方数据小魔方
运行总次数:0
代码可运行

上一篇着重讲解了网页解析中的XPath表达式,今天这一篇主要讲解另一套网页解析语法——CSS路径表达式。

R语言与Python中都有支持CSS表达式的解析库,R语言中以rvest包为主进行讲解,Python中为BeautifulSoup为主进行讲解。

本篇讲解内容实战网页时我的天善社区博客主页,网址如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
https://ask.hellobi.com/blog/datamofang/sitemap/

R语言:

R语言中,rvest中的默认解析语法即为css路径表达式,当然rvest也是支持XPath,只是XPath并非首选语法,而是备选语法,怎么知道呢,打印一下rvest的html_nodes函数参数内容即可得知。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
library("rvest")
url<-"https://ask.hellobi.com/blog/datamofang/sitemap/"
content<-read_html(url,encoding="UTF-8")

1、特殊符号:

  • “.”表示class(class属性值内含有空格,以.替代)
  • “#”表示id
  • “ ”空格也表示所有后代子元素,相当于xpath中的相对路径(//)
  • “>”表示子元素,相当于XPath中的绝对路径(/)
  • “*”匹配所有元素
  • “,”或条件,同时符合两个条件
  • “+”右侧相邻元素
  • “~”兄弟节点

以上是CSS表达式中几个最为常用的特殊符号,这些特殊符号在路径定位中都有着特殊意义,接下来一个一个进行解释。

“.”/“#”(class属性与id属性)

“.”和“#”分别代表标签内class属性和id属性的连接符。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<div style="margin-top: 25px;" id="raindu" class="btn-group btn-group-justified blog-menu" role="group">
     <a href="/blog/datamofang"  class="btn btn-default">我的首页</a>
     <a href="/blog/datamofang/sitemap/"  class="btn btn-default">博客地图</a>
</div>
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
myhtml<-'<div style="margin-top: 25px;" id="raindu" class="btn-group btn-group-justified blog-menu" role="group">
<a href="/blog/datamofang"  class="btn btn-default">我的首页</a>
<a href="/blog/datamofang/sitemap/"  class="btn btn-default">博客地图</a>
</div>'
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
read_html(myhtml,encoding="UTF-8")%>% html_nodes("div.btn-group a") %>% html_text()
#[1] "我的首页" "博客地图"
read_html(myhtml,encoding="UTF-8")%>% html_nodes("div#raindu a") %>% html_text()
#[1] "我的首页" "博客地图"

可以看到以上两句表达式都可以完美匹配出来div标签节点内部a节点内的文本,这里的定位主要是靠‘.’和’#’两个连接符实现的,这是相对比较规范的写法。

以上表达式写法中还有一个细节性的小知识点,就是class属性值倘若特别长,可以截取其前几个字符(可以作为唯一辨识就可以),倘若内部有空格,空格可以以“.”号替代,否则可能引起表达式匹配错误。

“>”和“ ”(右尖括号和空格)

右尖括号和空格在css表达式中起着重要作用,相信看过前一篇文章的一定记得我在解释XPath路径表达式的时候讲过绝对路径和相对路径,其详细内含这里就不解释了,如果你感兴趣可以查看前文,这里的“>”和”“ ”就扮演了css表达式中绝对路径和相对路径的角色。

左手用R右手Python系列16——XPath与网页解析库

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<li style="line-height: 28px;">
     <a style="" target="_blank" href="/blog/datamofang/9485">
        <b>balabalabala</b>
     离散颜色标度连续化的最佳方案
     </a>
     <span style="margin-left: 21px;"> 56次阅读/0条评论</span>
     <span style="margin-left: 5px;"> (2017-08-22)</span>
     <span style="margin-left: 21px;"> </span>
</li>
xml

myhtml<-'<li style="line-height: 28px;">
     <a style="" target="_blank" href="/blog/datamofang/9485">
        <b>balabalabala</b>
     离散颜色标度连续化的最佳方案
     </a>
     <span style="margin-left: 21px;"> 56次阅读/0条评论</span>
     <span style="margin-left: 5px;"> (2017-08-22)</span>
     <span style="margin-left: 21px;"> </span>
</li>'

绝对路径:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
read_html(myhtml,encoding="UTF-8")%>% html_nodes("li>a>b") %>% html_text()
[1] "balabalabala"

相对路径:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
read_html(myhtml,encoding="UTF-8")%>% html_nodes("li a b") %>% html_text()
[1] "balabalabala"read_html(myhtml,encoding="UTF-8")%>% html_nodes("li b") %>% html_text()
[1] "balabalabala"

从以上三个输出可以很明确的发现,所有的输出结果都是一样的,第一句函数执行的功能是在文档中查找li节点内的子节点a节点内的子节点b,并输出其文本内容;第二句函数执行的功能是查找文档中li节点内的所有节点为a(相对路径)的节点内所有节点为b的节点(相对路径),并输出其文本。第三句函数执行功能为在文档中查找所有li节点内所有节点为b的节点并输出其内容。因为myhtml文档中只有一个b节点,所有三者输出的内容是一样的。

“>”和“ ”(右尖括号和空格)的区别非常明显,也非常重要,请慎用“>”(绝对路径),只有在有100%把握的时候再用,一般来说使用“ ”(空格:相对路径)的css表达式比较稳健,但是在同一个文档中同名节点较多的情况下,因为相对路径需要遍历的路径较多,耗时长,可能匹配出没有价值的内容,所以在实际使用时还是要随机应变。

“*”和“,”星号和单引号:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
read_html(myhtml,encoding="UTF-8")%>% html_nodes("li *[style]") %>% html_text()
[1] "\nbalabalabala\n离散颜色标度连续化的最佳方案\n" " 56次阅读/0条评论"                     
[3] " (2017-08-22)"          " "         
read_html(myhtml,encoding="UTF-8")%>% html_nodes(" *[style]") %>% html_text()
[1] "\n     \nbalabalabala\n离散颜色标度连续化的最佳方案\n\n 56次阅读/0条评论\n (2017-08-22)\n \n"
[2] "\nbalabalabala\n离散颜色标度连续化的最佳方案\n"                                              
[3] " 56次阅读/0条评论"                                                                           
[4] " (2017-08-22)"                                                                               
[5] " "

以上第一句执行的功能是在li节点中查询所有包含style属性的节点,并输出对应节点文本内容,第二个匹配的范围更大一些,匹配了文档中所有包含style属性的节点并输出对应节点文本内容。可以看到li这个顶层节点内的所有文本被拼接在一起作为li的文本对象被输出了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
read_html(myhtml,encoding="UTF-8")%>% html_nodes("li a[target]") %>% html_text()
[1] "\nbalabalabala\n离散颜色标度连续化的最佳方案\n"
read_html(myhtml,encoding="UTF-8")%>% html_nodes("li span") %>% html_text()
[1] " 56次阅读/0条评论" " (2017-08-22)"     " "     
read_html(myhtml,encoding="UTF-8")%>% html_nodes("li a[target],li span") %>% html_text()
[1] "\nbalabalabala\n离散颜色标度连续化的最佳方案\n" " 56次阅读/0条评论"                      
[3] " (2017-08-22)"                                  " "

“,”这里的逗号相当于XPath中的“|”号,功能是分割条件表达式,即将逗号两边的内容作为单独的表达式,输出符合所有表达式匹配模式的内容。

“+”\“~”右侧相邻元素,兄弟节点

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
read_html(myhtml,encoding="UTF-8")%>% html_nodes("li a[target]+span") %>% html_text()
[1] " 56次阅读/0条评论"read_html(myhtml,encoding="UTF-8")%>% html_nodes("li a[target]~span") %>% html_text()
[1] " 56次阅读/0条评论" " (2017-08-22)"     " "

以上两句函数功能类似,但是有细微区别,第一句“+”输出现有节点的右侧相邻节点,而“~”则是输出现有节点的所有兄弟节点(同辈节点)。

2、谓语表达:

通常我们提取内容要按照标签内属性名称或者属性值进行条件限定来提取,这时候我们需要在表达式中对标签节点进行条件限定。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<ul class="blog-sitemap">
         <li style="line-height: 28px;">
                       <a style="" target="_blank" href="/blog/datamofang/9027">精美炫酷数据分析地图——简单几步轻松学会</a>
                       <span style="margin-left: 21px;"> 235次阅读/0条评论</span>
                       <span style="margin-left: 5px;">  (2017-07-27)</span>
                       <span style="margin-left: 21px;"> </span>
         </li>
         <li style="line-height: 28px;">
                       <a style="" target="_blank" href="/blog/datamofang/9000">不用编程,教你轻松搞定数据地图</a>
                       <span style="margin-left: 21px;"> 178次阅读/0条评论</span>
                       <span style="margin-left: 5px;">  (2017-07-26)</span>
                       <span style="margin-left: 21px;"> </span>
         </li>
         <li style="line-height: 28px;">
                       <a style="" target="_blank" href="/blog/datamofang/8996">Excel 有哪些可能需要熟练掌握而很多人不会的技能?</a>
                       <span style="margin-left: 21px;"> 142次阅读/0条评论</span>
                       <span style="margin-left: 5px;">  (2017-07-26)</span>
                       <span style="margin-left: 21px;"> </span>
         </li>
         <li style="line-height: 28px;">
                       <a style="" target="_blank" href="/blog/datamofang/8763">一般人不知道的几个excel制图技巧 </a>
                       <span style="margin-left: 21px;"> 199次阅读 / 0条评论</span>
                       <span style="margin-left: 5px;">  (2017-07-05)</span>
                       <span style="margin-left: 21px;"> </span>
        </li>
        <li style="line-height: 28px;">
                       <a style="" target="_blank" href="/blog/datamofang/8654">那些培训师都不曾告诉你的关于Excel图表的秘密~</a>
                       <span style="margin-left: 21px;"> 424次阅读/0条评论</span>
                       <span style="margin-left: 5px;">  (2017-06-20)</span>
                       <span style="margin-left: 21px;"> </span>
        </li>
        <li style="line-height: 28px;">
                       <a style="" target="_blank" href="/blog/datamofang/8595">Excel依然是一款强大的数据可视化利器~</a>
                       <span style="margin-left: 21px;"> 671次阅读/3条评论</span>
                       <span style="margin-left: 5px;">  (2017-06-15)</span>
                       <span style="margin-left: 21px;"> </span>
        </li></ul>
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
read_html(myhtml,encoding="UTF-8")%>% html_nodes("li a[target]") %>% html_text()
[1] "\nbalabalabala\n离散颜色标度连续化的最佳方案\n"

以上语句限制了我们查找的对象是li内所有含target属性的节点(这里仅有一个)

2、元素限定:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
p[attr]                 #包含什么属性
p[attr="value"]         #target为blank的元素
p[href^="subtring"]     #选择所有href属性值以https开头的a元素
p[href$=".pdf"]         #选择所有href属性值以.pdf结尾的a元素

p[href*="w3schools"]    #选择所有href属性值包含w3schools的a元素
p[title~="flower"]      #包含关系
css=button.attr:contains("OK")  #:contains是个Pseudo-class,用冒号开头,括号里是内容。

元素限定可能是我们在css表达式中运用到频率仅次于特殊符号的功能元素了,因为通常解析的目标网页体系和内容都非常庞大,如果不加以限定的话,肯定会输出很多对我们没有任何价值的信息。

以上文中几篇评论文章为例,我们来讲解以上所有元素限定的用法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
read_html(myhtml,encoding="UTF-8")%>% html_nodes("li[style]") %>% html_text()
[1] "\n精美炫酷数据分析地图——简单几步轻松学会\n 235次阅读/0条评论\n  (2017-07-27)\n \n"          
[2] "\n不用编程,教你轻松搞定数据地图\n 178次阅读/0条评论\n  (2017-07-26)\n \n"                  
[3] "\nExcel 有哪些可能需要熟练掌握而很多人不会的技能?\n 142次阅读/0条评论\n  (2017-07-26)\n \n"
[4] "\n一般人不知道的几个excel制图技巧 \n 199次阅读 / 0条评论\n  (2017-07-05)\n \n"              
[5] "\n那些培训师都不曾告诉你的关于Excel图表的秘密~\n 424次阅读/0条评论\n  (2017-06-20)\n \n"    
[6] "\nExcel依然是一款强大的数据可视化利器~\n 671次阅读/3条评论\n  (2017-06-15)\n \n"

这里限定了li标签的属性为style,所有上述语句输出了ul内部所有li标签中含有style属性的对应节点并输出文本内容。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
read_html(myhtml,encoding="UTF-8")%>% html_nodes("li a[target='_blank']") %>% html_text()
[1] "精美炫酷数据分析地图——简单几步轻松学会"           "不用编程,教你轻松搞定数据地图"                  
[3] "Excel 有哪些可能需要熟练掌握而很多人不会的技能?" "一般人不知道的几个excel制图技巧 "                
[5] "那些培训师都不曾告诉你的关于Excel图表的秘密~"     "Excel依然是一款强大的数据可视化利器~"

这里我限定了li内所有属性值为’_blank’的a节点并输出其文本内容,输出了所有博客文章名称。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
read_html(myhtml,encoding="UTF-8")%>% html_nodes("li a[href^='/blog']") %>% html_text()
[1] "精美炫酷数据分析地图——简单几步轻松学会"           "不用编程,教你轻松搞定数据地图"                  
[3] "Excel 有哪些可能需要熟练掌握而很多人不会的技能?" "一般人不知道的几个excel制图技巧 "                
[5] "那些培训师都不曾告诉你的关于Excel图表的秘密~"     "Excel依然是一款强大的数据可视化利器~"

本次限定了li节点内所有含有href属性值以“/blog”开头的a节点并输出这些节点的文本。(因为所有a节点的href属性值都是以/blog开头的,所有输出了所有文章名称)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
read_html(myhtml,encoding="UTF-8")%>% html_nodes("li a[href$='54']") %>% html_text()
[1] "那些培训师都不曾告诉你的关于Excel图表的秘密~"

与上面那句类似,这里限定的是href属性值以54结尾的a节点,并输出其文本内容,仅有一个符合条件。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
read_html(myhtml,encoding="UTF-8")%>% html_nodes("li a[href*='datamofang']") %>% html_text()
[1] "精美炫酷数据分析地图——简单几步轻松学会"           "不用编程,教你轻松搞定数据地图"                  
[3] "Excel 有哪些可能需要熟练掌握而很多人不会的技能?" "一般人不知道的几个excel制图技巧 "                
[5] "那些培训师都不曾告诉你的关于Excel图表的秘密~"     "Excel依然是一款强大的数据可视化利器~"

这里的“*”代表包含关系,即限定了href属性值内容包含字符串“datamofang”的所有节点a并输出其文本对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
read_html(myhtml,encoding="UTF-8")%>% html_nodes("li a[href~='datamofang']") %>% html_text()
character(0)

以上代码中的“~”也是代表包含关系,但是这里的包含关系与上一条的包含关系有所不同,这里的“~”专门用于匹配属性值为句子(带有单词边界【一般为空格】),所有本案例情形无法匹配到。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mycontent<-"<div class='ba'><ul id='myid' class='myclass' target='raindu is the blog of lityduyu'>raindu's blog</ul></div>"
read_html(mycontent,encoding="UTF-8")%>% html_nodes("div.ba ul[target~='blog']") %>% html_text()
[1] "raindu's blog"
read_html(mycontent,encoding="UTF-8")%>% html_nodes("div.ba ul[target*='blog']") %>% html_text()
[1] "raindu's blog"

这里可以看到,“~”的适用范围仅限于匹配句子中有单词边界的目标单词,而“”因为指代的包含关系限制较少,所以其匹配范围更广,也就是说“”的匹配操作可以涵盖所有“~”适用的匹配情形,但是如果明确了你的匹配目标是有单词边界的句子的话,适用“~”匹配可以避免输出无效内容,更为精确。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
read_html(myhtml,encoding="UTF-8")%>% html_nodes("li a[href]:contains('Excel')") %>% html_text()
[1] "Excel 有哪些可能需要熟练掌握而很多人不会的技能?" "那些培训师都不曾告诉你的关于Excel图表的秘密~"    
[3] "Excel依然是一款强大的数据可视化利器~"

以上的contains是一个匹配函数,跟XPath中的匹配函数及其类似,但是这里限定的是节点文本内包含的字符串,之前的操作都是基于属性值包含关系,以上匹配输出了所有含有href属性的a节点中文本内容包含字符串“Excel”的目标节点的文本对象。

3、Pseudo Classes伪类:nth-child/nth-of-type

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
nth-child  
p:nth-child(2)             #选择作为第二个子元素的p元素
p:nth-child(2n)            #选择作为偶数个子元素的p元素
p:nth-last-child(2)        #选择作为倒数第二个p元素
p:first-child              #选择作为第一个元素的p元素       
p:last-child               #选择作为倒数第一个元素的p元素       
nth-of-type
p:nth-of-type(2)           #选择第二个p元素
p:nth-of-type(2)           #选择第偶数个p元素
p:nth-last-of-type(2)      #选择倒数第二个p元素
p:first-of-type            #选择作为第一个元素的p元素       
p:last-of-type              #选择作为倒数第一个元素的p元素

首先给大家解释Pseudo Classes伪类中nth-child/nth-of-type的区别,对于nth-child,你可以理解为限定第n个位置必须是p元素,而nth-of-type的限定条件较为宽松,仅限定第二出现的p元素,会自动忽略那些非p元素,而前者则将所有元素放在一起排列位置。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mycontent<-       
      '<li style="line-height: 28px;">
              <a style="" target="_blank" href="/blog/datamofang/8595">Excel依然是一款强大的数据可视化利器~</a>
              <span style="margin-left: 21px;"> 671次阅读/3条评论</span>
              <span style="margin-left: 5px;">  (2017-06-15)</span>
              <span style="margin-left: 21px;"> </span>
       </li>'
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
read_html(mycontent,encoding="UTF-8")%>% html_nodes("li span:nth-child(1)") %>% html_text()
character(0)
read_html(mycontent,encoding="UTF-8")%>% html_nodes("li span:nth-of-type(1)") %>% html_text()
[1] " 671次阅读/3条评论"

看吧,区别立马呈现出来,两者皆有其适用场景,前者更适合子节点全部为同一类型时,后者则适合子节点中混杂有不同类型的节点。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
read_html(mycontent,encoding="UTF-8")%>% html_nodes("li span:first-child") %>% html_text()
character(0)
read_html(mycontent,encoding="UTF-8")%>% html_nodes("li span:first-of-type") %>% html_text()
[1] " 671次阅读/3条评论"

所以以上两句的区别仍然是在于元素类型是否相同,因为li的子节点中第一个节点是a而非span,所以适用span:first-child限定了第一个节点必须是span,自然输出内容为空,而span:first-of-type则输出子节点中的第一个span,限定较少,完成了匹配。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
read_html(mycontent,encoding="UTF-8")%>% html_nodes("li span:last-child") %>% html_text()
[1] " "
read_html(mycontent,encoding="UTF-8")%>% html_nodes("li span:last-of-type") %>% html_text()
[1] " "

当使用last来匹配的时候,因为li内的后三个节点都是span节点,也就是last-child是有符合条件的,所以返回最后一个span内容,内容为空。同理,span:last-of-type也匹配出来了,内容也为空。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
read_html(mycontent,encoding="UTF-8")%>% html_nodes("li span:nth-child(2n)") %>% html_text()
[1] " 671次阅读/3条评论" " "     
read_html(mycontent,encoding="UTF-8")%>% html_nodes("li span:nth-of-type(2n)") %>% html_text()
[1] "  (2017-06-15)"

这里的区别更加显著,使用span:nth-child(2n)匹配的是li的第2个子节点,但是刚好符合span处于偶数位置的条件,所以匹配出了节点内容,而span:nth-of-type(2n)则匹配出了所有子节点中的span节点的偶数位置节点。因而二者匹配输出的内容是不同的。

综上所述,span:nth-child限定比较严格,nth-of-type限定较为宽松,所以实际使用时一定要分清适用场景。

Python版:

这里我使用Python的BeautifulSoup包的解析器重现以上内容。

1、特殊符号:

“.”/“#”(class属性与id属性)

“.”和“#”分别代表标签内class属性和id属性的连接符。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<div style="margin-top: 25px;" id="raindu" class="btn-group btn-group-justified blog-menu" role="group">
     <a href="/blog/datamofang"  class="btn btn-default">我的首页</a>
     <a href="/blog/datamofang/sitemap/"  class="btn btn-default">博客地图</a>
</div>
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
html='''
   <div style="margin-top: 25px;" id="raindu" class="btn-group btn-group-justified blog-menu" role="group">
     <a href="/blog/datamofang"  class="btn btn-default">我的首页</a>
     <a href="/blog/datamofang/sitemap/"  class="btn btn-default">博客地图</a>
</div>'''from bs4 import BeautifulSoup
soup = BeautifulSoup(html,"lxml")
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
text=[]
for mytext in soup.select("div.btn-group a"):
    text.append(mytext.get_text())
print(text)
['我的首页', '博客地图']
text=[]
for mytext in soup.select("div#raindu a"):
    text.append(mytext.get_text())
print(text)
['我的首页', '博客地图']

可以看到以上两句表达式都可以完美匹配出来div标签节点内部a节点内的文本,这里的定位主要是靠‘.’和’#’两个连接符实现的,这是相对比较规范的写法。

“>”和“ ”(右尖括号和空格)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
myhtml=\
'''
     <li style="line-height: 28px;">
     <a style="" target="_blank" href="/blog/datamofang/9485">
      <b>balabalabala</b>
     离散颜色标度连续化的最佳方案
     </a>
     <span style="margin-left: 21px;"> 56次阅读/0条评论</span>
     <span style="margin-left: 5px;"> (2017-08-22)</span>
     <span style="margin-left: 21px;"> </span>
</li>
'''
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#绝对路径:
soup = BeautifulSoup(myhtml,"lxml")
soup.select("li > a > b")[0].get_text()
'balabalabala'
#相对路径
soup.select("li a b")[0].get_text()
'balabalabala'
soup.select("li b")[0].get_text()
'balabalabala'

从以上三个输出可以很明确的发现,所有的输出结果都是一样的,第一句函数执行的功能是在文档中查找li节点的子节点a节点到的子节点b,并输出其文本内容;第二句函数执行的功能是查找文档中li节点中的所有节点为a(相对路径)的节点内所有节点为b的节点(相对路径),并输出其文本内容。第三句函数执行功能为在文档中查找所有li节点内的所有节点为b的节点并输出其内容。因为myhtml文档中只有一个b节点,所有三者输出的内容是一样的。

所以“>”和“ ”(右尖括号和空格)的区别非常明显,也非常重要,请慎用“>”(绝对路径),只有在有100%把握的时候再用,一般来说使用“ ”(空格:相对路径)的css表达式比较稳健,但是在同一个文档中同名节点较多的情况下,因为相对路径需要遍历的路径较多,耗时长、可能匹配出没有价值的内容,所以在实际使用时还是要随机应变。

“*”和“,”星号和单引号:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
text=[]for mytext in soup.select("li [style]"):
    text.append(mytext.get_text())
print(text)
['\nbalabalabala\n     离散颜色标度连续化的最佳方案\n     ', ' 56次阅读/0条评论', ' (2017-08-22)', ' ']         " "         
text=[]
for mytext in soup.select("[style]"):
    text.append(mytext.get_text())
print(text)
['\nbalabalabala\n     离散颜色标度连续化的最佳方案\n     ', ' 56次阅读/0条评论', ' (2017-08-22)', ' ', '\n\nbalabalabala\n     离散颜色标度连续化的最佳方案\n     \n 56次阅读/0条评论\n (2017-08-22)\n \n', '\nbalabalabala\n     离散颜色标度连续化的最佳方案\n     ', ' 56次阅读/0条评论', ' (2017-08-22)', ' ']

以上第一句执行的功能是在li节点中查询所有包含style属性的节点,并输出对应节点文本内容,第二个匹配的范围更大一些,匹配了文档中所有包含style属性的节点并输出对应节点文本内容。可以看到li这个顶层节点内的所有文本被拼接在一起作为li的文本对象被输出了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
soup.select("li a[target]")[0].get_text()
'\nbalabalabala\n     离散颜色标度连续化的最佳方案\n     '
text=[]
for mytext in soup.select("li span"):
    text.append(mytext.get_text())
print(text)
[' 56次阅读/0条评论', ' (2017-08-22)', ' ']

text=[]
for mytext in soup.select("li a[target],li span"):
    text.append(mytext.get_text())
print(text)
['\nbalabalabala\n     离散颜色标度连续化的最佳方案\n     ', ' 56次阅读/0条评论', ' (2017-08-22)', ' ']                                " "

“,”这里的逗号相当于XPath中的“|”号,功能是分割条件表达式,即将逗号两边的内容作为单独的表达式,输出符合所有表达式匹配模式的内容。

2、谓语表达:

通常我们提取内容要按照标签内属性名称或者属性值进行条件限定来提取,这时候我们需要在表达式中对标签节点进行身份限定。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<ul class="blog-sitemap">
         <li style="line-height: 28px;">
                       <a style="" target="_blank" href="/blog/datamofang/9027">精美炫酷数据分析地图——简单几步轻松学会</a>
                       <span style="margin-left: 21px;"> 235次阅读/0条评论</span>
                       <span style="margin-left: 5px;">  (2017-07-27)</span>
                       <span style="margin-left: 21px;"> </span>
         </li>
         <li style="line-height: 28px;">
                       <a style="" target="_blank" href="/blog/datamofang/9000">不用编程,教你轻松搞定数据地图</a>
                       <span style="margin-left: 21px;"> 178次阅读/0条评论</span>
                       <span style="margin-left: 5px;">  (2017-07-26)</span>
                       <span style="margin-left: 21px;"> </span>
         </li>
         <li style="line-height: 28px;">
                       <a style="" target="_blank" href="/blog/datamofang/8996">Excel 有哪些可能需要熟练掌握而很多人不会的技能?</a>
                       <span style="margin-left: 21px;"> 142次阅读/0条评论</span>
                       <span style="margin-left: 5px;">  (2017-07-26)</span>
                       <span style="margin-left: 21px;"> </span>
         </li>
         <li style="line-height: 28px;">
                       <a style="" target="_blank" href="/blog/datamofang/8763">一般人不知道的几个excel制图技巧 </a>
                       <span style="margin-left: 21px;"> 199次阅读 / 0条评论</span>
                       <span style="margin-left: 5px;">  (2017-07-05)</span>
                       <span style="margin-left: 21px;"> </span>
        </li>
        <li style="line-height: 28px;">
                       <a style="" target="_blank" href="/blog/datamofang/8654">那些培训师都不曾告诉你的关于Excel图表的秘密~</a>
                       <span style="margin-left: 21px;"> 424次阅读/0条评论</span>
                       <span style="margin-left: 5px;">  (2017-06-20)</span>
                       <span style="margin-left: 21px;"> </span>
        </li>
        <li style="line-height: 28px;">
                       <a style="" target="_blank" href="/blog/datamofang/8595">Excel依然是一款强大的数据可视化利器~</a>
                       <span style="margin-left: 21px;"> 671次阅读/3条评论</span>
                       <span style="margin-left: 5px;">  (2017-06-15)</span>
                       <span style="margin-left: 21px;"> </span>
        </li>
</ul>
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
soup = BeautifulSoup(myhtml,"lxml")
soup.select("li a[target]")[0].get_text()
'精美炫酷数据分析地图——简单几步轻松学会'

以上语句限制了我们查找的对象是li内所有含target属性的节点(这里仅有一个)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
###元素限定:
p[attr]                 #包含什么属性
p[attr="value"]         #target为blank的元素
p[href^="subtring"]     #选择所有href属性值以https开头的a元素
p[href$=".pdf"]         #选择所有href属性值以.pdf结尾的a元素

p[href*="w3schools"]    #选择所有href属性值包含w3schools的a元素
p[title~="flower"]      #包含关系
css=button.attr:contains("OK")  #:contains是个Pseudo-class,用冒号开头,括号里是内容。

元素限定可能是我们在css表达式中运用到频率仅次于特殊符号的功能元素了,因为通常解析的目标网页体系和内容都非常庞大,如果不加以限定的话,肯定会输出很多对我们没有任何用处的内容信息。

以上文中几篇评论文章为例,我们来讲解以上所有元素限定的用法:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
text=[]
for mytext in soup.select("li[style]"):
    text.append(mytext.get_text())
print(text)
['\n精美炫酷数据分析地图——简单几步轻松学会\n 235次阅读/0条评论\n  (2017-07-27)\n \n', '\n不用编程,教你轻松搞定数据地图\n 178次阅读/0条评论\n  (2017-07-26)\n \n', '\nExcel 有哪些可能需要熟练掌握而很多人不会的技能?\n 142次阅读/0条评论\n  (2017-07-26)\n \n', '\n一般人不知道的几个excel制图技巧 \n 199次阅读 / 0条评论\n  (2017-07-05)\n \n', '\n那些培训师都不曾告诉你的关于Excel图表的秘密~\n 424次阅读/0条评论\n  (2017-06-20)\n \n', '\nExcel依然是一款强大的数据可视化利器~\n 671次阅读/3条评论\n  (2017-06-15)\n \n']

这里我们限定了li标签的属性为style,所有上述语句输出了ul内部所有li标签中含有style属性的节点对应并输出其文本内容。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
text=[]
for mytext in soup.select("li a[target='_blank']"):
    text.append(mytext.get_text())
print(text)

[‘精美炫酷数据分析地图——简单几步轻松学会’, ‘不用编程,教你轻松搞定数据地图’, ‘Excel 有哪些可能需要熟练掌握而很多人不会的技能?’, ‘一般人不知道的几个excel制图技巧 ‘, ‘那些培训师都不曾告诉你的关于Excel图表的秘密~’, ‘Excel依然是一款强大的数据可视化利器~’]

这里我限定了li内所有属性值为’_blank’的a节点并输出其文本内容,输出了所有博客文章名称。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
text=[]
for mytext in soup.select("li a[href^='/blog']"):
    text.append(mytext.get_text())
print(text)
['精美炫酷数据分析地图——简单几步轻松学会', '不用编程,教你轻松搞定数据地图', 'Excel 有哪些可能需要熟练掌握而很多人不会的技能?', '一般人不知道的几个excel制图技巧 ', '那些培训师都不曾告诉你的关于Excel图表的秘密~', 'Excel依然是一款强大的数据可视化利器~']

本次限定了li节点内所有含有href属性值以“/blog”开头的a节点并输出这些节点的文本内容。(因为所有a节点的href属性值都是以/blog开头的,所有输出了所有文章名称)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
text=[]
for mytext in soup.select("li a[href$='54']"):
    text.append(mytext.get_text())
print(text)
['那些培训师都不曾告诉你的关于Excel图表的秘密~']

与上面那句类似,这里限定的是href属性值以54结尾的a节点,并输出其文本内容,仅有一个符合条件。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
text=[]
for mytext in soup.select("li a[href*='datamofang']"):
    text.append(mytext.get_text())
print(text)
['精美炫酷数据分析地图——简单几步轻松学会', '不用编程,教你轻松搞定数据地图', 'Excel 有哪些可能需要熟练掌握而很多人不会的技能?', '一般人不知道的几个excel制图技巧 ', '那些培训师都不曾告诉你的关于Excel图表的秘密~', 'Excel依然是一款强大的数据可视化利器~']

这里的“*”代表包含关系,即限定了href属性值内容包含字符串“datamofang”的所有节点a并输出其文本对象。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
text=[]
for mytext in soup.select("li a[href~='datamofang']"):
    text.append(mytext.get_text())
print(text)
[]

以上代码中的“~”也是代表包含关系,但是这里的包含关系与上一条的包含关系有所不同,这里的“~”专门用于匹配属性值为句子(带有单词边界【一般为空格】),所有本案例情形无法匹配到。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
mycontent="<div class='ba'><ul id='myid' class='myclass' target='raindu is the blog of lityduyu'>raindu's blog</ul></div>"
soup = BeautifulSoup(mycontent,"lxml")
soup.select("div.ba ul[target~='blog']")[0].get_text()
"raindu's blog"
soup.select("div.ba ul[target*='blog']")[0].get_text()
"raindu's blog"

这里可以看到,“~”的适用范围仅限于匹配句子中有单词边界的目标单词,而“”因为指代的包含关系限制较少,所以其匹配范围更广,也就是说“”的匹配操作可以涵盖所有“~”适用的匹配情形,但是如果明确了你的匹配目标是有单词边界的句子的话,适用“~”匹配可以避免输出无效内容,更为精确。

3、Pseudo Classes伪类:nth-child/nth-of-type

发现BeautifuSoup暂时还不支持css路径表达式中的Pseudo Classes伪类伪类,不过BeautifuSoup中可选的解析器有很多,这一点儿并不会对网页解析造成太大困扰,即便是适用以上这些已经支持的CSS表达式同样可以完成大部分解析工作。

最后使用BeautifuSoup的css解析工具完成博客文章信息的解析工作。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from bs4 import BeautifulSoup
url="https://ask.hellobi.com/blog/datamofang/sitemap/"
import requests
header={"User-Agent":"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36"}
response = requests.get(url,headers=header)
soup = BeautifulSoup(response.text,"lxml")
#文章名称:
myclass=[]
for i in soup.select("ul li a[href*='datamofang']"):
    myclass.append(i.get_text(strip=True))
print(myclass)
#文章链接:
Pagelinks=[]
for i in soup.select("ul li a[href*='datamofang']"):
    Pagelinks.append("https://ask.hellobi.com"+i.get('href'))
print(Pagelinks)
#文章阅读量、阅读日期:
Pageviews=[];Pagedate=[]
for i in soup.select("ul li[style]"):
    Pageviews.append(i.find_all("span")[0].get_text(strip=True))
    Pagedate.append(i.find_all("span")[1].get_text(strip=True))
print(Pageviews,Pagedate)
import pandas as pd
mycontent=pd.DataFrame({"myclass":myclass,"Pagelinks":Pagelinks,"Pageviews":Pageviews,"Pagedate":Pagedate})
print(mycontent)

本文参考文献: https://www.w3schools.com/cssref/trysel.asp http://tutorials.jenkov.com/css/selectors.html#universal-selector http://www.zhangxinxu.com/wordpress/2011/06/css3%E9%80%89%E6%8B%A9%E5%99%A8nth-child%E5%92%8Cnth-of-type%E4%B9%8B%E9%97%B4%E7%9A%84%E5%B7%AE%E5%BC%82/ http://lxml.de/xpathxslt.html http://cuiqingcai.com/1319.html

往期案例数据请移步本人GitHub: https://github.com/ljtyduyu/DataWarehouse/tree/master/File

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-10-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据小魔方 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
左手用R右手Python——CSS网页解析实战
之前我陆陆续续写了几篇介绍在网页抓取中CSS和XPath解析工具的用法,以及实战应用,今天这一篇作为系列的一个小结,主要分享使用R语言中Rvest工具和Python中的requests库结合css表达
数据小磨坊
2018/04/11
1.1K0
左手用R右手Python——CSS网页解析实战
左手用R右手Python系列——任务进度管理
一直觉得运行代码的时候,如果有一个提示任务运行进度的进度条提示就好,很多时候我们的程序运行时间普遍较长,如果程序运行没有任何提示,那简直是一场噩梦,根本不知道到底是程序在偷懒还是真的卡住了,而如果再代码里写print函数,循环较多的话,你的屏幕会被打印的提示文本瞬间刷屏。 后来经过搜索,还真让我发现了解决方法。今天给大家介绍两个包,这两个包可以做任务任务处理、进程处理工作,编写一些简易的交互界面。 这里仅介绍简单的用法,仅仅满足我们日常任务进度提示即可。 library("tcltk") library("
数据小磨坊
2018/04/11
1.2K0
左手用R右手Python系列——任务进度管理
R 爬虫|手把手带你爬取 800 条文献信息
今天学习了一些关于 R 爬虫的知识,后续会陆续写一些笔记,当然对于爬虫有更好的一些工具来进行爬取数据,作为入门小白,我自己先从 R 语言尝试开始吧。
庄闪闪
2021/08/20
6.3K0
R 爬虫|手把手带你爬取 800 条文献信息
左手用R右手Python系列之——表格数据抓取之道
在抓取数据时,很大一部分需求是抓取网页上的关系型表格。 对于表格而言,R语言和Python中都封装了表格抓取的快捷函数,R语言中XML包中的readHTMLTables函数封装了提取HTML内嵌表格的功能,rvest包的read_table()函数也可以提供快捷表格提取需求。Python中read_html同样提供直接从HTML中抽取关系表格的功能。 HTML语法中内嵌表格有两类,一类是table,这种是通常意义上所说的表格,另一类是list,这种可以理解为列表,但从浏览器渲染后的网页来看,很难区分这两种,
数据小磨坊
2018/04/11
3.4K0
左手用R右手Python系列之——表格数据抓取之道
小技巧,把Markdown文本发布到微信公众号文章
估计很多人都是这样,平常工作在github,等到有成果要发布,又要写微信公众号。 github用Markdown,微信公众号,至少截止今天,还是沿用富文本的方式。不是说富文本不好,但每次精心撰写的内容,重新排一遍版,还真是怪烦的。 如果在github是使用jekyll相对会容易一点,在页面上拷贝、到微信粘贴一下,大多内容都会差不多。否则就只好转换成html显示在浏览器,然后再拷贝粘贴。 Markdown转换成html大多人都会,有很多所见即所得的工具软件,比如Marked2,不过多数都是收费的。其
俺踏月色而来
2018/06/20
1.5K0
京东购物车(动态)网页搭建,利用JavaScript实现
源代码已分享至本人云盘~~~ 链接:https://pan.baidu.com/s/1ul_DbL2KVdETTc6himvfUA 提取码:7wbw
时间静止不是简史
2020/07/24
1.9K0
京东购物车(动态)网页搭建,利用JavaScript实现
手把手 | 教你爬下100部电影数据:R语言网页爬取入门指南
大数据文摘作品,转载要求见文末 编译 | 姚佳灵,蒋晔,杨捷 前言 网页上的数据和信息正在呈指数级增长。如今我们都使用谷歌作为知识的首要来源——无论是寻找对某地的评论还是了解新的术语。所有这些信息都已经可以从网上轻而易举地获得。 网络中可用数据的增多为数据科学家开辟了可能性的新天地。我非常相信网页爬取是任何一个数据科学家的必备技能。在如今的世界里,我们所需的数据都在互联网上,使用它们唯一受限的是我们对数据的获取能力。有了本文的帮助,您定会克服这个困难。 网上大多数的可用数据并不容易获取。它们以非结构化的形
大数据文摘
2018/05/21
1.7K0
前端学习笔记之CSS过渡模块
阅读目录 一 伪类选择器复习 二 过渡模块的基本使用 三 控制过渡的速度transition-timing-function 四 过渡模块连写 一 伪类选择器复习 注意点: #1 a标签的伪类选择器可以单独出现,也可以一起出现 #2 a标签的伪类选择器如果一起出现,有严格的顺序要求,否则失效 编写的顺序必须要严格遵循: l v h a a:link{ color: skyblue; } a:visited {
Jetpropelledsnake21
2019/02/15
4230
python︱HTML网页解析BeautifulSoup学习笔记
一、载入html页面信息 一种是网站在线的网页、一种是下载下来的静态网页。 1、在线网页 参考《python用BeautifulSoup库简单爬虫入门+案例(爬取妹子图)》中的载入内容: import
悟乙己
2018/01/02
3.3K0
同时用R语言和Python爬取知乎美图
学习Python已有两月有余,是时候检验下学习效果了,之前练习了不少R语言数据爬取,Python的爬虫模块还没有来得及认真入门,乱拼乱凑就匆忙的开始了,今天就尝试着使用R+Python来进行图片爬取,
数据小磨坊
2018/04/11
1.2K0
同时用R语言和Python爬取知乎美图
前端基础-CSS梅兰练习
四、梅兰练习 引入字体图标css和当前文件的css <link rel="stylesheet" type="text/css" href="font/iconfont.css"> <link rel="stylesheet" type="text/css" href="css/index.css"> 顶部代码: <!-- 顶部开始 --> <div class="top container"> <div class="content clearfix"> <div
cwl_java
2020/04/07
4680
前端基础-CSS梅兰练习
求一个网页设计作业——个人博客(HTML+CSS)
✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 🥇 关于作者: 💬历任研发工程师,技术组长,教学总监;曾于2016年、2020年两度荣获CSDN年度十大博客之星。 十载寒冰,难凉热血;多年过去,历经变迁,物是人非。 然而,对于技术的探索和追求从未停歇。 💪坚持原创,热衷分享,初心未改,继往开来! 一、👨‍🎓网站题目 🏀校园篮球网页设计、⚽足球体育运动、🤽体育游泳运动、🏓兵乓球 、🎾网球、等网站的设计与制作。 二、✍️网站描述 🏷️ 大学生校园运动静态HTML网页设计作品,采用DIV CSS
IT司马青衫
2022/08/21
4010
求一个网页设计作业——个人博客(HTML+CSS)
京东网页(动态)搭建,利用jquery实现
源代码已分享至本人云盘~~~ 链接:https://pan.baidu.com/s/1Nr5l2Smcmaevs5HHDh5y_A 提取码:blif
时间静止不是简史
2020/07/24
3.5K0
京东网页(动态)搭建,利用jquery实现
R语言爬虫教程与实例操作:如何爬取基金与Pubmed网站信息
这个教程是一棵树zj(https://github.com/yikeshu0611)
Chris生命科学小站
2023/02/28
1.5K0
R语言爬虫教程与实例操作:如何爬取基金与Pubmed网站信息
Html5响应式设计实现九宫格
自从响应式设计的理念提出以来,越来越大的网站采用这种思想。各类大型网站也如雨后春笋般的涌了出来。如:小米商城,天猫等。 至于响应式设计的概念等大家可以去百度百度,我这里就不相信讲解了。直接为大家带来源码,用Html5实现响应式的九宫格。代码如下:
业余草
2019/01/21
2.5K0
Html5响应式设计实现九宫格
京东购物车网页(静态)搭建
二、项目环境 1、软件环境:Win10+HBuilder+Chrome浏览器 2、项目思路 : 根据需要实现的效果,自上而下,依次实现导航栏、搜索框、标题栏、商品详情展示框、结算窗口的编写。
时间静止不是简史
2020/07/24
2K0
京东购物车网页(静态)搭建
HTML+CSS美食静态网页设计
1、首先,新建一个文件,名字可以随便取,我这里的文件名叫:爱尚美食网页。在 爱尚美食网页 文件夹里面还需要有一个 css文件夹,一个images文件夹,和一个index.html文件。如下图所示:
全栈程序员站长
2022/11/09
3.2K0
HTML+CSS美食静态网页设计
蓝色时间轴个人博客网页模板代码
看雪时间轴个人博客模板,女生唯美简洁个人博客静态页面模板,蓝色时间轴个人网页模板,下雪空间个人模板代码.这是一个有关看雪时间轴的css3+html5网站静态的个人博客网页模板
博客趣
2024/01/15
3040
蓝色时间轴个人博客网页模板代码
卧槽, R 语言也能爬取网页的数据!
爬虫技术是一种从网页中获 取数据的方式,是按照一定规则,自动地抓取网页数据的程序或者脚本。除了Python可以写爬虫程序外,R语言一样可以实现爬虫功能
Python研究者
2022/04/08
6.3K0
卧槽, R 语言也能爬取网页的数据!
html css制作静态网页_简单的静态网页代码
网页简介:经过pink老师的课程学习之后,制作了一个简单的静态页面,主要是运用html和css。
全栈程序员站长
2022/10/04
9.7K0
html css制作静态网页_简单的静态网页代码
推荐阅读
相关推荐
左手用R右手Python——CSS网页解析实战
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档