python数据解析

67 阅读11分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

数据解析篇

简介:使用引擎解析网页里面得数据,根据规则提取入库

一、正则

开源测试工具 tool.oschina.net/regex/

官网:docs.python.org/zh-cn/3/lib…

.匹配除 "\n" 之外的任何单个字符。要匹配包括 '\n' 在内的任何字符,请使用象 '[.\n]' 的模式。
\d匹配一个数字字符。等价于 [0-9]。
\D匹配一个非数字字符。等价于 0-9。
\s匹配任何空白字符,包括空格、制表符、换页符等等。等价于 [ \f\n\r\t\v]。
\S匹配任何非空白字符。等价于 \f\n\r\t\v。
\w匹配包括下划线的任何单词字符。等价于'[A-Za-z0-9_]'。
\W匹配任何非单词字符。等价于 'A-Za-z0-9_'。

1.match

用法: 提取关键参数 比如 token sign 后端返回得签名信息 算法

  • match 方法会尝试从字符串的起始位置匹配正则表达式,如果匹配,就返回匹配成功的结果;如果不匹配,就返回 None
import re
​
content = 'Hello 123 456 welcome to tuling'
print(len(content))
result = re.match('^Hello\s\d\d\d\s\d{3}\s\w{7}', content)
print(result)
print(result.group())
print(result.span())
  • group() 返回被 正则 匹配的字符串
  • start() 返回匹配开始的位置
  • span() 返回一个元组包含匹配 (开始,结束) 的位置
1、匹配数字
import re
​
content = 'Hello 123456 welcome to tuling'
result = re.match('^Hello\s(\d+)\swelcome', content)
print(result)
print(result.group(1))
print(result.span())
2、通用匹配
import re
​
content = 'Hello 123 456 welcome to tuling'
# 匹配所有数据
result = re.match('^Hello.*ng$', content)
# 匹配某某开始到某某结束
result = re.match('^Hello(.*)ng$', content).group(1)
print(result)
print(result.group())
print(result.span())
3、贪婪和非贪婪
  • python默认贪婪模式
  • 在"*","?","+","{m,n}"后面加上?,使贪婪变成非贪婪
import re
​
content = 'http://feier.com/yyds'
result1 = re.match('http.*?com/(.*?)', content)
result2 = re.match('http.*?com/(.*)', content)
print('result1', result1.group())
print('result2', result2.group())
4、修饰符
re.I使匹配对大小写不敏感
re.L做本地化识别(locale-aware)匹配
re.M多行匹配,影响 ^ 和 $
re.S使 . 匹配包括换行在内的所有字符
re.U根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.
re.X该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。
import re
# 这个修饰符的作用是匹配包括换行符在内的所有字符。
content = '''Hello 1234567 World_This
is a Regex Demo
'''
result = re.match('^He.*?(\d+).*?Demo$', content)
result = re.match('^He.*?(\d+).*?Demo$', content,re.S)
print(result.group())

2、search

用法: 提取数据相关

匹配时会扫描整个字符串,然后返回第一个成功匹配的结果,如果搜索完了还没有找到,就返回 None。

案例文本

html = '''<div id="songs-list">
<h2 class="title">经典老歌</h2>
<p class="introduction">
经典老歌列表
</p>
<ul id="list" class="list-group">
<li data-view="2">一路上有你</li>
<li data-view="7">
<a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
</li>
<li data-view="4" class="active">
<a href="/3.mp3" singer="齐秦">往事随风</a>
</li>
<li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
<li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
<li data-view="5">
<a href="/6.mp3" singer="邓丽君">但愿人长久</a>
</li>
</ul>
</div>'''
1、匹配所有
results = re.findall('<li.*?href="(.*?)".*?singer="(.*?)">(.*?)</a>', html, re.S)
print(results)  
print(type(results))  
for result in results:  
    print(result)  
    print(result[0], result[1], result[2])

练习:提取字符串里面的数字

str1 = 'asdbsd12312312eqw'

