python网络爬虫常用解析器

BeautifulSoup

BeautifulSoup 是一个HTML/XML 的解析器,主要用于解析和提取 HTML/XML 数据。

它基于HTML DOM 的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多,所以性能要低于lxml。

BeautifulSoup 用来解析 HTML 比较简单,API非常人性化,支持CSS选择器、Python标准库中的HTML解析器,也支持 lxml 的 XML解析器。

目前最常用的版本是Beautiful Soup 4,也就是 bs4 ,所以在导入时 import bs4 就是在导入Beautiful Soup 4。

BeautifulSoup4安装

pip install beautifulsoup4

BeautifulSoup4解析器

BeautifulSoup做的工作就是对html标签进行解释和分类,不同的解析器对相同html标签会做出不同解释。

默认情况下,直接输入一个link解析时,python内部默认的解析器是"html.parser"。

BeautifulSoup4简单使用

假设有这样一个Html,具体内容如下:

<!DOCTYPE html>
<html>
<head>
    <meta content="text/html;charset=utf-8" http-equiv="content-type" />
    <meta content="IE=Edge" http-equiv="X-UA-Compatible" />
    <meta content="always" name="referrer" />
    <link href="https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css" rel="stylesheet" type="text/css" />
    <title>百度一下,你就知道 </title>
</head>
<body link="#0000cc">
  <div id="wrapper">
    <div id="head">
        <div class="head_wrapper">
          <div id="u1">
            <a class="mnav" href="http://news.baidu.com" name="tj_trnews">新闻 </a>
            <a class="mnav" href="https://www.hao123.com" name="tj_trhao123">hao123 </a>
            <a class="mnav" href="http://map.baidu.com" name="tj_trmap">地图 </a>
            <a class="mnav" href="http://v.baidu.com" name="tj_trvideo">视频 </a>
            <a class="mnav" href="http://tieba.baidu.com" name="tj_trtieba">贴吧 </a>
            <a class="bri" href="//www.baidu.com/more/" name="tj_briicon" style="display: block;">更多产品 </a>
          </div>
        </div>
    </div>
  </div>
</body>
</html>

创建beautifulsoup4对象:

import bs4.element
from bs4 import BeautifulSoup
import requests

#
# html = requests.get('https://www.cnblogs.com/xfeiyun/category/2213556.html')
# print(html.text)

fo = open('./test.html', 'rb')
text = fo.read()
# 解析器
soup = BeautifulSoup(text, 'html.parser')
print(soup.prettify())  # 格式化html结构
print(soup.title)  # 获取title标签的名称
print(soup.title.name)  # 获取title的name
print(soup.title.string)  # 获取head标签的所有内容
print(soup.head)
print(soup.div)  # 获取第一个div标签中的所有内容
print(soup.div["id"])  # 获取第一个div标签的id的值
print(soup.a)
print(soup.find_all("a"))  # 获取所有的a标签
print(soup.find(id="u1"))  # 获取id="u1"
for item in soup.find_all("a"):
    print(item.get("href"))  # 获取所有的a标签,并遍历打印a标签中的href的值
for item in soup.find_all("a"):
    print(item.get_text())
print("|=============================================|")
# 根据id查找节点
wrapper = soup.select('#wrapper')[0]
if isinstance(wrapper, bs4.element.Tag):
    u1 = wrapper.select('#u1')[0]  # 查找ul节点,select()查找都是列表,这里只有一个,所以取下标0
    for child in u1.children:  # children不仅仅包含标签节点,也包括文本内容、特殊符号(如果\n)等
        print(type(child))
    # alist = u1.find_all("a")  # 查找所有的a节点
    print("---------> 属性匹配")
    alist = u1.find_all(attrs={"class": "mnav"})  # 查找有指定属性的节点
    for a in alist:
        print(a.string)  # 获取节点的内容
        print(a.attrs)  # 获取节点的属性字典

    print("---------> 文本匹配")
    textList = u1.find_all(text='地图 ')  # 注意:必须与html的文本节点完成匹配,空格也不能省略,否则匹配不到
    for atext in textList:
        print(atext)  # 这里输出的是文本

BeautifulSoup4四大对象种类

BeautifulSoup4将复杂HTML文档转换成一个复杂的树形结构,每个节点都是Python对象,所有对象可以归纳为4种:

  • Tag(标签)
  • NavigableString(内容)
  • BeautifulSoup(文档)
  • Comment(注释)

