数据解析之正则与BS4

1.数据解析

1.数据解析就是应用一定的技术手段在响应数据中获取目标数据
2.常用数据解析方式:
    正则: 匹配高效, 但正则表达式书写有难度
    BS4: 解析数据速度慢, 但使用简单
    xpath: 解析速度快, 使用简单
3.环境配置:
(1).正则: pip install re
(2).BS4:
    pip install lxml
    pip install BeautifulSoup4
(3).xpath:
    pip install lxml

  


2.正则(复习)

# 1.元字符匹配
    .     匹配任意字符,除了换行符
​
    []    用来表示一组字符,单独列出:[abc] 匹配 'a','b'或'c'
​
    [^...]  匹配除了字符组中字符的所有字符
​
    \d    匹配任意数字,等价于 [0-9].
​
    \D    匹配任意非数字
​
    \w    匹配字母数字及下划线
​
    \W    匹配非字母数字及下划线
​
    \s    匹配任意空白字符,等价于 [\t\n\r\f].
​
    \S    匹配任意非空字符

# 2.字符组: 要求在一个位置匹配的字符可能出现很多种情况, 各种情况组成一个组
    [0123456789]: 匹配0到9任意字符
    [0-9]: 同上
    [a-z]: 匹配a到z的任意小写字母
    [A-Z]: 匹配A到Z的任意大写字母
    [0-9a-fA-F]: 以上三种的组合, 匹配0-9任意数组或a到f之间任意字母, 不区分大小写
    自定义字符组:[a3h5]  --->  代表匹配a, 3, h, 5等字符

# 量词:
    *       重复零次或更多次
    +       重复一次或更多次
    ?       重复零次或一次
    {n}     重复n次
    {n,}    重复n次或更多次
    {n,m}   重复n到m次

# 边界修饰符
    ^       匹配开始
    $       匹配结尾

# 分组
    在正则表达式中添加(), 就形成了一个分组, 在re模块中优先匹配显示分组内容
    import re
    s = "<a href='www.baidu.com'>正则匹配实验</a>"
    res = re.findall("href='(.*)'>", s)
    print(res)

# 匹配模式
    re.S  单行模式
    re.M  多行模式  
    re.I 忽略大小写

# 贪婪匹配与非贪婪匹配
​
    贪婪匹配是指: 在使用量词:  * ,  +  等时, 尽可能多的匹配内容
​
    非贪婪匹配是指: 使用?对正则表达式进行修饰, 使量词的匹配尽可能少, 如+代表匹配1次或多次, 在?的修饰下, 只匹配1次.

# re模块
1.re.findall('正则表达式', '待匹配字符串'): 返回所有满足匹配条件的结果, 以列表形式返回
2.re.search('正则表达式', '带匹配字符串'): 匹配到第一个就返回一个对象, 该对象使用group()进行取值, 如果为匹配到则返回None
3.re.match('正则表达式', '待匹配字符串'): 只从字符串开始进行匹配, 如果匹配成功返回一个对象,同样使用group()进行取值, 匹配不成功返回None
4.re.compile('正则表达式'): 将正则表达式编译为对象, 但需要按该正则表达式匹配是可以在直接使用该对象调用以上方法即可.

  


# 示例:    
s = "abcabc你好"# findall方法演示
res_findall = re.findall('a', s)
print('findall匹配结果:', res_findall)
​
# search方法演示, 不确定是否能匹配出结果, 不可直接使用group进行取值, 需要判断或进行异常处理
res_search = re.search("", s)
if res:
    print('search匹配结果', res.group())
else:
    print('None')
    
# match方法演示:
res_match_1 = re.match('abc', s)
res_match_2 = re.match('bc', s)
print('res_match_1结果:', res_match_1)
print('res_match_2结果:', res_match_2)
​
# compile方法演示:
re_obj = re.compile('ab')
res = re_obj.findall(s)
print(res)