答案

re.search('bsd(\d+)eq', str1).group(1)
2、正则提取案例

地址:s.taobao.com/

import urllib.request
headers = {
    'cookie': 'miid=120076508994945398; cna=s5XgGHTA/QACAa8N+wOHxkKB; thw=cn; t=09ef0b533d5f5014504435330c5b23a7; _m_h5_tk=9afb8815f59ecdd6d90f960fa950b608_1632318660385; _m_h5_tk_enc=32d88fe37367c7d8191e0d6ccaa6aeb8; xlly_s=1; _samesite_flag_=true; cookie2=11352226bbcc90e131fb8e2d9a3a78a2; _tb_token_=ed63bd661b9e0; sgcookie=E1002hzxj0%2BkNyK8SW%2BdTaNG6Ou62zkRwlh9oIg6TSxlgEVkBKN0WwQH%2Fw9SRRgzrUuiBsCz5ZwM5v0Z0kwmK1MF3NBMh3Yh3q5FfwrTVvf7M0s%3D; unb=3071675414; uc3=nk2=AQc0Nbnx9S4H8pkl%2BA%3D%3D&vt3=F8dCujdzW2tWEWdg7bI%3D&id2=UNDTw7IM5DYpuA%3D%3D&lg2=WqG3DMC9VAQiUQ%3D%3D; csg=bc1d7fa4; lgc=bcptbtptp%5Cu5F20%5Cu5F20; cancelledSubSites=empty; cookie17=UNDTw7IM5DYpuA%3D%3D; dnk=bcptbtptp%5Cu5F20%5Cu5F20; skt=d666d4e9a1995a28; existShop=MTYzMjMwOTgxNQ%3D%3D; uc4=id4=0%40UgcjZF9065lxnqGgFvjXZ5vSp20%2B&nk4=0%40A6qJ%2FkooLkypmChVx5EvcDW8EO%2BkDz4V; tracknick=bcptbtptp%5Cu5F20%5Cu5F20; _cc_=VT5L2FSpdA%3D%3D; _l_g_=Ug%3D%3D; sg=%E5%BC%A043; _nk_=bcptbtptp%5Cu5F20%5Cu5F20; cookie1=BxZoM4%2FT%2BmdjW5MEqR9Mt5craH93rw995UJ3Ud496K4%3D; enc=zdYu03KQWYVyBA9LCueMaJsN0HdEWldS4oFNif9IGQEQaT%2B3o%2Bpb4Mv8qOADxIw325G1hjBZ8c6veP2pQaHigw%3D%3D; hng=CN%7Czh-CN%7CCNY%7C156; mt=ci=109_1; uc1=cookie16=VFC%2FuZ9az08KUQ56dCrZDlbNdA%3D%3D&cookie21=U%2BGCWk%2F7p4mBoUyS4E9C&cookie14=Uoe3dYITfaJ8EQ%3D%3D&existShop=false&cookie15=UtASsssmOIJ0bQ%3D%3D&pas=0; JSESSIONID=A10BE1A97F1454FFCFEE08EC1DA77AD4; tfstk=cmMcB3i71jPXWIPoOKwjV8YaxfORZiBa-AkSUvVE8Z3Q65kPiGfPThuHqr0mB11..; l=eBNHRWw4gR5fSGh9BOfwourza77OSIRA_uPzaNbMiOCPOzfp5yfRW6FgCAT9C3GVh6k6R35NsM4TBeYBqS24n5U62j-la_kmn; isg=BNDQjj_8hRiU-VkcaLeqLGneoR4imbTjIXyEsMqhnCv-BXCvcqmEcya33c3l0my7',
    'referer': 'https://s.taobao.com/search?q=%E4%B9%90%E9%AB%98%E6%95%B0%E6%8D%AE&imgfile=&js=1&stats_click=search_radio_all%3A1&initiative_id=staobaoz_20210922&ie=utf8',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.63 Safari/537.36'
}
url = 'https://s.taobao.com/search?q=%E7%A7%AF%E6%9C%A8&imgfile=&commend=all&ssid=s5-e&search_type=item&sourceId=tb.index&spm=a21bo.21814703.201856-taobao-item.1&ie=utf8&initiative_id=tbindexz_20170306&p4ppushleft=2%2C48&s=44'
req = urllib.request.Request(url,headers=headers)
res = urllib.request.urlopen(req)
response = (res.read().decode('utf-8'))
regex = re.compile('<script>\s*g_page_config = (.*?);\s*g_srp_loadCss')
lst_res = regex.findall(response)
print(lst_res)