Tag

Tag通俗点讲就是HTML中的一个个标签,例如:

from bs4 import BeautifulSoup

from bs4 import BeautifulSoup

file = open('./test.html', 'rb')
html = file.read()
bs = BeautifulSoup(html, "html.parser")
# 获取title标签的所有内容
print(bs.title)
# 获取head标签的所有内容
print(bs.head)
# 获取第一个a标签的所有内容
print(bs.a)
# 类型
print(type(bs.a))

我们可以利用 soup 加标签名轻松地获取这些标签的内容,这些对象的类型是bs4.element.Tag。但是注意,它查找的是在所有内容中的第一个符合要求的标签。

对于 Tag,它有两个重要的属性,是 name 和 attrs:

from bs4 import BeautifulSoup

file = open('./test.html', 'rb')
html = file.read()
bs = BeautifulSoup(html, "html.parser")
# [document] #bs 对象本身比较特殊,它的 name 即为 [document]
print(bs.name)
# head #对于其他内部标签,输出的值便为标签本身的名称
print(bs.head.name)
# 在这里,我们把 a 标签的所有属性打印输出了出来,得到的类型是一个字典。
print(bs.a.attrs)
# 还可以利用get方法,传入属性的名称,二者是等价的
print(bs.a['class'])  # 等价 bs.a.get('class')
# 可以对这些属性和内容等等进行修改
bs.a['class'] = "newClass"
print(bs.a)
# 还可以对这个属性进行删除
del bs.a['class']
print(bs.a)

NavigableString

既然我们已经得到了标签的内容,那么问题来了,我们要想获取标签内部的文字怎么办呢?很简单,用 .string 即可,例如:

from bs4 import BeautifulSoup

file = open('./test.html', 'rb')
html = file.read()
bs = BeautifulSoup(html, "html.parser")

print(bs.title.string)
print(type(bs.title.string))

BeautifulSoup

BeautifulSoup对象表示的是一个文档的内容。大部分时候,可以把它当作 Tag 对象,是一个特殊的 Tag,我们可以分别获取它的类型,名称,以及属性,例如:

from bs4 import BeautifulSoup

file = open('./test.html', 'rb')
html = file.read()

bs = BeautifulSoup(html, "html.parser")
print(type(bs.name))
print(bs.name)
print(bs.attrs)

Comment

Comment 对象是一个特殊类型的 NavigableString 对象,其输出的内容不包括注释符号。

from bs4 import BeautifulSoup

file = open('./test.html', 'rb')
html = file.read()
bs = BeautifulSoup(html, "html.parser")
print(bs.a)
# 此时不能出现空格和换行符,a标签如下:
# <a class="mnav" href="http://news.baidu.com" name="tj_trnews"><!--新闻--></a>
print(bs.a.string)  # 新闻
print(type(bs.a.string))  # <class 'bs4.element.Comment'>

遍历文档树

.contents:获取Tag的所有子节点,返回一个list

# tag的.content 属性可以将tag的子节点以列表的方式输出
print(bs.head.contents)
# 用列表索引来获取它的某一个元素
print(bs.head.contents[1])

.children:获取Tag的所有子节点,返回一个生成器

for child in  bs.body.children:
    print(child)

.descendants:获取Tag的所有子孙节点,返回的是一个 (子孙节点)的迭代器,包含所有子孙节点,用于循环遍历。

.strings:如果Tag包含多个字符串,即在子孙节点中有内容,可以用此获取,而后进行遍历

.stripped_strings:与strings用法一致,只不过可以去除掉那些多余的空白内容

.parent:获取Tag的父节点

.parents:递归得到父辈元素的所有节点,返回一个生成器

.previous_sibling:获取当前Tag的上一个节点,属性通常是字符串或空白,真实结果是当前标签与上一个标签之间的顿号和换行符

.next_sibling:获取当前Tag的下一个节点,属性通常是字符串或空白,真实结果是当前标签与下一个标签之间的顿号与换行符

.previous_siblings:获取当前Tag的上面所有的兄弟节点,返回一个生成器

.next_siblings:获取当前Tag的下面所有的兄弟节点,返回一个生成器

.previous_element:获取解析过程中上一个被解析的对象(字符串或tag),可能与previous_sibling相同,但通常是不一样的

.next_element:获取解析过程中下一个被解析的对象(字符串或tag),可能与next_sibling相同,但通常是不一样的

