数据解析篇

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

一、正则

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

官网:https://docs.python.org/zh-cn/3/library/re.html

day2-数据解析 - 图1

. 匹配除 “\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
  1. import re
  2. content = 'Hello 123 456 welcome to tuling'
  3. print(len(content))
  4. result = re.match('^Hello\s\d\d\d\s\d{3}\s\w{7}', content)
  5. print(result)
  6. print(result.group())
  7. print(result.span())
  • group() 返回被 正则 匹配的字符串

  • start() 返回匹配开始的位置

  • span() 返回一个元组包含匹配 (开始,结束) 的位置

1、匹配数字
  1. import re
  2. content = 'Hello 123456 welcome to tuling'
  3. result = re.match('^Hello\s(\d+)\swelcome', content)
  4. print(result)
  5. print(result.group(1))
  6. print(result.span())

2、通用匹配
  1. import re
  2. content = 'Hello 123 456 welcome to tuling'
  3. # 匹配所有数据
  4. result = re.match('^Hello.*ng$', content)
  5. # 匹配某某开始到某某结束
  6. result = re.match('^Hello(.*)ng$', content).group(1)
  7. print(result)
  8. print(result.group())
  9. print(result.span())

3、贪婪和非贪婪
  • python默认贪婪模式
  • 在”*”,”?”,”+”,”{m,n}”后面加上?,使贪婪变成非贪婪
  1. import re
  2. content = 'http://feier.com/yyds'
  3. result1 = re.match('http.*?com/(.*?)', content)
  4. result2 = re.match('http.*?com/(.*)', content)
  5. print('result1', result1.group())
  6. print('result2', result2.group())

4、修饰符
re.I 使匹配对大小写不敏感
re.L 做本地化识别(locale-aware)匹配
re.M 多行匹配,影响 ^ 和 $
re.S 使 . 匹配包括换行在内的所有字符
re.U 根据Unicode字符集解析字符。这个标志影响 \w, \W, \b, \B.
re.X 该标志通过给予你更灵活的格式以便你将正则表达式写得更易于理解。
  1. import re
  2. # 这个修饰符的作用是匹配包括换行符在内的所有字符。
  3. content = '''Hello 1234567 World_This
  4. is a Regex Demo
  5. '''
  6. result = re.match('^He.*?(\d+).*?Demo$', content)
  7. result = re.match('^He.*?(\d+).*?Demo$', content,re.S)
  8. print(result.group())

2、search

用法: 提取数据相关

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

案例文本

  1. html = '''<div id="songs-list">
  2. <h2 class="title">经典老歌</h2>
  3. <p class="introduction">
  4. 经典老歌列表
  5. </p>
  6. <ul id="list" class="list-group">
  7. <li data-view="2">一路上有你</li>
  8. <li data-view="7">
  9. <a href="/2.mp3" singer="任贤齐">沧海一声笑</a>
  10. </li>
  11. <li data-view="4" class="active">
  12. <a href="/3.mp3" singer="齐秦">往事随风</a>
  13. </li>
  14. <li data-view="6"><a href="/4.mp3" singer="beyond">光辉岁月</a></li>
  15. <li data-view="5"><a href="/5.mp3" singer="陈慧琳">记事本</a></li>
  16. <li data-view="5">
  17. <a href="/6.mp3" singer="邓丽君">但愿人长久</a>
  18. </li>
  19. </ul>
  20. </div>'''

1、匹配所有
  1. results = re.findall('<li.*?href="(.*?)".*?singer="(.*?)">(.*?)</a>', html, re.S)
  2. print(results)
  3. print(type(results))
  4. for result in results:
  5. print(result)
  6. print(result[0], result[1], result[2])

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

  1. str1 = 'asdbsd12312312eqw'

答案

  1. re.search('bsd(\d+)eq', str1).group(1)

2、正则提取案例

地址:https://s.taobao.com/

  1. import urllib.request
  2. headers = {
  3. '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',
  4. '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',
  5. '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'
  6. }
  7. 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'
  8. req = urllib.request.Request(url,headers=headers)
  9. res = urllib.request.urlopen(req)
  10. response = (res.read().decode('utf-8'))
  11. regex = re.compile('<script>\s*g_page_config = (.*?);\s*g_srp_loadCss')
  12. lst_res = regex.findall(response)
  13. print(lst_res)

4、匹配中文

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

5、练习案例

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

参考答案

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

二、 Pyquery

环境安装

  1. pip install pyquery==1.4.3
  2. 版本:pyquery==1.4.3

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

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

1、实例演示

  1. from pyquery import PyQuery as pq
  2. doc = pq(html)
  3. print(doc('li'))

2、解析网页

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

3、css选择器

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

4、提取内容

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

5、子节点

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

5.1 子节点

  1. lis = items.children()
  2. print(type(lis))
  3. print(lis)

6、父节点

  1. co = items.parent()
  2. print(type(co))
  3. print(co)

7、兄弟节点

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

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

8、 属性获取

  1. from pyquery import PyQuery as pq
  2. doc = pq(html)
  3. a = doc('.item-0.active a')
  4. print(a, type(a))
  5. print(a.attr('href'))
  • 遍历提取
    1. doc = pq(html)
    2. a = doc('a')
    3. for s in a.items():
    4. print(s.attr('href')) # 属性获取
    5. print(s.text()) # 值获取