4、匹配中文

[\u4e00-\u9fa5]
s = '大家晚上好asdasdsad'
aa = re.findall('[\u4e00-\u9fa5]+',s)

5、练习案例

提取单前网站所有的图片地址

参考答案

from pyquery import PyQuery as pq
import requests
res = requests.get('https://pic.netbian.com/4kmeinv/index.html')
res.encoding = 'gbk'
qq = pq(res.text)
qqq = qq('div.slist')
pattern = '<img[^>]*>'
result1 = re.findall(pattern, str(qqq))
for i in result1:
    print(re.search('src="(.*?)"',i).group(1))

二、 Pyquery

环境安装

pip install pyquery==1.4.3
版本:pyquery==1.4.3

利用它,我们可以直接解析 DOM 节点的结构,并通过 DOM 节点的一些属性快速进行内容提取。

html = '''
<div id="cont">
    <ul class="slist">
         <li class="item-0">web开发</li>
         <li class="item-1"><a href="link2.html">爬虫开发</a></li>
         <li class="item-0 active"><a href="link3.html"><span class="bold">数据分析</span></a></li>
         <li class="item-1 active"><a href="link4.html">深度学习</a></li>
         <li class="item-0"><a href="link5.html">机器学习</a></li>
     </ul>
 </div>
'''

1、实例演示

from pyquery import PyQuery as pq
doc = pq(html)
print(doc('li'))

2、解析网页

from pyquery import PyQuery as pq
doc = pq(url='https://www.python.org/')
print(doc('title'))  # 根据标记提取

3、css选择器

doc = pq(html)
print(doc('#cont .slist li'))
print(type(doc('#cont .slist li')))

4、提取内容

for item in doc('#cont .slist li').items():
    print(item.text())

5、子节点

from pyquery import PyQuery as pq
doc = pq(html)
items = doc('.slist')
print(type(items))
print(items)       # 提取节点所有内容
lis = items.find('li')    # 获取符合条件的li标签
print(type(lis))
print(lis)

5.1 子节点

lis = items.children()
print(type(lis))
print(lis)

6、父节点

co = items.parent()
print(type(co))
print(co)

7、兄弟节点

前面我们说明了子节点和父节点的用法,还有一种节点叫作兄弟节点。如果要获取兄弟节点,可以使用 siblings 方法。

from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.slist .item-0.active')
# print(li.siblings())
print(li.siblings('.active'))

8、 属性获取

from pyquery import PyQuery as pq
doc = pq(html)
a = doc('.item-0.active a')
print(a, type(a))
print(a.attr('href'))
  • 遍历提取

    doc = pq(html)
    a = doc('a')
    for s in a.items():
        print(s.attr('href'))  # 属性获取
        print(s.text())   #  值获取
        
    

9、节点操作

对节点进行动态修改,比如为某个节点添加一个 class,移除某个节点等,这些操作有时会为提取信息带来极大的便利。

addClass 和 removeClass

from pyquery import PyQuery as pq
doc = pq(html)
li = doc('.item-0.active')
print(li)
li.removeClass('active')
print(li)
li.addClass('active')

10、伪类选择器

from pyquery import PyQuery as pq
doc = pq(html)
li = doc('li:nth-child(2)')    # 第二个节点
li = doc('li:nth-child(2n)')   # 偶数位节点
li = doc('li:last-child')      # 最后一个节点
print(li)