.previous_elements:返回一个生成器,可以向前访问文档的解析内容

.next_elements:返回一个生成器,可以向后访问文档的解析内容

.has_attr:判断Tag是否包含属性

搜索文档树

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

(1)name参数:

字符串过滤:会查找与字符串完全匹配的内容

a_list = bs.find_all("a")
print(a_list)

正则表达式过滤:如果传入的是正则表达式,那么BeautifulSoup4会通过search()来匹配内容

from bs4 import BeautifulSoup
import re

file = open('./test.html', 'rb')
html = file.read()
bs = BeautifulSoup(html, "html.parser")
t_list = bs.find_all(re.compile("a"))
for item in t_list:
    print(item)

列表:如果传入一个列表,BeautifulSoup4将会与列表中的任一元素匹配到的节点返回

t_list = bs.find_all(["meta", "link"])
for item in t_list:
    print(item)

方法:传入一个方法,根据方法来匹配

from bs4 import BeautifulSoup

file = open('./test.html', 'rb')
html = file.read()
bs = BeautifulSoup(html, "html.parser")


def name_is_exists(tag):
    return tag.has_attr("name")


t_list = bs.find_all(name_is_exists)
for item in t_list:
    print(item)

(2)kwargs参数:

from bs4 import BeautifulSoup
import re

file = open('./test.html', 'rb')
html = file.read()
bs = BeautifulSoup(html, "html.parser")
# 查询id=head的Tag
t_list = bs.find_all(id="head")
print(t_list)
# 查询href属性包含ss1.bdstatic.com的Tag
t_list = bs.find_all(href=re.compile("http://news.baidu.com"))
print(t_list)
# 查询所有包含class的Tag(注意:class在Python中属于关键字,所以加_以示区别)
t_list = bs.find_all(class_=True)
for item in t_list:
    print(item)

(3)attrs参数:

并不是所有的属性都可以使用上面这种方式进行搜索,比如HTML的data-*属性:

t_list = bs.find_all(data-foo="value")

如果执行这段代码,将会报错。我们可以使用attrs参数,定义一个字典来搜索包含特殊属性的tag:

t_list = bs.find_all(attrs={"data-foo":"value"})
for item in t_list:
    print(item)

(4)text参数:

通过text参数可以搜索文档中的字符串内容,与name参数的可选值一样,text参数接受 字符串,正则表达式,列表

from bs4 import BeautifulSoup
import re

file = open('./test.html', 'rb')
html = file.read()
bs = BeautifulSoup(html, "html.parser")
t_list = bs.find_all(attrs={"data-foo": "value"})
for item in t_list:
    print(item)
t_list = bs.find_all(text="hao123")
for item in t_list:
    print(item)
t_list = bs.find_all(text=["hao123", "地图", "贴吧"])
for item in t_list:
    print(item)
t_list = bs.find_all(text=re.compile("\d"))
for item in t_list:
    print(item)

当我们搜索text中的一些特殊属性时,同样也可以传入一个方法来达到我们的目的:

def length_is_two(text):
    return text and len(text) == 7


t_list = bs.find_all(text=length_is_two)
for item in t_list:
    print(item)

(5)limit参数:

以传入一个limit参数来限制返回的数量,当搜索出的数据量为5,而设置了limit=2时,此时只会返回前2个数据

from bs4 import BeautifulSoup

file = open('./test.html', 'rb')
html = file.read()
bs = BeautifulSoup(html, "html.parser")
t_list = bs.find_all("a", limit=2)
for item in t_list:
    print(item)

find_all除了上面一些常规的写法,还可以对其进行一些简写:

# 两者是相等的
# t_list = bs.find_all("a") => t_list = bs("a")
t_list = bs("a") # 两者是相等的
# t_list = bs.a.find_all(text="新闻") => t_list = bs.a(text="新闻")
t_list = bs.a(text="新闻")

find()

find()将返回符合条件的第一个Tag,有时我们只需要找一个Tag时,我们就可以用到find()方法了。当然了,也可以使用find_all()方法,传入一个limit=1,然后再取出第一个值也是可以的,不过未免繁琐。

from bs4 import BeautifulSoup

file = open('./test.html', 'rb')
html = file.read()
bs = BeautifulSoup(html, "html.parser")
# 返回只有一个结果的列表
t_list = bs.find_all("title", limit=1)
print(t_list)
# 返回唯一值
t = bs.find("title")
print(t)
# 如果没有找到,则返回None
t = bs.find("abc")
print(t)