# 利用正则表达式抓取校花网图片
import re
import requests
​
# 请求url, 抓取页面
url = 'http://www.xiaohuar.com/hua/'
headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
}
res = requests.get(url=url, headers=headers)
html = res.text
​
# 利用正则匹配页面中的img标签, 获取其src属性值
all_src = re.findall('<img.*src="(.*jpg)"', html, re.M)
​
# 遍历获取到的图片连接, 处理连接为完整的url, 再次请求抓取图片二进制流数据, 写入文件
for num, url in enumerate(all_src):
    url = 'http://www.xiaohuar.com' + url
    image = requests.get(url=url, headers=headers)
    with open('%s.jpg' % num, 'wb') as f:
        f.write(image.content)

 

 

3.BS4详解

# BS4解析库解析数据原理:
定位标签节点  --->  提取标签节点内容或属性值
​
# BS4基本使用步骤:
from BS4 import BeautifulSoup
...
soup = BeautifulSoup(text, 'lxml')  # 网络文件实例化对象
soup = BeautifulSoup(open('filename', 'lxml')
tag_element_or_attribute = soup.选择器

# BS4可用选择器:
 (1).节点选择器:
        - 节点名单选: soup.div
        - 嵌套选择: soup.div.span
        - 关联选择: 
            - 子节点:contents返回字节子节点, 包含换行符, 返回类型是一个列表
                   children效果相同, 但返回一个生成器
            - 子孙节点:descendants, 返回生成器遍历取值
                   1).深度选择, 从第一个子节点开始, 直至第一个子节点内的所有孙节点全部选择到
                   2).如果两个标签在两行上, 匹配第一个节点的所有深度后选择换行符.
                   3).如果标签中的文本是换行的, 则不单独匹配换行符, 换行符包含在文本内
            - 父节点:parent
            - 祖先节点:parents
            - 兄弟节点:
                    next_sibling: 当前节点的下一个兄弟节点
                    previous_sibling: 当前节点的上一个兄弟节点
                        
                        
(2).方法选择器:
        1).find_all(name, attrs, recursive, text, **kwargs)
            - 标签名选择:soup.find_all(name='ul')   # 选择所有ul标签
            - 嵌套选择:
                for ul  in soup.find_all(name='ul'):   # 循环ul列表, 选择每个ul中的li标签
                    ul.find_all(name='li')
            - 属性值选择:soup.find_all(attrs={'class': 'element'})   # 根据属性选择节点, 等效:soup.find_all(class='element')
            - 文本正则选择:soup.find_all(text=re.compile('link'))  # 返回所有标签中包含link字符的文字的对象
        2).find():返回一个对象
            
(3).css选择器:css选择器需要调用select方法, 改方法返回一个列表
        1).选择:
                soup.select('ul li')  # 选择ul下的li所有标签
                soup.select('.panel')  # 选择class的值为panel的标签
                soup.select('#item1')  # 选择id为item1的标签
         2).嵌套选择:
                for ul in soup.select('ul'):
                    ul.select('li')
          3).获取文本与属性:
                获取属性:
                    for li in soup.select('ul li'):
                        print(li.attrs['id'])
                        print(li['id'])   # 上下两种形式效果一致
                获取文本:
                    for li in soup.select('ul li'):
                        print(li.get_text())
                        print(li.string)

  

# 节点文本或属性的获取
    tag_element.name  # 获取节点名称
    tag_element.attrs  # 获取节点所有属性, 结果为字典形式
    tag_element.attrs['name']  # 获取节点的单个属性值, 等效: tag_element['name'], 属性多值是返回列表
    tag_element.string   # 获取节点的文本内容
如果使用关联选择, 且结果为生成器可以先转为列表再索引定位元素后在调用上面的获取元素方法, 如:
    list(p.parents)[0].attrs['name']

  


 

posted @ 2020-09-09 11:35  高登汗  阅读(506)  评论(0编辑  收藏  举报