三、 xpath

插件下载:chrome.zzzmh.cn/index

参考:blog.csdn.net/m0_43432638…

XPath 的选择功能十分强大,它提供了非常简洁明了的路径选择表达式。另外,它还提供了超过 100 个内建函数,用于字符串、数值、时间的匹配以及节点、序列的处理等。几乎所有我们想要定位的节点,都可以用 XPath 来选择。

官网:www.w3.org/TR/xpath/

安装解析引擎:

 pip install  lxml

表 3-1 XPath 常用规则

表 达 式描  述
nodename选取此节点的所有子节点
/从当前节点选取直接子节点
//从当前节点选取子孙节点
.选取当前节点
..选取当前节点的父节点
@选取属性

案例演示

text = '''
<div>
    <ul>
         <li class="item-0"><a href="link1.html">first item</a></li>
         <li class="item-1"><a href="link2.html">second item</a></li>
         <li class="item-inactive"><a href="link3.html">third item</a></li>
         <li class="item-1"><a href="link4.html">fourth item</a></li>
         <li class="item-0"><a href="link5.html">fifth item</a>
     </ul>
 </div>
'''

1,解析

from lxml import etree

html = etree.HTML(text)
result = etree.tostring(html)
print(result.decode('utf-8'))

2、节点操作

我们一般会用 // 开头的 XPath 规则来选取所有符合要求的节点。这里以前面的 HTML 文本为例,如果要选取所有节点,可以这样实现:

result = html.xpath('//*')

# 这里使用 * 代表匹配所有节点,也就是整个 HTML 文本中的所有节点都会被获取。可以看到,返回形式是一个列表,每个元素是 Element 类型,其后跟了节点的名称,如 htmlbodydivullia 等,所有节点都包含在列表中了。

3、子节点

result = html.xpath('//li/a')

result = html.xpath('//li/a/text()')  # 提取数据

result = html.xpath('//li/a/@href')   # 属性值

4、指定节点获取

result = html.xpath('//li[@class="item-0"]/a/text()')  
print(result)

# ['first item', 'fifth item']

5 、节点轴选择

XPath 提供了很多节点轴选择方法,包括获取子元素、兄弟元素、父元素、祖先元素等,示例如下:

html = etree.HTML(text)
result = html.xpath('//li[1]/ancestor::*')  # 选取当前节点的所有先辈
print(result)
result = html.xpath('//li[1]/attribute::*')   # 选取当前节点的所有属性
print(result)
result = html.xpath('//li[1]/child::a[@href="link1.html"]')  # 选取当前节点的所有子元素

6、找翻页元素

#  最后一个
//span[contains(@class,'s-pagination-strip')]/span[last()]
#  提取下一页     
//span[contains(@class,'s-pagination-strip')]/*[last()]
#  下一页
//span[@class="s-pagination-strip"]/a[text()="下一页"]

7、案例演示

  • 说明:提取当前网站的首页标题信息,要求使用xpath解析器

四、 Beautiful Soup

参考文章:blog.csdn.net/weixin_5574…

简单来说,BeautifulSoup 就是 Python 的一个 HTML 或 XML 的解析库,我们可以用它来方便地从网页中提取数据,官方的解释如下:

BeautifulSoup 提供一些简单的、Python 式的函数用来处理导航、搜索、修改分析树等功能。它是一个工具箱,通过解析文档为用户提供需要抓取的数据,因为简单,所以不需要多少代码就可以写出一个完整的应用程序。 BeautifulSoup 自动将输入文档转换为 Unicode 编码,输出文档转换为 utf-8 编码。你不需要考虑编码方式,除非文档没有指定一个编码方式,这时你仅仅需要说明一下原始编码方式就可以了。 BeautifulSoup 已成为和 lxml、html5lib 一样出色的 Python 解释器,为用户灵活地提供不同的解析策略或强劲的速度。

表 4-1 Beautiful Soup 支持的解析器