从结果可以看出find_all,尽管传入了limit=1,但是返回值仍然为一个列表,当我们只需要取一个值时,远不如find方法方便。但是如果未搜索到值时,将返回一个None。

在上面介绍BeautifulSoup4的时候,我们知道可以通过bs.div来获取第一个div标签,如果我们需要获取第一个div下的第一个div,

我们可以这样:

t = bs.div.div
# 等价于
t = bs.find("div").find("div")

CSS选择器

BeautifulSoup支持发部分的CSS选择器,在Tag获取BeautifulSoup对象的.select()方法中传入字符串参数,即可使用CSS选择器的语法找到Tag:

通过标签名查找

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

通过类名查找

print(bs.select('.mnav'))

通过id查找

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

组合查找

print(bs.select('div .bri'))

属性查找

print(bs.select('a[class="bri"]'))
print(bs.select('a[href="http://tieba.baidu.com"]'))

直接子标签查找

t_list = bs.select("head > title")
print(t_list)

兄弟节点标签查找

t_list = bs.select(".mnav ~ .bri")
print(t_list)

获取内容

t_list = bs.select("title")
print(bs.select('title')[0].get_text())

XPath

XPath,全称 XML Path Language,即 XML 路径语言,它是一门在 XML 文档中查找信息的语言。XPath 可用来在 XML 文档中对元素和属性进行遍历。

  • XPath 使用路径表达式在 XML 文档中进行导航
  • XPath 包含一个标准函数库
  • XPath 是 XSLT 中的主要元素
  • XPath 是一个 W3C 标准

要使用XPath,需要安装一个第三方库lxml:

pip install lxml

节点(Node)

在 XPath 中,有七种类型的节点:元素、属性、文本、命名空间、处理指令、注释以及文档(根)节点。XML 文档是被作为节点树来对待的。树的根被称为文档节点或者根节点。

xpath中节点的关系:

xpath语法

from lxml import etree
import requests

# 本地文件解析 -> 将源码转化为能被XPath匹配的格式
print("本地文件解析......")
parser = etree.HTMLParser(encoding='utf-8')
tree = etree.parse('test.html', parser=parser)

# 远程html url解析 -> 将源码转化为能被XPath匹配的格式
print("远程html url解析......")
resp = requests.get('https://www.cnblogs.com/xfeiyun/category/2213556.html')
tree2 = etree.HTML(resp.text)

实例:

<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book>
  <title lang="eng">Harry Potter</title>
  <price>29.99</price>
</book>

<book>
  <title lang="eng">Learning XML</title>
  <price>39.95</price>
</book>
</bookstore>

选取节点

XPath 使用路径表达式在 XML 文档中选取节点,节点是沿着路径或者 step 来选取的。

表达式

描述

nodename

选取当前节点的所有子节点

/

从根节点选取

//

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

.

选取当前节点

..

选取当前节点的父节点

@

选取属性

text()

选取文本

示例:

路径表达式

结果

bookstore

选取 bookstore 元素的所有子节点

/bookstore

选取根元素 bookstore

bookstore/book

选取bookstore 下名字为 book的所有子元素。

//book

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

bookstore//book

选取bookstore 下名字为 book的所有后代元素,而不管它们位于 bookstore 之下的什么位置。

//@lang

选取所有名为 lang 的属性。

from lxml import etree

# 本地文件解析
parser = etree.XMLParser(encoding='utf-8')
tree = etree.parse('demo.xml', parser=parser)

# 常用的表达式
# 从根节点选取bookstore元素, bookstore本例中只有一个
bookStoreNode = tree.xpath("/bookstore")[0]
print(bookStoreNode)

# 选取当前节点
print(bookStoreNode.xpath('.'))

# 选取此节点的所有子节点
bookNodeList = bookStoreNode.xpath('book')
print(bookNodeList)

# 选取当前节点的父节点
parentBookNode = bookNodeList[0].xpath('..')
print(parentBookNode)

# 选择所有的book下面的title中的lang属性的值
attrlist = tree.xpath("/bookstore/book/title/@lang")
print(attrlist)

# 选择所有的book下面的title的文本
textlist = tree.xpath("/bookstore/book/title/text()")
print(textlist)

# 选取所有 book 子元素,而不管它们在文档中的位置。
nodeList = tree.xpath('//book')
print(nodeList)

