正则 bs4 xpath的数据请求方式和获取数据方式

(一)BeautifulSoup

从本心来说,我更喜欢用BeautifulSoup。因为它更符合直观语义特性,find()和find_all()函数已经基本上足够提取出任何的信息,对于身份证号、QQ号等特征特别明显的数据,顶多再加上一个正则表达式就完全OK了。

Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。

Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。你不需要考虑编码方式,除非文档没有指定一个编码方式,这时,Beautiful Soup就不能自动识别编码方式了。然后,你仅仅需要说明一下原始编码方式就可以了。Beautiful Soup已成为和lxml、html6lib一样出色的python解释器,为用户灵活地提供不同的解析策略或强劲的速度。

pip install beautifulsoup4                 #pip方式安装bs4

pip install lxml                           #pip方式安装 lxml 解析工具

具体介绍参见:

BeautifulSoup 的结构和用法简介

静下心来看完这篇博文才了解到原来BeautifulSoup有这么多的用法,特别是find_all()和find(),下面是一些实例:

#实例

import requests

from bs4 import BeautifulSoup

headers = {'User-Agent': "

Mozilla/5.0 (Windows NT 6.1; WOW64; rv:61.0) Gecko/20100101 Firefox/61.0

"}

html = requests.get(url, headers=headers)

soup = BeautifulSoup(html.text, 'lxml')  #以上是网络获取html

soup=BeautifulSoup(open('index.html')) # 读取本地的html,加个open函数即可

print(soup.prettify())  # 用标准html 显示方法打印html

#soup.find_all()方法介绍 ,soup.find()与之基本类似,只是返回的是第一个值

find_all( name , attrs , recursive , text , **kwargs )

soup.find_all('b')  #查找所有的b标签,返回列表

soup.find_all(re.compile("^b")) # 正则表达式

soup.find_all(["a", "b"])  #传入列表参数,找到所有的a标签和b标签

soup.find_all(id='link2')  #传入id是link2的参数,Beautiful Soup会搜索每个tag的”id”属性

soup.find_all(href=re.compile("elsie")) #传入正则表达式,查找所有的href标签内容中含有 elsie 的内容

soup.find_all(href=re.compile("elsie"), id='link1') # 多层过滤,除了href进行限定之外,对id标签的内容也做了限定

soup.find_all("div", class_="sister") #最常用的查找技巧,这里之所以加‘_=’是因为‘class’不仅是html中的tag,也是python语法的关键词,其他的不用加下划线

data_soup.find_all(attrs={"data-foo": "value"}) # 针对html5里面的data- 进行的专项查找

soup.find_all(text="Elsie") # 对text内容进行查找

soup.find_all(text=["Tillie", "Elsie", "Lacie"]) # 列表形式进行查找,与上面name类似

soup.find_all(text=re.compile("Dormouse")) # 正则表达式形式,与上面类似

soup.find_all("a", limit=2) # 找到前两个a标签, limit用来限定次数(

还有一个select()函数比较有用,基本用法如下:

# 我们在写 CSS 时,标签名不加任何修饰,类名前加点,id名前加 #,在这里我们也可以利用类似的方法来筛选元素,用到的方法是soup.select(),返回类型是list

(1)通过标签名查找

soup.select('title')

(2)通过类名查找

soup.select('.sister')

(3)通过 id 名查找

soup.select('#link1')

(4)组合查找

组合查找即和写 class 文件时,标签名与类名、id名进行的组合原理是一样的,例如查找 p 标签中,id 等于 link1的内容,二者需要用空格分开

soup.select('p #link1')

(5)属性查找

查找时还可以加入属性元素,属性需要用中括号括起来,注意属性和标签属于同一节点,所以中间不能加空格,否则会无法匹配到。

soup.select('a[class="sister"]')

soup.select('a[href="http://example.com/elsie"]')

get_text()方法可以用来获取内容,请看下面代码:

soup = BeautifulSoup(html.text, 'lxml')

print (type(soup.select('title')))

print (soup.select('title')[0].get_text())  # 获取第一个title标签的对应内容

for title in soup.select('title'):

           print (title.get_text()) # 获取列表中的title对应内容

好了,BeautifulSoup的用法基本介绍到这里,除了速度上比较鸡肋之外,BeautifulSoup的查找方法做到了堪称人性化,给人以非常直观的语义理解。

 

(二)Xpath的介绍和用法

 

XPath 是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。结构关系包括 父、子、兄弟、先辈、后代等。

这里主要参考这篇博文:

Xpath 语法和lxml的用法简介

表达式                         功能描述

nodename       选取此节点的所有子节点。

/                        从根节点选取。

//                       从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。

.                        选取当前节点。

..                      选取当前节点的父节点。

@                     选取属性。

实例

在下面的表格中,我们已列出了一些路径表达式以及表达式的结果:

路径表达式                                                     结果

bookstore                            选取 bookstore 元素的所有子节点。

/bookstore                           选取根元素 bookstore。注释:假如路径起始于正斜杠( / ),则此路径始                                                终代表到某元素的绝对路径!

bookstore/book                  选取属于 bookstore 的子元素的所有 book 元素。

//book                                  选取所有 book 子元素,而不管它们在文档中的位置。

bookstore//book                选择属于 bookstore 元素的后代的所有 book 元素,而不管它们位于                                                      bookstore 之下的什么位置。

//@lang                               选取名为 lang 的所有属性

谓语(Predicates)

谓语用来查找某个特定的节点或者包含某个指定的值的节点。谓语被嵌在 方括号 中。

实例

在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:

路径表达式                                                                        结  果

/bookstore/book[1]                       选取属于 bookstore 子元素的第一个 book 元素。

/bookstore/book[last()]                 选取属于 bookstore 子元素的最后一个 book 元素。

/bookstore/book[last()-1]             选取属于 bookstore 子元素的倒数第二个 book 元素。

/bookstore/book[position()<3]     选取最前面的两个属于 bookstore 元素的子元素的 book 元素。

//title[@lang]                                  选取所有拥有名为 lang 的属性的 title 元素。

//title[@lang=’eng’]                        选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。

/bookstore/book[price>35.00]     选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值                                                           须大于 35.00。

/bookstore/book[price>35.00]/title     选取 bookstore 元素中的 book 元素的所有 title 元素,且其中                                                                   的 price 元素的值须大于 35.00。

路径表达式                                    结果

 

/bookstore/*                   选取 bookstore 元素的所有子元素。

//*                                    选取文档中的所有元素。

//title[@*]                        选取所有带有属性的 title 元素。

选取若干路径

通过在路径表达式中使用“|”运算符,您可以选取若干个路径。

实例

在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式                                                                        结果

//book/title | //book/price                        选取 book 元素的所有 title 和 price 元素。

//title | //price                                            选取文档中的所有 title 和 price 元素。

/bookstore/book/title | //price                 选取属于 bookstore 元素的 book 元素的所有 title 元素,以及                                                                  文档中所有的 price 元素。

用法简介:

# beautiful   漂亮的
# soup 汤
# beautifulsoup  是 python的第三方库

from  bs4 import  BeautifulSoup
# beautfuisoup 里面需要两个参数一个open方法一个为固定方法
# 1.想要解析的数据
# 2.设置编码格式

bs=BeautifulSoup(open('index.html',encoding='utf-8'),'lxml')
print(bs)
print(type(bs))
#获取网页中的tital标签
print(bs.title)
#获取head标签即head标签内部的所有其他标签
print(bs.head)
#获取网页当中的第一标签
print(bs.a)
# 总结:ba.xx
# 获取所有xx中的第一标签以及第一个xx里面的内容

# document 文档、#获取当前 内容的标签名 bs 为一个整体而你不是一个具体
print(bs.name)

print(bs.title.name)

# attribute 属性
# 如果没有做出特别的处理, bs.xx永远获取的是所有xx的第一属性
print(bs.html.attr)
#keyError只能从自己有的属性中找
print(bs.a['id'])
print(bs.a['href'])
# 类名和id不一样
# id必须是唯一的一个标签只能由一个id
# class不是唯一的,不同标签可以有同一个class
# 同一个标签也可以拥有多个class
print(bs.a['class'])
print(bs.html['lang'])
#delete
del bs.a['id']
print(bs.a)
#获取指定标签的文本内容
print(bs.a.string)
#string获取的文本指的是本标签的文本
#不包含子标签的文本
print(bs.div.string)
#遍历------
# contents能够获取指定标签下的uoyouneir

print(bs.head.contents)
print(bs.body.contents)
#获取所有内容当中指定索引的内容


# print(bs.div.content[3])
# parent 父级
#获取指定标签的父级标签和父签内部的所有标签
head=bs.title.parent
print(head)
res=bs.find_all('a')
print(res)
for value in res:
    print(value)
print(bs.find(id='jd'))
# id 是唯一的 通过地来找 只能找到一个 所以用find
#class不是唯一的 通过class来找 可能找到duoge
print(bs.find_all(class_ ='shopping'))

#找到所有符和条件当中的第一个
print(bs.find(class_='shopping'))
print(bs.find_all(id='id'))

# select
print(bs.select('title'))

print(bs.select('a'))
#.表示类名 #表示id
#   #first
print(bs.select('.first'))
print(bs.select('#jd'))
#找到一个类名为now的div标签的
print(bs.select('div.now'))

 

(三)正则表达式

最让人头疼的正则表达式

普通字符

普通字符包括没有显式指定为元字符的所有可打印和不可打印字符。这包括所有大写和小写字母、所有数字、所有标点符号和一些其他符号。

非打印字符

非打印字符也可以是正则表达式的组成部分。下表列出了表示非打印字符的转义序列:

字符                                               描  述

\cx         匹配由x指明的控制字符。例如, \cM 匹配一个 Control-M 或回车符。x 的值必须为 A-Z 或                 a-z 之一。否则,将 c 视为一个原义的 'c' 字符。

\f            匹配一个换页符。等价于 \x0c 和 \cL。

\n           匹配一个换行符。等价于 \x0a 和 \cJ。

\r            匹配一个回车符。等价于 \x0d 和 \cM。

\s            匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。

\S            匹配任何非空白字符。等价于 [^ \f\n\r\t\v]。

\t              匹配一个制表符。等价于 \x09 和 \cI。

\v             匹配一个垂直制表符。等价于 \x0b 和 \cK。

 

特殊字符

 

所谓特殊字符,就是一些有特殊含义的字符。若要匹配这些特殊字符,必须首先使字符"转义",即,将反斜杠字符\放在它们前面。下表列出了正则表达式中的特殊字符:

特别字符描述

$                      匹配输入字符串的结尾位置。如果设置了 RegExp 对象的 Multiline 属性,则 $ 也匹配 '\n' 或 '\r'。要匹配 $ 字符本身,请使用 \$。

( )                     标记一个子表达式的开始和结束位置。子表达式可以获取供以后使用。要匹配这些字符,请使用 和和。

*                        匹配前面的子表达式零次或多次。要匹配 * 字符,请使用 \*。

+                       匹配前面的子表达式一次或多次。要匹配 + 字符,请使用 \+。

.                         匹配除换行符 \n 之外的任何单字符。要匹配 . ,请使用 \. 。

[                         标记一个中括号表达式的开始。要匹配 [,请使用 \[。

?                        匹配前面的子表达式零次或一次,或指明一个非贪婪限定符。要匹配 ? 字符,请使                           用 \?。

\                          将下一个字符标记为或特殊字符、或原义字符、或向后引用、或八进制转义符。例                            如, 'n' 匹配字符 'n'。'\n' 匹配换行符。序列 '\\' 匹配 "\",而 '\(' 则匹配 "("。

^                          匹配输入字符串的开始位置,除非在方括号表达式中使用,此时它表示不接受该字                            符集合。要匹配 ^ 字符本身,请使用 \^。

{                          标记限定符表达式的开始。要匹配 {,请使用 \{。

|                           指明两项之间的一个选择。要匹配 |,请使用 \|。

限定符

限定符用来指定正则表达式的一个给定组件必须要出现多少次才能满足匹配。有*或+或?或{n}或{n,}或{n,m}共6种。

正则表达式的限定符有:

字符                                                                          描述

*                        匹配前面的子表达式零次或多次。例如,zo* 能匹配 "z" 以及 "zoo"。* 等价于{0,}。

+                       匹配前面的子表达式一次或多次。例如,'zo+' 能匹配 "zo" 以及 "zoo",但不能匹配 "z"。+ 等价于 {1,}。

?                        匹配前面的子表达式零次或一次。例如,"do(es)?" 可以匹配 "do" 或 "does" 中的"do" 。? 等价于 {0,1}。

{n}                      n 是一个非负整数。匹配确定的 n 次。例如,'o{2}' 不能匹配 "Bob" 中的 'o',但是能匹配 "food" 中的两个 o。

{n,}                     n 是一个非负整数。至少匹配n 次。例如,'o{2,}' 不能匹配 "Bob" 中的 'o',但能匹配 "foooood" 中的所有 o。'o{1,}' 等价于 'o+'。'o{0,}' 则等价于 'o*'。

{n,m}                  m 和 n 均为非负整数,其中n <= m。最少匹配 n 次且最多匹配 m 次。例                 如,"o{1,3}" 将匹配 "fooooood" 中的前三个 o。'o{0,1}' 等价于 'o?'。请注意在逗号和两个数之间不能有空格。

定位符

定位符使您能够将正则表达式固定到行首或行尾。它们还使您能够创建这样的正则表达式,这些正则表达式出现在一个单词内、在一个单词的开头或者一个单词的结尾。

定位符用来描述字符串或单词的边界,^和$分别指字符串的开始与结束,span class="marked">\b 描述单词的前或后边界,span class="marked">\B 表示非单词边界。

正则表达式的限定符有:

字符描述

^                         匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与                              \n 或 \r 之后的位置匹配。

$                        匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与                              \n 或 \r 之前的位置匹配。

\b                        匹配一个字边界,即字与空格间的位置。

\B                        非字边界匹配。

反义字符

有时需要查找不属于某个能简单定义的字符类的字符。比如想查找除了数字以外,其它任意字符都行的情况,这时需要用到反义:

常用的反义代码

代码/语法                           说明

\W                        匹配任意不是字母,数字,下划线,汉字的字符

\S                         匹配任意不是空白符的字符

\D                         匹配任意非数字的字符

\B                          匹配不是单词开头或结束的位置

[^x]                        匹配除了x以外的任意字符

[^aeiou]                匹配除了aeiou这几个字母以外的任意字符

例子:\S+匹配不包含空白符的字符串。

]+>匹配用尖括号括起来的以a开头的字符串。

举个例子,学习一下:

from  urllib.request import Request ,urlopen


full_url = 'https://www.qiushibaike.com/hot/page/'

headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36'
}

 

 request = Request(full_url,headers = headers)
    response = urlopen(request)
    # 获取对应网页的全部内容
    code = response.read().decode()
    # 正则匹配的内容 从指定的开始的位置 到全部内容结束
    # 所以只需要指定开始的位置 不需要指定结束的位置

    # 如果我们想要正则获取某一对标签里面的内容的时候
    # 那么需要将这对标签对写完整 而且在想要获取的内容
    # 上添加()例如:<h2>(.*?)</h2>
    pattern = re.compile(r'<div class="author clearfix">.*?<h2>(.*?)</h2>.*?<div class="articleGender.*?Icon">(.*?)</div>',re.S)
    result = pattern.findall(code)
    print(result)

(四)总结

通过写这篇博文,对于html网页数据的三种查找方法进行了重温和了解,其中正则表达式耗时最多,总是出现这样那样的问题,当解决之后,还是比较有成就感的。xpath也并不太难用,介于soup和正则表达式之间吧,甚至某些时候比soup还要方便一些。

(1)在Xpath查找中,//代表所有 /代表子节点,  如果越级查找的话,要用// ,如果没有越级的话,/比较合适

(2)正则表达式中,可以分开写每个要提取的信息,最后再合在一起,要不然太长了。如果非要写在一起,建议每一个(.*?)占据一行,做好标注,要不出错了很难排错的。

猜你喜欢

转载自blog.csdn.net/sklsklsklsxy/article/details/81346485