解析器使用方法优势劣势
Python 标准库BeautifulSoup(markup, "html.parser")Python 的内置标准库、执行速度适中 、文档容错能力强Python 2.7.3 or 3.2.2) 前的版本中文容错能力差
LXML HTML 解析器BeautifulSoup(markup, "lxml")速度快、文档容错能力强需要安装 C 语言库
LXML XML 解析器BeautifulSoup(markup, "xml")速度快、唯一支持 XML 的解析器需要安装 C 语言库
html5libBeautifulSoup(markup, "html5lib")最好的容错性、以浏览器的方式解析文档、生成 HTML5 格式的文档速度慢、不依赖外部扩展

通过以上对比可以看出,lxml 解析器有解析 HTML 和 XML 的功能,而且速度快,容错能力强,所以推荐

4.0 安装

pip install beautifulsoup4   # bs4

4.1 测试code

from bs4 import BeautifulSoup
soup = BeautifulSoup('<p>Hello world</p>', 'lxml')
print(soup.p.string)

4.2 节点选择器

直接调用节点的名称就可以选择节点元素,再调用 string 属性就可以得到节点内的文本了,这种选择方式速度非常快。如果单个节点结构层次非常清晰,可以选用这种方式来解析。

下面再用一个例子详细说明选择元素的方法:

html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.title)
print(type(soup.title))
print(soup.title.string)
print(soup.head)
print(soup.p)

4.2.1 获取属性

每个节点可能有多个属性,比如 id 和 class 等,选择这个节点元素后,可以调用 attrs 获取所有属性:

print(soup.p.attrs)
print(soup.p.attrs['name'])

4.2.2 嵌套选择

html = """
<html><head><title>The Dormouse's story</title></head>
<body>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.head.title)
print(type(soup.head.title))
print(soup.head.title.string)

4.2.3 关联选择

在做选择的时候,有时候不能做到一步就选到想要的节点元素,需要先选中某一个节点元素,然后以它为基准再选择它的子节点、父节点、兄弟节点等,这里就来介绍如何选择这些节点元素

html = """
<html>
    <head>
        <title>The Dormouse's story</title>
    </head>
    <body>
        <p class="story">
            Once upon a time there were three little sisters; and their names were
            <a href="http://example.com/elsie" class="sister" id="link1">
                <span>Elsie</span>
            </a>
            <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> 
            and
            <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>
            and they lived at the bottom of a well.
        </p>
        <p class="story">...</p>
"""
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.p.contents)

4.2.4 select(根据选择器选取指定内容)

标签选择器(a),类选择器(.dudu),id选择器(#lala),组合选择器(a, .dudu, #lala, .meme),层级选择器(div.dudu#lala.meme.xixi 表示下面好多级和 div>p>a>.lala 只能是下面一级 ),伪类选择器(不常用),属性选择器 (input[name=‘lala’])

样例
htmls = """
<html>
    <head>
        <title>The Dormouse's story</title>
    </head>
    <body>
        <p class="story">
            Once upon a time there were three little sisters; and their names were
            <a href="http://example.com/elsie" class="sister" id="link1" title="xl">
                <span>Elsie</span>
            </a>
            <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> 
            and
            <a href="http://example.com/tillie" class="sister" id="link3" rel="noopener noreferrer ">Tillie</a>
            and they lived at the bottom of a well.
        </p>
        <p class="story">...</p>
"""
1.2案例一(层级选择器,返回的都是列表)
soup.select('.story > a > span')[0].text
1.3案例二(id选择器)
print(soup.select('#link1'))
1.3案例三(提取属性)
soup.select('#link1')[0].attrs['href']
1.4 案例四 (属性选择器)
print(soup.select("input[type='password']"))
1.3案例四(提取实际数据)

url地址: movie.douban.com/chart

ht = open('s.html',encoding='utf-8').read()
soup1 = BeautifulSoup(ht, 'lxml')
text = soup1.select('.recmd-content')
for i in text:
    print(i.text)