谓语(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 元素,要求book元素的子元素 price 元素的值须大于 35.00。

/bookstore/book[price>35.00]/title

选取所有 bookstore 元素中的 book 元素的 title 元素,要求book元素的子元素 price 元素的值须大于 35.00

示例:

# 选取属于 bookstore 子元素的第一个 book 元素。
firstBookNode = tree.xpath('/bookstore/book[1]')
print(firstBookNode)

# 选取属于 bookstore 子元素的最后一个 book 元素。
lastBookNode = tree.xpath('/bookstore/book[last()]')
print(lastBookNode)

# 选取最前面的两个属于  bookstore 元素的子元素的 book 元素。
positionBookNode = tree.xpath('/bookstore/book[position()<3]')
print(positionBookNode)

# 选取所有拥有名为 lang 的属性的 title 元素。
print(tree.xpath('//title[@lang]'))

# 选取所有 bookstore 元素的 book 元素,要求book元素的子元素 price 元素的值须大于 35.00。
print(tree.xpath('/bookstore/book[price>35.00]'))

# 选取所有 bookstore 元素中的 book 元素的 title 元素,要求book元素的子元素 price 元素的值须大于 35.00
print(tree.xpath('/bookstore/book[price>35.00]/title'))

选取未知节点

XPath 通配符可用来选取未知的 XML 元素。

通配符

描述

*

匹配任何元素节点

@*

匹配任何属性节点

node()

匹配任何类型的节点

示例:

路径表达式

结果

/bookstore/*

选取 bookstore 元素的所有子节点

//*

选取文档中的所有元素

//title[@*]

选取所有带有属性的 title 元素。

选取若干路径

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

示例:

路径表达式

结果

//book/title | //book/price

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

//title | //price

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

/bookstore/book/title|//price

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

XPath 轴

轴可定义某个相对于当前节点的节点集。

轴名称

结果

ancestor

选取当前节点的所有先辈(父、祖父等)

ancestor-or-self

选取当前节点的所有先辈(父、祖父等)以及当前节点本身

attribute

选取当前节点的所有属性

child

选取当前节点的所有子元素。

descendant

选取当前节点的所有后代元素(子、孙等)。

descendant-or-self

选取当前节点的所有后代元素(子、孙等)以及当前节点本身。

following

选取文档中当前节点的结束标签之后的所有节点。

namespace

选取当前节点的所有命名空间节点

parent

选取当前节点的父节点。

preceding

选取文档中当前节点的开始标签之前的所有节点。

preceding-sibling

选取当前节点之前的所有同级节点。

self

选取当前节点。

路径

(1)位置路径表达式

位置路径可以是绝对的,也可以是相对的。

绝对路径起始于正斜杠( / ),而相对路径不会这样。在两种情况中,位置路径均包括一个或多个步,每个步均被斜杠分割:

  • 绝对位置路径:/step/step/...
  • 相对位置路径:step/step/...

 每个步均根据当前节点集之中的节点来进行计算。

(2)步(step)包括:

轴(axis):定义所选节点与当前节点之间的树关系

节点测试(node-test):识别某个轴内部的节点

零个或者更多谓语(predicate):更深入地提炼所选的节点集

步的语法:轴名称::节点测试[谓语]

示例:

例子

结果

child::book

选取所有属于当前节点的子元素的 book 节点

attribute::lang

选取当前节点的 lang 属性

child::*

选取当前节点的所有子元素

attribute::*

选取当前节点的所有属性

child::text()

选取当前节点的所有文本子节点

child::node()

选取当前节点的所有子节点

descendant::book

选取当前节点的所有 book 后代

ancestor::book

选择当前节点的所有 book 先辈

ancestor-or-self::book

选取当前节点的所有book先辈以及当前节点(假如此节点是book节点的话)

child::*/child::price

选取当前节点的所有 price 孙。

XPath 运算符

运算符

描述

实例

返回值

|

计算两个节点集

//book | //cd

返回所有带有 book 和 ck 元素的节点集

+

加法

6 + 4

10

-

减法

6 - 4

2

*

乘法

6 * 4

24

div

除法

8 div 4

2

=

等于

price=9.80

如果 price 是 9.80,则返回 true。

如果 price 是 9.90,则返回 fasle。

!=

不等于

price!=9.80

如果 price 是 9.90,则返回 true。

如果 price 是 9.80,则返回 fasle。

<

小于

price<9.80

如果 price 是 9.00,则返回 true。

如果 price 是 9.90,则返回 fasle。

<=

小于或等于

price<=9.80

如果 price 是 9.00,则返回 true。

如果 price 是 9.90,则返回 fasle。

>

大于

price>9.80

如果 price 是 9.90,则返回 true。

如果 price 是 9.80,则返回 fasle。

>=

大于或等于

price>=9.80

如果 price 是 9.90,则返回 true。

如果 price 是 9.70,则返回 fasle。

or

price=9.80 or  price=9.70

如果 price 是 9.80,则返回 true。

如果 price 是 9.50,则返回 fasle。

and

price>9.00  and price<9.90

如果 price 是 9.80,则返回 true。

如果 price 是 8.50,则返回 fasle。

mod

计算除法的余数

5 mod 2

1

常用函数

  • starts-with() 获取以xxx开头的元素
tree.xpath('//title[starts-with(@lang,"ch")]')
  • contains() 获取包含xxx的元素
tree.xpath('//title[contains(@lang,"chinese")]')
  • and 并且 同时
tree.xpath("//title[@lang='chinese' and @lang='chinese']")
  • or 或者
tree.xpath("//title[@lang='chinese' or @name='fuck']")
  • not 非
tree.xpath("//title[@lang='chinese' and not(contains(@name,'fuck'))]")
  • text()
tree.xpath('//title[contains(text(),"chinese")]')
tree.xpath('//title[@lang="chinese"]/text()')

正则表达式

正则表达式在网络爬虫中非常重要,可以在一大段文字中提取出有用的信息,虽然正则表达式不是最简单和高效的数据提取方式,但是是最直接的方式,Python中内置re模块来支持正则表达式。

正则表达式是由普通字符和元字符组成。

正则表达式中常见的特殊字符有以下几个:.+?*$[]()^{}\

如果要在正则表达式中表示这几个字符本身,就应该在字符前面加上\。

常用元字符

.                     匹配除换行符以外的任意字符
\w                    匹配数字、字母、下划线,汉字
\s                    匹配任意的一个空白符,如空格,\t,\r,\n等
\d                    匹配任意的一个数字,等价于[0-9]
\n                    匹配一个换行符
\t                    匹配一个制表符
^                     匹配字符串的开始
$                     匹配字符串的结尾
\W                    匹配非(字母、数字、下划线、汉字)
\D                    匹配一个非数字,等价于[^\d],[^0-9]
\S                    匹配非空白符
a|b                   匹配a或b
()                    匹配括号内的表达式,也表示一个组
[]                    匹配字符组中的其中一个字符
[^]                   匹配一个除了字符组中出现的字符

量词

想要一次性匹配多个字符,则需要使用到量词

*                     表示左边的字符可重复零次或更多次
+                     表示左边的字符可重复一次或更多次
?                    表示左边的字符可重复零次或一次
{n}                   n为整数,表示左边的字符必须且只能重复n次
{n,}                  表示左边的字符最少重复n次或更多次
{n,m}                 表示左边的字符至少重复n次,最多重复m次

惰性匹配和贪婪匹配

在量词中的?,*,+,{}都属于贪婪匹配,就是尽可能多的匹配到结果。
在使用.*后面如果加了?,这是尽可能的少匹配。表示惰性匹配。.?,.+,.{}类似

下面举个简单的例子来说明。

1.贪婪匹配是先看整个字符串是否匹配,如果不匹配,它会去掉字符串的最后一个字符,并再次尝试。如果还不匹配,那么再去掉当前最后一个,直到发现匹配或不剩任何字符。

var str='abcdabceba'

/.+b/         //匹配一个或多个任意字符后面跟一个字母b

执行str.match(/.+b/)

第一次(先看整个字符串是否是一个匹配) abcdabceba 不匹配,然后去掉最后一个字符a

第二次(去掉最后一个字符后再匹配) abcdabceb 匹配,返回abcdabceb。

2.惰性匹配是从左侧第一个字符开始向右匹配, 先看第一个字符是不是一个匹配, 如果不匹配就加入下一个字符再尝式匹配, 直到发现匹配...

执行str.match(/.+?b/)

第一次(读入左侧第一个字符) a 不匹配加一个再式

第二次 ab 匹配,返回ab

正则表达式中的函数

re.search(pattern, string, flags=0)

res = re.search("\d+", "我今年18岁了,体重为60kg")
# 输出子串及起止位置
print(res.group(), res.span())  # 18 (3, 5)
  • 查找字符串中可以匹配成功的子串,匹配到第一个结果就返回。不会匹配出多个结果。
  • 成功则返回一个匹配对象,否则返回None
content = '我的微博密码是:1234567,QQ密码是:33445566,银行卡密码是:888888,Github密码是:999abc999'
password_search = re.search('密码是:(.*?),', content)
password_search_not_find = re.search('xxx:(.*?),', content)
print(password_search)
print(password_search.group())
print(password_search.group(0))
print(password_search.group(1))
print(password_search_not_find)

re.findall(pattern, string, flags = 0)

list1 = re.findall("\d+", "我今年18岁了,体重为60kg")
print(list1)  # ['18', '60']
  • 查找字符串中所有和模式匹配的子串(不重叠)放入列表。一个子串都找不到就返回空表[]。
content = '我的微博密码是:1234567,QQ密码是:33445566,银行卡密码是:888888,Github密码是:999abc999'
password_list = re.findall(':(.*?),', content)
print('找到内容,返回:{}'.format(password_list))

re.match(pattern, string, flags = 0)

result = re.match("\d+", "18岁了,我喜欢5个明星")
print(result.group())  # 18,result.group()是匹配到的字符串
  • 从字符串string的起始位置开始匹配一个模式pattern(即默认加了^),匹配到第一个结果就返回。 (一般用于匹配手机号,邮箱号)
  • 成功则返回一个匹配对象,否则返回None
  • flags 标志位,用于控制模式串的匹配方式,如:是否区分大小写,多行匹配等等。如: re.M | re.I表示忽略大小写,且多行匹配

re.finditer(pattern, string, flags = 0)

it = re.finditer("\d+", "我今年18岁了,体重为60kg")
for item in it:
    print(item.group(), item.span())  # 18 (3, 5)     60 (11, 13)
  • 查找字符串中所有和模式匹配的子串(不重叠),每个子串对应于一个匹配对象,返回匹配对象的迭代器。(一般用于爬虫)
obj = re.compile(r"今天吃了(?P<mian>\d+)碗面,又吃了(?P<xian>\d+)盘小咸菜")
result = obj.finditer("明天我要吃4碗面,喝上8碗汤。今天吃了5碗面,又吃了6盘小咸菜,昨天吃了1碗面条")
for item in result:
    print(item.group("mian"))  # 5
    print(item.group("xian"))  # 6
    print(item.groupdict())  # {'mian': '5', 'xian': '6'}

其他操作

split分割

ret = re.split('[ab]', 'qwerafjbcd')
# 先按'a'分割,得到'qwer'和'fjbcd',再对'qwer'和'fjbcd'分别按'b'分割
print(ret)  # ['qwer', 'fj', 'cd']

re.sub(pattern, repl, string, count=0, flags=0)

用于替换匹配的字符串,比str.replace功能更加强大

ret = re.sub('[a-z]+', 'sb', '武配齐是abc123', )
print(ret)  # 武配齐是sb123
ret = re.sub('\d+', '|', 'alex22wupeiqi33oldboy55', count=2)
print(ret)  # alex|wupeiqi|oldboy55

compiler

obj = re.compile(r'\d{3}')  # 将正则表达式编译成为一个 正则表达式对象,规则要匹配的是3个数字
obj = re.compile(r'\d+')  # 先加载这个正则,后面可以直接用这个正则去匹配内容
lst = obj.findall("我今天吃了3个馒头,喝了2碗粥")
print(lst)  # ['3', '2']

爬虫必须会的重点

  • ()括起来的内容是你最终想要的结果
  • (?P正则) 把正则匹配到的内容直接放在name组里面,后面取数据的时候直接group(name)
obj = re.compile(r"今天吃了(?P<mian>\d+)碗面,又吃了(?P<xian>\d+)盘小咸菜")
result = obj.finditer("明天我要吃4碗面,喝上8碗汤。今天吃了5碗面,又吃了6盘小咸菜,昨天吃了1碗面条")
for item in result:
    print(item.group("mian"))       # 5
    print(item.group("xian"))       # 6
    print(item.groupdict())         # {'mian': '5', 'xian': '6'}

 

参考:

 

posted @ 2022-09-19 08:25  残城碎梦  阅读(648)  评论(0编辑  收藏  举报