9、节点操作

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

addClass 和 removeClass

  1. from pyquery import PyQuery as pq
  2. doc = pq(html)
  3. li = doc('.item-0.active')
  4. print(li)
  5. li.removeClass('active')
  6. print(li)
  7. li.addClass('active')

10、伪类选择器

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

三、 xpath

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

参考:https://blog.csdn.net/m0_43432638/article/details/90319198

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

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

安装解析引擎:

  1. pip install lxml

表 3-1 XPath 常用规则

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

案例演示

  1. text = '''
  2. <div>
  3. <ul>
  4. <li class="item-0"><a href="link1.html">first item</a></li>
  5. <li class="item-1"><a href="link2.html">second item</a></li>
  6. <li class="item-inactive"><a href="link3.html">third item</a></li>
  7. <li class="item-1"><a href="link4.html">fourth item</a></li>
  8. <li class="item-0"><a href="link5.html">fifth item</a>
  9. </ul>
  10. </div>
  11. '''

1,解析

  1. from lxml import etree
  2. html = etree.HTML(text)
  3. result = etree.tostring(html)
  4. print(result.decode('utf-8'))

2、节点操作

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

  1. result = html.xpath('//*')
  2. # 这里使用 * 代表匹配所有节点,也就是整个 HTML 文本中的所有节点都会被获取。可以看到,返回形式是一个列表,每个元素是 Element 类型,其后跟了节点的名称,如 html、body、div、ul、li、a 等,所有节点都包含在列表中了。

3、子节点

  1. result = html.xpath('//li/a')
  2. result = html.xpath('//li/a/text()') # 提取数据
  3. result = html.xpath('//li/a/@href') # 属性值

4、指定节点获取

  1. result = html.xpath('//li[@class="item-0"]/a/text()')
  2. print(result)
  3. # ['first item', 'fifth item']

5 、节点轴选择

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

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

6、找翻页元素

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

7、案例演示

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

四、 Beautiful Soup

参考文章:https://blog.csdn.net/weixin_55742843/article/details/116535390

简单来说,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 语言库
html5lib BeautifulSoup(markup, “html5lib”) 最好的容错性、以浏览器的方式解析文档、生成 HTML5 格式的文档 速度慢、不依赖外部扩展

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

4.0 安装

  1. pip install beautifulsoup4 # bs4

4.1 测试code

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

4.2 节点选择器

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

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

  1. html = """
  2. <html><head><title>The Dormouse's story</title></head>
  3. <body>
  4. <p class="title" name="dromouse"><b>The Dormouse's story</b></p>
  5. <p class="story">Once upon a time there were three little sisters; and their names were
  6. <a href="http://example.com/elsie" class="sister" id="link1"><!-- Elsie --></a>,
  7. <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
  8. <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
  9. and they lived at the bottom of a well.</p>
  10. <p class="story">...</p>
  11. """
  12. from bs4 import BeautifulSoup
  13. soup = BeautifulSoup(html, 'lxml')
  14. print(soup.title)
  15. print(type(soup.title))
  16. print(soup.title.string)
  17. print(soup.head)
  18. print(soup.p)

4.2.1 获取属性

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

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

4.2.2 嵌套选择

  1. html = """
  2. <html><head><title>The Dormouse's story</title></head>
  3. <body>
  4. """
  5. from bs4 import BeautifulSoup
  6. soup = BeautifulSoup(html, 'lxml')
  7. print(soup.head.title)
  8. print(type(soup.head.title))
  9. print(soup.head.title.string)

4.2.3 关联选择

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

  1. html = """
  2. <html>
  3. <head>
  4. <title>The Dormouse's story</title>
  5. </head>
  6. <body>
  7. <p class="story">
  8. Once upon a time there were three little sisters; and their names were
  9. <a href="http://example.com/elsie" class="sister" id="link1">
  10. <span>Elsie</span>
  11. </a>
  12. <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a>
  13. and
  14. <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>
  15. and they lived at the bottom of a well.
  16. </p>
  17. <p class="story">...</p>
  18. """
  19. from bs4 import BeautifulSoup
  20. soup = BeautifulSoup(html, 'lxml')
  21. 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’])

样例
  1. htmls = """
  2. <html>
  3. <head>
  4. <title>The Dormouse's story</title>
  5. </head>
  6. <body>
  7. <p class="story">
  8. Once upon a time there were three little sisters; and their names were
  9. <a href="http://example.com/elsie" class="sister" id="link1" title="xl">
  10. <span>Elsie</span>
  11. </a>
  12. <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a>
  13. and
  14. <a href="http://example.com/tillie" class="sister" id="link3" rel="noopener noreferrer ">Tillie</a>
  15. and they lived at the bottom of a well.
  16. </p>
  17. <p class="story">...</p>
  18. """

1.2案例一(层级选择器,返回的都是列表)
  1. soup.select('.story > a > span')[0].text

1.3案例二(id选择器)
  1. print(soup.select('#link1'))

1.3案例三(提取属性)
  1. soup.select('#link1')[0].attrs['href']

1.4 案例四 (属性选择器)
  1. print(soup.select("input[type='password']"))

1.3案例四(提取实际数据)

url地址:https://movie.douban.com/chart

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