爬虫学习3-网页解析器

BeautifulSoup解析器:

解析器使用方法条件
bs4的html解析器BeautifulSoup(html,‘html.parser’)安装bs4
lxml的html解析器BeautifulSoup(html,’lxml')pip install lxml
lxml的xml解析器BeautifulSoup(html,‘xml’)pip install lxml
html5lib的解析器BeautifulSoup(html,‘html5lib’)pip install html5lib

基本元素

基本元素说明
tag标签,<>开头,</>结尾
name标签的名字,,<tag>.name
attrs标签的属性,<tag>.attrs
NavigableStringString,<tag>.String
Comment标签内字符串的注释部分,Comment类型

搜索节点(html中的标签)

find_all(name,attrs,recursive,string)

name:对标签名称的检索字符串

attrs:对标签属性值的检索字符串,可棕注属性栓索

recursive:是否对子孙全部检索,默以True

string:<>..</>中字符串区域的检索字符串

eg:


import bs4

from bs4 import BeautifulSoup

import re



html = "<body><a href='./test/123.txt'>hjhkj</a> <div class='test'>fdfdfd<div class='test'>fdfdfd</div></div></body>"

soup = BeautifulSoup(html,"html.parser")

#查找所有标签为a,链接为./test/123.txt形式的节点

m = soup.find_all('a',href=re.compile(r'./test/(.*).txt'))# (另一种写法\d+\)



#查找所有标签为div,class为test,文字为fdfdfd的节点

m=soup.find_all('div',class_='test',string = 'fdfdfd')

m = soup.div(class_='test',string = 'fdfdfd')



soup.div.attrs#tag的attrs

soup.div.parent.name#tag的父亲节点的名字

m = soup.a.string#String

#显示:

m = soup.prettify()#友好度+1



print(m)

###标签树的遍历

上行遍历

属性说明
.parent节点的父亲标签
.parents节点父辈的迭代,用来遍历父辈节点

下行遍历

属性说明
.contentstag的子节点列表
.children子结点的迭代,用于循环儿子节点
.descendants子孙节点的迭代,循环遍历所有子孙节点

平行遍历

属性说明
.next_sibling返回下一个平行节点的tag
.previous_sibling分返回上一个平行节点
.next_siblings迭代类型,返回后续所有平行节点
.previous_siblings迭代类型,返回前序所有平行节点

实例


# coding:utf-8

from bs4 import BeautifulSoup

import re



html_doc = """  #定义一个长字符串,存储html代码,'''可以保留格式

<html><head><title>The Dormouse's story</title></head>

<body>

<p class="title"><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>

http://example.com/lacie

<p class="story">...</p>

"""

soup = BeautifulSoup(html_doc, 'html.parser')#传入的html字符串;使用的解析器;编码方式

print(soup.prettify())#按照标准的缩进格式输出获取的soup内容

#提取所有的连接出来

links = soup.find_all('a')

for link in links:

    print(link.name, link['href'], link['class'], link.get_text())



#获取lacie连接

link_node = soup.find('a', href='http://example.com/lacie')

print(link_node.name, link_node['href'], link_node.get_text())



#用强大的 正则匹配

link_node = soup.find('a', href=re.compile(r"ill"))

print(link_node.name, link_node['href'], link_node.get_text())



#获取P标签

p_node = soup.find('p', class_=re.compile(r"ti"))

print(p_node.name, p_node['class'], p_node.get_text())



#抓取暗链接

link_node = soup.find_all(style='display:none;')

print(link_node)



#选择器

soup.select('title')#选择title标签

soup.select('p nth-of-type(3)')



#通过tag标签逐层查找

soup.select('body a')#查找body标签下面的a标签

#找到某个tag标签下的直接子标签:

soup.select('head>title')



#通过id来查找:

soup.select('#link1')



#通过class来查找:

soup.select('.story')

soup.select('[class~=story]')



#通过是否存在某个属性来查找:

soup.select('a[href]')



#通过属性的值来查找:

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

正则RE

特殊字符:(使用r’string’定义字符)


'.'#点号,在默认模式中,匹配任何一个字符,除了新的行newline。如果DOTALL标记指定了,那么还可以匹配newline。

 '^'#匹配字符串的开始

 '$'#匹配字符串的结束。比如foo匹配foo或者foobar,但是foo$只能匹配到foo。

 '*'#匹配0个或者多个字符,比如ab*,可以匹配a,ab,abbbb等

 '+'#匹配1个或者多个字符,比如ab+,可以匹配ab,或者abbbb

 '?'#匹配0或者1个字符,比如ab?只能匹配a或者ab。

'*?+'#贪婪模式的,会尽可能多的匹配字符,

'*?+',#在后面加上一个?则会变为非贪婪模式,尽可能匹配少的字符。

#我们一般用非贪婪模式。



#m,n表示匹配的数量,如果不指定m或者n,则表示没有上限,下限不能低于0个

 '{m}',#指定匹配的数量,比如a{6}表示将匹配6个a字符,而不是5个,准确匹配。

 '{m,n}',#匹配在m~n个字符之间,包含m和n,比如a{3,5}将匹配3-5个a字符,一般会取上限来匹配。

 '{m,n}?',#非贪婪模式的匹配,尽可能匹配少,取下限m来匹配。

 '[]',#用于创造一个字符的集合,字符可以单独出现或者使用0-9,a-z这种格式。

 

 比如

 1.[amk]会匹配am或者k字符[a-z]将会匹配a-z之间的任何一个字符[0-5][0-9]匹配从00-59的所有字符

 2.如果在[]中包含一个特殊字符的集合比如[(+*)]这些特殊字符将会失去其特殊含义只匹配字面意义'('')''+''*'

 3.如果在[]的开始有一个'^'比如[^5]将会匹配任何一个不是5的字符



 '|',#A|B,AB是任意的RE表达式,可以匹配A或者B

 '(...)',#括号内的表达式将作为分组,从表达式左边开始每遇到一个分组的左括号,编号+1。分组表达式作为一个整体,可以后接数量词。

 比如:

 1.(abc){2}匹配abcabca(123|456)c匹配a456c或者a123c



 '(?P<name>...)'#分组,除了原有的编号外,指定一个额外的别名,比如:(?P<id>abc){2},可以匹配abcabc,通过.'id'来访问。

 '\<number>'#引用编号为<number>的分组匹配到的字符串。比如(\d)abc\1,可以匹配1abc1,或者5abc5

 '(?P=name)',#引用别名为<name>的分组匹配到的字符串,比如(?P<id>\d)abc(?P=id),可以匹配1abc1,5abc5等。



 '\d',#表示数字,0-9之间的一个,比如a\dc,可以匹配a1c;

 '\D',#表示非数字,也可以用[^\d]来代替,

 '\s'#表示空白字符

 '\S'#表示非空白字符

 '\w',#表示单词字符,[A-Za-z0-9_]

 '\W'#表示非单词字符。

 '\A',#仅匹配字符串的开头,\Aabc可以匹配abc。

 '\Z',#仅匹配字符串结尾,abc\Z,匹配abc

方法:

1. re.findall(pattern,string)

对string中所有符合pattern规则的进行匹配

2. re.compile(pattern,flag=0)

返回一个对象的模式,用法:re.compile(pattern,flag=0).findall(string)

pattern为正则字符串

flag,匹配模式,以下可选


re.I #忽略大小写

re.M #多行模式

re.S #点任意匹配,

re.L #

re.U

re.X #详细模式,这个模式下正则表达式可以是多行,忽略空白字符,并可以加入注释

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

match方法会从要匹配的string的开头开始,尝试匹配pattern,一直向后,如果遇到无法匹配的字符,就返回None,如果匹配未结束已经到达string的末尾也会返回None,表示匹配失败。匹配成功会返回True,以及匹配到的内容的group,非完全匹配,完全匹配需要在表达式末尾加上’$'

eg:

pattern=re.compile(r'hello')

result1=re.match(pattern,'hello')

result2=re.match(pattern,'helloo world')

result3=re.match(pattern,'helo world')

result4=re.match(pattern,'hello world')



if result1:

    print(result1.group())

else:

    print('1匹配失败!')

if result2:

    print(result2.group())

else:

    print('2匹配失败!')

if result3:

    print(result3.group())

else:

    print('3匹配失败!')

if result4:

    print(result4.group())

else:

    print('4匹配失败!')





m=re.match(r'(\w+) (\w+)(?P<sign>.*)','hello world!')#单词+空格+单词+任意字符



print(m.string)#获取匹配时使用的文本

print(m.re)#获取匹配时使用的pattern对象

print(m.pos)#文本中开始搜索的位置

print(m.endpos)#返回结束搜索的位置

print(m.lastindex)#最后一个被捕获的分组在文本中的索引

print(m.lastgroup)#最后一个最捕获的分组的别名

print(m.group())#获得一个或多个分组截获的字符串,如果指定多个参数,将会以元祖形式返回

print(m.group(1,2))#指定多个参数

print(m.groups())#以元组形式返回所有分组截获的字符串

print(m.groupdict())#返回具有别名的组的别名为Key,字符串为value的字典,不包含别名的组不统计

print(m.start(2))#返回指定的组截获的子串在string中的起始索引

print(m.end(2))#返回指定的组在string中的结束索引

print(m.span(2))#返回(start(group),end(group))

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

类似与match方法,但是match会检测re是不是在string的开始位置就能够匹配到,而search会扫描整个string来匹配。match方法只有在0位置匹配成功的话才会返回,如果不是开始i位置,即使后面由匹配,也会返回None。

eg:

pattern=re.compile(r'world')

match=re.search(pattern,'hello world!')

if match:

    print(match.group()

5. re.split(pattern, string, maxsplit=0, flags=0)

将string按照匹配的子串分割后返回列表,maxsplit参数指定最大分割次数,不指定的话会全部分割。

eg:

pattern=re.compile(r'\d+')

split=re.split(pattern,'one1two2three3four4')

print(split)

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

搜索string,以列表的形式返回全部能匹配的子串。

eg:

pattern = re.compile(r'\d+')

print(re.findall(pattern,'one1two23three3four4'))

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

搜索string,返回一个顺序访问每一个匹配结果的迭代器

eg:

pattern=re.compile(r'\d+')

for m in re.finditer(pattern,'one1two23three3four4'):

    print(m.group())

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

使用repl替换string中的每一个匹配的子串后返回替换的字符串。

repl可以是一个方法,这个方法应该只接受一个match对象作为参数,并且返回一个字符串用于替换。

count可以指定最多替换次数,不指定的话就全部替换。

eg:

pattern=re.compile(r'(\w+) (\w+)')

string='i say, hello world!'

print(re.sub(pattern,r'\2 \1',string))



def func(m):

    return m.group(1).title()+' '+m.group(2).title()

print(re.sub(pattern,func,string))

CC BY-NC 4.0

爬虫学习4
爬虫学习2-Requests库学习

Comments